• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3  * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this list of
9  *    conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12  *    of conditions and the following disclaimer in the documentation and/or other materials
13  *    provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16  *    to endorse or promote products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <errno.h>
33 #include <pthread.h>
34 #include <unistd.h>
35 #include <securec.h>
36 #include "los_task.h"
37 #include "los_task_pri.h"
38 
39 #define POLLING_INTERVAL_FOR_JOIN 1000
40 #define PTHREAD_NAMELEN 16
41 
42 struct PthreadData {
43     CHAR name[PTHREAD_NAMELEN];
44     BOOL detached;
45     BOOL exited;
46     VOID *exitCode;
47 };
48 
49 /**
50  * pthread id and native task id translation is use for preventing
51  * wrongly passing pthread id to LOS_TaskXXX API as a native task id.
52  */
53 
54 /* translate pthread_t id to taskID */
P2T(pthread_t id)55 static inline UINT32 P2T(pthread_t id)
56 {
57     return (UINT32)id - g_taskMaxNum;
58 }
59 
60 /* translate taskID to pthread_t id */
T2P(UINT32 id)61 static inline pthread_t T2P(UINT32 id)
62 {
63     return (pthread_t)(id + g_taskMaxNum);
64 }
65 
66 /**
67  * Only pthreads can be set name by pthread_setname_np.
68  * Native threads are detached by default and its name can't be set.
69  */
70 
IsValidTask(UINT32 taskID)71 static inline BOOL IsValidTask(UINT32 taskID)
72 {
73     return (taskID < g_taskMaxNum) &&
74            !(OS_TCB_FROM_TID(taskID)->taskStatus & OS_TASK_STATUS_UNUSED);
75 }
76 
77 static void *PthreadEntry(UINT32 param1, UINT32 param2, UINT32 param3, UINT32 param4);
IsPthread(UINT32 taskID)78 static inline BOOL IsPthread(UINT32 taskID)
79 {
80     return IsValidTask(taskID) &&
81            (OS_TCB_FROM_TID(taskID)->taskEntry == PthreadEntry);
82 }
83 
GetPthreadData(UINT32 taskID)84 static inline struct PthreadData *GetPthreadData(UINT32 taskID)
85 {
86     return IsPthread(taskID) ?
87            (struct PthreadData *)(UINTPTR)(OS_TCB_FROM_TID(taskID)->taskName) : NULL;
88 }
89 
PthreadEntry(UINT32 param1,UINT32 param2,UINT32 param3,UINT32 param4)90 static void *PthreadEntry(UINT32 param1, UINT32 param2, UINT32 param3, UINT32 param4)
91 {
92     void *(*startRoutine)(void *) = (void *)(UINTPTR)param1;
93     void *param = (void *)(UINTPTR)param2;
94     void *retVal = NULL;
95     UINT32 taskID = LOS_CurTaskIDGet();
96     struct PthreadData *pthreadData = NULL;
97 
98     (void)param3;
99     (void)param4;
100 
101     retVal = startRoutine(param);
102 
103     pthreadData = GetPthreadData(taskID);
104     if (pthreadData->detached) {
105         free(pthreadData);
106         pthreadData = NULL;
107     } else {
108         pthreadData->exitCode = retVal;
109         pthreadData->exited = TRUE;
110         if (LOS_ERRNO_TSK_SUSPEND_LOCKED == LOS_TaskSuspend(taskID)) {
111             LOS_TaskUnlock();
112         }
113         while (LOS_ERRNO_TSK_SUSPEND_LOCKED != LOS_TaskSuspend(taskID)) {
114         }
115     }
116 
117     return retVal;
118 }
119 
pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* startRoutine)(void *),void * arg)120 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
121                    void *(*startRoutine)(void *), void *arg)
122 {
123     TSK_INIT_PARAM_S taskInitParam = {0};
124     UINT32 taskID;
125     struct PthreadData *pthreadData = NULL;
126 
127     if ((thread == NULL) || (startRoutine == NULL)) {
128         return EINVAL;
129     }
130 
131     pthreadData = malloc(sizeof(struct PthreadData));
132     if (pthreadData == NULL) {
133         return ENOMEM;
134     }
135 
136     (void)memset_s(pthreadData, sizeof(struct PthreadData), 0, sizeof(struct PthreadData));
137 
138     taskInitParam.usTaskPrio = LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO;
139     taskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
140     if (attr) {
141         if (attr->detachstate == PTHREAD_CREATE_DETACHED) {
142             pthreadData->detached = TRUE;
143         }
144         if (attr->stackaddr_set) {
145             free(pthreadData);
146             return ENOTSUP;
147         }
148         if (attr->stacksize_set) {
149             taskInitParam.uwStackSize = attr->stacksize;
150         }
151         taskInitParam.usTaskPrio = (UINT16)attr->schedparam.sched_priority;
152     }
153 
154     taskInitParam.pcName       = pthreadData->name;
155     taskInitParam.pfnTaskEntry = PthreadEntry;
156     taskInitParam.auwArgs[0]   = (UINT32)(UINTPTR)startRoutine;
157     taskInitParam.auwArgs[1]   = (UINT32)(UINTPTR)arg;
158 
159     if (LOS_TaskCreateOnly(&taskID, &taskInitParam) != LOS_OK) {
160         free(pthreadData);
161         return EAGAIN;
162     }
163 
164     /* set pthread default name */
165     (void)sprintf_s(taskInitParam.pcName, PTHREAD_NAMELEN, "pthread%u", (UINT32)T2P(taskID));
166 
167     (void)LOS_TaskResume(taskID);
168 
169     *thread = T2P(taskID);
170     return 0;
171 }
172 
pthread_setschedparam(pthread_t thread,int policy,const struct sched_param * param)173 int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param)
174 {
175     UINT32 taskID = P2T(thread);
176 
177     if (param == NULL) {
178         return EINVAL;
179     }
180 
181     if (!IsValidTask(taskID)) {
182         return ESRCH;
183     }
184 
185     /* Only support SCHED_RR policy now */
186     if (policy != SCHED_RR) {
187         return ENOTSUP;
188     }
189 
190     if ((param->sched_priority < OS_TASK_PRIORITY_HIGHEST) ||
191         (param->sched_priority >= OS_TASK_PRIORITY_LOWEST)) {
192         return EINVAL;
193     }
194 
195     if (LOS_TaskPriSet(taskID, (UINT16)param->sched_priority) != LOS_OK) {
196         return EPERM;
197     }
198 
199     return 0;
200 }
201 
pthread_getschedparam(pthread_t thread,int * policy,struct sched_param * param)202 int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param)
203 {
204     UINT32 prio;
205     UINT32 taskID = P2T(thread);
206 
207     if ((policy == NULL) || (param == NULL)) {
208         return EINVAL;
209     }
210 
211     if (!IsValidTask(taskID)) {
212         return ESRCH;
213     }
214 
215     prio = LOS_TaskPriGet(taskID);
216     if (prio == OS_INVALID) {
217         return EINVAL;
218     }
219 
220     *policy = SCHED_RR;
221     param->sched_priority = prio;
222     return 0;
223 }
224 
pthread_self(void)225 pthread_t pthread_self(void)
226 {
227     UINT32 taskID = LOS_CurTaskIDGet();
228     return T2P(taskID);
229 }
230 
pthread_cancel(pthread_t thread)231 int pthread_cancel(pthread_t thread)
232 {
233     UINT32 taskID = P2T(thread);
234     struct PthreadData *pthreadData = NULL;
235 
236     if (!IsValidTask(taskID)) {
237         return ESRCH;
238     }
239 
240     if (thread == pthread_self()) {
241         return EDEADLK;
242     }
243 
244     if (!IsPthread(taskID)) {
245         switch (LOS_TaskDelete(taskID)) {
246             case LOS_OK:
247                 return 0;
248             case LOS_ERRNO_TSK_NOT_CREATED:
249                 return ESRCH;
250             case LOS_ERRNO_TSK_OPERATE_IDLE:
251             case LOS_ERRNO_TSK_SUSPEND_SWTMR_NOT_ALLOWED:
252                 return EPERM;
253             default:
254                 break;
255         }
256         return EINVAL;
257     }
258 
259     pthreadData = GetPthreadData(taskID);
260     if (pthreadData->detached) {
261         (void)LOS_TaskDelete(taskID);
262         free(pthreadData);
263         return 0;
264     }
265 
266     if (pthreadData->exited) {
267         return 0;
268     }
269 
270     pthreadData->exitCode = PTHREAD_CANCELED;
271     pthreadData->exited = TRUE;
272     LOS_TaskSuspend(taskID);
273     return 0;
274 }
275 
VoidTask(UINT32 param1,UINT32 param2,UINT32 param3,UINT32 param4)276 static void *VoidTask(UINT32 param1, UINT32 param2, UINT32 param3, UINT32 param4)
277 {
278     (void)param1;
279     (void)param2;
280     (void)param3;
281     (void)param4;
282     return 0;
283 }
284 
CleanupTaskResource(void)285 static void CleanupTaskResource(void)
286 {
287     TSK_INIT_PARAM_S taskInitParam = {0};
288     UINT32 taskID;
289 
290     taskInitParam.pcName       = "void";
291     taskInitParam.pfnTaskEntry = VoidTask;
292     taskInitParam.usTaskPrio = LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO;
293     taskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE;
294 
295     (void)LOS_TaskCreate(&taskID, &taskInitParam);
296 }
297 
pthread_join(pthread_t thread,void ** retval)298 int pthread_join(pthread_t thread, void **retval)
299 {
300     UINT32 taskStatus;
301     struct PthreadData *pthreadData = NULL;
302     UINT32 taskID = P2T(thread);
303     if (!IsValidTask(taskID)) {
304         return ESRCH;
305     }
306 
307     if (!IsPthread(taskID)) {
308         /* native thread is always detached so it can't be join */
309         return EINVAL;
310     }
311 
312     if (thread == pthread_self()) {
313         return EDEADLK;
314     }
315 
316     pthreadData = GetPthreadData(taskID);
317 
318     while (LOS_TaskStatusGet(taskID, &taskStatus) == LOS_OK) {
319         if (pthreadData->detached) {
320             /* detached thread can't be join */
321             return EINVAL;
322         }
323         if ((taskStatus & OS_TASK_STATUS_SUSPEND) && pthreadData->exited) {
324             if (retval) {
325                 *retval = pthreadData->exitCode;
326             }
327             (void)LOS_TaskDelete(taskID);
328             free(pthreadData);
329             pthreadData = NULL;
330             CleanupTaskResource();
331             return 0;
332         }
333         /* we use usleep to simplify this implementation or we need an extra semaphore for each pthread */
334         usleep(POLLING_INTERVAL_FOR_JOIN);
335     }
336 
337     return ESRCH;
338 }
339 
pthread_detach(pthread_t thread)340 int pthread_detach(pthread_t thread)
341 {
342     UINT32 taskID = P2T(thread);
343     struct PthreadData *pthreadData = NULL;
344 
345     if (!IsValidTask(taskID)) {
346         return ESRCH;
347     }
348 
349     if (!IsPthread(taskID)) {
350         /* native thread is always detached so it can't be detach again */
351         return EINVAL;
352     }
353 
354     pthreadData = GetPthreadData(taskID);
355     if (pthreadData->detached) {
356         /* detached thread can't be detach again */
357         return EINVAL;
358     }
359 
360     if (pthreadData->exited) {
361         (void)LOS_TaskDelete(taskID);
362         free(pthreadData);
363         return 0;
364     }
365     pthreadData->detached = TRUE;
366     return 0;
367 }
368 
pthread_exit(void * retVal)369 void pthread_exit(void *retVal)
370 {
371     UINT32 taskID = LOS_CurTaskIDGet();
372     struct PthreadData *pthreadData = NULL;
373 
374     if (!IsPthread(taskID)) {
375         /* native thread just delete self */
376         while (LOS_OK != LOS_TaskDelete(taskID)) {
377         }
378     }
379 
380     pthreadData = GetPthreadData(taskID);
381     if (pthreadData->detached) {
382         free(pthreadData);
383         while (LOS_OK != LOS_TaskDelete(taskID)) {
384         }
385     } else {
386         pthreadData->exitCode = retVal;
387         pthreadData->exited = TRUE;
388         while (LOS_ERRNO_TSK_SUSPEND_LOCKED != LOS_TaskSuspend(taskID)) {
389         }
390     }
391 }
392 
pthread_setname_np(pthread_t thread,const char * name)393 int pthread_setname_np(pthread_t thread, const char *name)
394 {
395     UINT32 taskID = P2T(thread);
396     if (!IsValidTask(taskID)) {
397         return ESRCH;
398     }
399 
400     if (!IsPthread(taskID)) {
401         /* native thread's name is readonly so it can not be set */
402         return EPERM;
403     }
404 
405     /* ensure the following strcpy_s not fail and making unneeded side effect (set src to empty string) */
406     if (strlen(name) >= PTHREAD_NAMELEN) {
407         return ERANGE;
408     }
409 
410     if (strcpy_s(LOS_TaskNameGet(taskID), PTHREAD_NAMELEN, name) != EOK) {
411         return ERANGE;
412     }
413     return 0;
414 }
415 
pthread_getname_np(pthread_t thread,char * buf,size_t buflen)416 int pthread_getname_np(pthread_t thread, char *buf, size_t buflen)
417 {
418     const char *name = NULL;
419     UINT32 taskID = P2T(thread);
420     if (!IsValidTask(taskID)) {
421         return ESRCH;
422     }
423 
424     name = LOS_TaskNameGet(taskID);
425     if (name == NULL) {
426         name = "";
427     }
428 
429     /* ensure the following strcpy_s not fail and making unneeded side effect (set src to empty string) */
430     if (strlen(name) >= PTHREAD_NAMELEN) {
431         return ERANGE;
432     }
433 
434     if (strcpy_s(buf, buflen, name) != EOK) {
435         return ERANGE;
436     }
437     return 0;
438 }
439