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