1 /*
2 * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3 */
4
5 #include <string.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <time.h>
9 #include <sched.h>
10 #include <aos/errno.h>
11 #include <aos/kernel.h>
12 #include <aos/rhino.h>
13
14 #include "internal/pthread.h"
15 #include "internal/sched.h"
16
17 pthread_mutex_t g_pthread_lock = PTHREAD_MUTEX_INITIALIZER;
18
pthread_atfork(void (* prepare)(void),void (* parent)(void),void (* child)(void))19 int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
20 {
21 /* Fork is not supported. */
22 return ENOSYS;
23 }
24
pthread_cleanup_pop(int execute)25 void pthread_cleanup_pop(int execute)
26 {
27 pthread_tcb_t *ptcb = NULL;
28 pthread_cleanup_t *cleanup = NULL;
29
30 ptcb = __pthread_get_tcb(pthread_self());
31 if (ptcb == NULL) {
32 return;
33 }
34
35 cleanup = ptcb->cleanup;
36 if (cleanup != NULL) {
37 ptcb->cleanup = cleanup->prev;
38 if (execute != 0) {
39 cleanup->cleanup_routine(cleanup->para);
40 }
41 free(cleanup);
42 }
43 }
44
pthread_cleanup_push(void (* routine)(void *),void * arg)45 void pthread_cleanup_push(void (*routine)(void *), void *arg)
46 {
47 pthread_tcb_t *ptcb = NULL;
48 pthread_cleanup_t *cleanup = NULL;
49
50 ptcb = __pthread_get_tcb(pthread_self());
51 if (ptcb == NULL) {
52 return;
53 }
54
55 cleanup = (pthread_cleanup_t *) malloc(sizeof(pthread_cleanup_t));
56 if (cleanup != NULL) {
57 cleanup->cleanup_routine = routine;
58 cleanup->para = arg;
59 cleanup->prev = ptcb->cleanup;
60 ptcb->cleanup = cleanup;
61 }
62 }
63
do_pthread_cleanup(pthread_tcb_t * ptcb)64 static void do_pthread_cleanup(pthread_tcb_t *ptcb)
65 {
66 pthread_cleanup_t *cleanup = NULL;
67
68 /* Execute all existed cleanup functions and free it. */
69 do {
70 cleanup = ptcb->cleanup;
71 if (cleanup != NULL) {
72 ptcb->cleanup = cleanup->prev;
73 cleanup->cleanup_routine(cleanup->para);
74 free(cleanup);
75 }
76 } while (ptcb->cleanup != NULL);
77 }
78
79 /* Exit the pthread, never return. */
pthread_exit(void * value_ptr)80 void pthread_exit(void* value_ptr)
81 {
82 pthread_tcb_t *ptcb = NULL;
83
84 ptcb = __pthread_get_tcb(pthread_self());
85 if (ptcb == NULL) {
86 return;
87 }
88
89 ptcb->return_value = value_ptr;
90
91 /* Run cleanup functions of the thread */
92 do_pthread_cleanup(ptcb);
93
94 /* Run destructor functions of the thread */
95 pthread_tsd_dtors();
96
97 /* The underlying task in kernel is not available, unlink it from ptcb. */
98 aos_task_ptcb_set(&(ptcb->task), NULL);
99 ptcb->task = NULL;
100
101 if (ptcb->attr.detachstate == PTHREAD_CREATE_JOINABLE) {
102 /* Give join sem if is joinable */
103 aos_sem_signal(&(ptcb->join_sem));
104 } else if (ptcb->attr.detachstate == PTHREAD_CREATE_DETACHED) {
105 /* Free the join sem */
106 aos_sem_free(&(ptcb->join_sem));
107
108 /* The user/kernel task stack and task tcb should be freed by kernel, here we
109 * only free the ptcb.
110 */
111 ptcb->magic = 0;
112 free(ptcb);
113 } else {
114 ;
115 }
116
117 /* Exit the task, never return. */
118 aos_task_exit(0);
119 }
120
start_pthread(void * arg)121 static void start_pthread(void *arg)
122 {
123 pthread_tcb_t *ptcb = arg;
124 pthread_exit(ptcb->thread_entry(ptcb->thread_para));
125 }
126
pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)127 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
128 void *(*start_routine)(void *), void *arg)
129 {
130 int ret = 0;
131 pthread_tcb_t *ptcb = NULL; /* The ptcb of new thread. */
132 pthread_tcb_t *ptcb_c = NULL; /* The ptcb of current thread. */
133 int kpolicy = 0;
134 int kpriority = 0;
135
136 if ((thread == NULL) || ((attr != NULL) && (attr->flag != PTHREAD_DYN_INIT))
137 || (start_routine == NULL)) {
138 return EINVAL;
139 }
140 /* Init the pthread handle as NULL. */
141 *thread = NULL;
142
143 /* Create ptcb for the new thread. */
144 ptcb = (pthread_tcb_t *)malloc(sizeof(pthread_tcb_t));
145 if (ptcb == NULL) {
146 return ENOMEM;
147 }
148 memset(ptcb, 0, sizeof(pthread_tcb_t));
149 ptcb->magic = PTHREAD_TCB_MAGIC;
150
151 if (attr != NULL) {
152 ptcb->attr = *attr;
153 if (attr->inheritsched == PTHREAD_INHERIT_SCHED) {
154 ptcb_c = __pthread_get_tcb(pthread_self());
155 if (ptcb_c != NULL) {
156 ptcb->attr = ptcb_c->attr;
157 }
158 }
159 } else {
160 pthread_attr_init(&(ptcb->attr));
161 }
162
163 /* Init joinable semaphore. */
164 if (ptcb->attr.detachstate == PTHREAD_CREATE_JOINABLE) {
165 ret = aos_sem_new(&(ptcb->join_sem), 0);
166 if (ret != 0) {
167 ret = -1;
168 goto out3;
169 }
170 }
171
172 ptcb->thread_entry = start_routine;
173 ptcb->thread_para = arg;
174
175 strncpy(ptcb->thread_name, "posix_thread", PTHREAD_NAME_MAX_LEN);
176 kpolicy = aos_task_sched_policy_get_default();
177 kpriority = sched_priority_posix2rhino(kpolicy, ptcb->attr.sched_priority);
178 if (kpriority < 0) {
179 ret = -1;
180 goto out2;
181 }
182 ret = aos_task_create(&(ptcb->task), ptcb->thread_name, start_pthread,
183 ptcb, NULL, ptcb->attr.stacksize, kpriority, 0);
184 if (ret != 0) {
185 ret = -1;
186 goto out2;
187 }
188
189 /* Store ptcb in kernel task tcb, and get it back when call pthread_self. */
190 ret = aos_task_ptcb_set(&(ptcb->task), ptcb);
191 if (ret != 0) {
192 ret = -1;
193 goto out1;
194 }
195
196 /* Success. */
197 *thread = ptcb;
198
199 ret = aos_task_resume(&(ptcb->task));
200 if (ret != 0) {
201 ret = -1;
202 *thread = NULL;
203 goto out1;
204 }
205
206 return 0;
207
208 out1:
209 aos_task_delete(&(ptcb->task));
210 out2:
211 aos_sem_free(&(ptcb->join_sem));
212 out3:
213 if (ptcb != NULL) {
214 ptcb->magic = 0;
215 free(ptcb);
216 }
217 return ret;
218 }
219
pthread_detach(pthread_t thread)220 int pthread_detach(pthread_t thread)
221 {
222 int ret = 0;
223 pthread_tcb_t *ptcb = NULL;
224
225 if (thread == NULL) {
226 return EINVAL;
227 }
228
229 ptcb = __pthread_get_tcb(thread);
230 if (ptcb == NULL) {
231 return EINVAL;
232 }
233
234 if (ptcb->attr.detachstate == PTHREAD_CREATE_DETACHED) {
235 return EINVAL;
236 } else {
237 ret = pthread_mutex_lock(&g_pthread_lock);
238 if (ret != 0) {
239 return EAGAIN;
240 }
241 ptcb->attr.detachstate = PTHREAD_CREATE_DETACHED;
242 pthread_mutex_unlock(&g_pthread_lock);
243 }
244
245 return 0;
246 }
247
pthread_timedjoin_np(pthread_t thread,void ** retval,const struct timespec * abstime)248 int pthread_timedjoin_np(pthread_t thread, void **retval, const struct timespec *abstime)
249 {
250 int ret = 0;
251 pthread_tcb_t *ptcb = NULL;
252 struct timeval time_now = {0};
253 uint64_t msec = AOS_WAIT_FOREVER;
254 uint64_t nsec = 0;
255 int64_t sec = 0;
256
257 if (thread == NULL) {
258 return EINVAL;
259 }
260 ptcb = __pthread_get_tcb(thread);
261 if (ptcb == NULL) {
262 return EINVAL;
263 }
264
265 if (ptcb == pthread_self()) {
266 return EINVAL;
267 }
268
269 if (ptcb->attr.detachstate != PTHREAD_CREATE_JOINABLE) {
270 return EINVAL;
271 }
272
273 /* Get the time to wait. */
274 if (abstime != NULL) {
275 gettimeofday(&time_now, NULL);
276
277 if ((time_now.tv_usec * 1000) > abstime->tv_nsec) {
278 nsec = abstime->tv_nsec + 1000000000 - time_now.tv_usec * 1000;
279 sec = abstime->tv_sec - time_now.tv_sec - 1;
280 } else {
281 nsec = abstime->tv_nsec - time_now.tv_usec * 1000;
282 sec = abstime->tv_sec - time_now.tv_sec;
283 }
284
285 if (sec < 0) {
286 return EINVAL;
287 }
288
289 msec = sec * 1000 + nsec / 1000000;
290 }
291
292 ret = aos_sem_wait(&(ptcb->join_sem), msec);
293 if (ret == 0) {
294 if (retval != NULL) {
295 *retval = ptcb->return_value;
296 }
297 /* The task tcb struct and user/kernel stack should be freed by kernel when task delete */
298 aos_sem_free(&(ptcb->join_sem));
299 ptcb->magic = 0;
300 free(ptcb);
301 } else if (ret == -ETIMEDOUT) {
302 return ETIMEDOUT;
303 } else {
304 return -1;
305 }
306
307 return 0;
308 }
309
pthread_join(pthread_t thread,void ** retval)310 int pthread_join(pthread_t thread, void **retval)
311 {
312
313 return pthread_timedjoin_np(thread, retval, NULL);
314 }
315
pthread_cancel(pthread_t thread)316 int pthread_cancel(pthread_t thread)
317 {
318 return ENOSYS;
319 }
320
pthread_testcancel(void)321 void pthread_testcancel(void)
322 {
323 return;
324 }
325
pthread_setcancelstate(int state,int * oldstate)326 int pthread_setcancelstate(int state, int *oldstate)
327 {
328 return ENOSYS;
329 }
330
pthread_setcanceltype(int type,int * oldtype)331 int pthread_setcanceltype(int type, int *oldtype)
332 {
333 return ENOSYS;
334 }
335
pthread_kill(pthread_t thread,int sig)336 int pthread_kill(pthread_t thread, int sig)
337 {
338 return ENOSYS;
339 }
340
pthread_equal(pthread_t t1,pthread_t t2)341 int pthread_equal(pthread_t t1, pthread_t t2)
342 {
343 return (int)(t1 == t2);
344 }
345
pthread_getschedparam(pthread_t thread,int * policy,struct sched_param * param)346 int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param)
347 {
348 int ret = 0;
349 pthread_tcb_t *ptcb = NULL;
350 uint8_t priority = 0;
351 unsigned int slice = 0;
352 int kpolicy = 0;
353
354 if (policy == NULL || param == NULL) {
355 return EINVAL;
356 }
357
358 ptcb = __pthread_get_tcb(thread);
359 if (ptcb == NULL) {
360 return ESRCH;
361 }
362
363 ret = aos_task_sched_policy_get(&(ptcb->task), (uint8_t *)&kpolicy);
364 if (ret != 0) {
365 return EINVAL;
366 }
367
368 ret = aos_task_pri_get(&(ptcb->task), &priority);
369 if (ret != 0){
370 return EINVAL;
371 }
372
373 /* Slice should be 0 if that is not RR policy. */
374 ret = aos_task_time_slice_get(&(ptcb->task), (uint32_t *) &slice);
375 if (ret != 0) {
376 return EINVAL;
377 }
378
379 *policy = sched_policy_rhino2posix(kpolicy);
380 param->sched_priority = sched_priority_rhino2posix(kpolicy, priority);
381 param->slice = slice;
382
383 return 0;
384 }
385
pthread_setschedparam(pthread_t thread,int policy,const struct sched_param * param)386 int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param)
387 {
388 int ret = 0;
389 pthread_tcb_t *ptcb = NULL;
390 uint8_t priority = 0;
391 int kpolicy = 0;
392
393 ptcb = __pthread_get_tcb(thread);
394 if (ptcb == NULL) {
395 return EINVAL;
396 }
397
398 kpolicy = sched_policy_posix2rhino(policy);
399 if (kpolicy == -1) {
400 return EINVAL;
401 }
402
403 if ((param == NULL) || (param->sched_priority < aos_sched_get_priority_min(kpolicy))
404 || (param->sched_priority > aos_sched_get_priority_max(kpolicy))) {
405 return EINVAL;
406 }
407
408 priority = sched_priority_posix2rhino(kpolicy, param->sched_priority);
409
410 /* Change the policy and priority of the thread */
411 ret = aos_task_sched_policy_set(&(ptcb->task), kpolicy, priority);
412 if ((ret == 0) && (kpolicy == KSCHED_RR)) {
413 ret = aos_task_time_slice_set(&(ptcb->task), param->slice);
414 }
415 if (ret != 0) {
416 return -1;
417 }
418
419 ptcb->attr.sched_priority = param->sched_priority;
420 ptcb->attr.sched_slice = param->slice;
421
422 return 0;
423 }
424
pthread_self(void)425 pthread_t pthread_self(void)
426 {
427 int ret = 0;
428 pthread_tcb_t *ptcb = NULL;
429 aos_task_t task;
430
431 task = aos_task_self();
432 aos_task_ptcb_get(&task, (void **)&ptcb);
433 if (ptcb == NULL) {
434 /* Create ptcb for native task in case that call pthread_self */
435 ptcb = (pthread_tcb_t *)malloc(sizeof(pthread_tcb_t));
436 if (ptcb == NULL) {
437 return NULL;
438 }
439 memset(ptcb, 0, sizeof(pthread_tcb_t));
440
441 ptcb->magic = PTHREAD_TCB_MAGIC;
442 ptcb->task = task;
443
444 /* Set ptcb in task tcb. */
445 ret = aos_task_ptcb_set(&(ptcb->task), ptcb);
446 if (ret != 0) {
447 ptcb->magic = 0;
448 free(ptcb);
449 return NULL;
450 }
451 }
452
453 return ptcb;
454 }
455
pthread_setschedprio(pthread_t thread,int prio)456 int pthread_setschedprio(pthread_t thread, int prio)
457 {
458 int ret = 0;
459 pthread_tcb_t * ptcb = NULL;
460 uint8_t old_prio = 0;
461 int kpolicy = 0;
462 int kpriority = 0;
463
464 ptcb = __pthread_get_tcb(thread);
465 if (ptcb == NULL) {
466 return EINVAL;
467 }
468
469 ret = aos_task_sched_policy_get(&(ptcb->task), (uint8_t *)&kpolicy);
470 if (ret != 0) {
471 return -1;
472 }
473
474 kpriority = sched_priority_posix2rhino(kpolicy, prio);
475 if (kpriority < 0) {
476 return -1;
477 }
478
479 ret = aos_task_pri_change(&(ptcb->task), kpriority, &old_prio);
480 if (ret != 0) {
481 return -1;
482 }
483
484 return 0;
485 }
486
pthread_once(pthread_once_t * once_control,void (* init_routine)(void))487 int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
488 {
489 int ret = 0;
490
491 ret = pthread_mutex_lock(&g_pthread_lock);
492 if (ret != 0) {
493 return EAGAIN;
494 }
495
496 if (*once_control == PTHREAD_ONCE_INIT)
497 {
498 *once_control = !PTHREAD_ONCE_INIT;
499
500 pthread_mutex_unlock(&g_pthread_lock);
501
502 init_routine ();
503 return 0;
504 }
505
506 pthread_mutex_unlock(&g_pthread_lock);
507
508 return 0;
509 }
510
pthread_getcpuclockid(pthread_t thread,clockid_t * clock_id)511 int pthread_getcpuclockid(pthread_t thread, clockid_t *clock_id)
512 {
513 if ((thread == NULL) || (clock_id == NULL)) {
514 return EINVAL;
515 }
516
517 *clock_id = CLOCK_MONOTONIC;
518
519 return 0;
520 }
521
pthread_getconcurrency(void)522 int pthread_getconcurrency(void)
523 {
524 /* User thread and kernel thread are one-to-one correspondence in AliOS Things,
525 so the concurrency is 0 */
526 return 0;
527 }
528
pthread_setconcurrency(int new_level)529 int pthread_setconcurrency(int new_level)
530 {
531 /* User thread and kernel thread are one-to-one correspondence in AliOS Things,
532 so the concurrency can not be set */
533 return ENOSYS;
534 }
535
pthread_setname_np(pthread_t thread,const char * name)536 int pthread_setname_np(pthread_t thread, const char *name)
537 {
538 pthread_tcb_t *ptcb = NULL;
539
540 if ((thread == NULL) || (name == NULL)) {
541 return EINVAL;
542 }
543
544 ptcb = __pthread_get_tcb(thread);
545 if (ptcb == NULL) {
546 return EINVAL;
547 }
548
549 /* Truncate the name if it's too long. */
550 strncpy(ptcb->thread_name, name, PTHREAD_NAME_MAX_LEN);
551 if (strlen(name) > PTHREAD_NAME_MAX_LEN) {
552 return ERANGE;
553 }
554
555 return 0;
556 }
557
pthread_getname_np(pthread_t thread,char * name,size_t len)558 int pthread_getname_np(pthread_t thread, char *name, size_t len)
559 {
560 pthread_tcb_t *ptcb = NULL;
561
562 if ((thread == NULL) || (name == NULL)) {
563 return EINVAL;
564 }
565
566 if (len < PTHREAD_NAME_MAX_LEN) {
567 return ERANGE;
568 }
569
570 ptcb = __pthread_get_tcb(thread);
571 if (ptcb == NULL) {
572 return EINVAL;
573 }
574
575 memset(name, 0, len);
576 strncpy(name, ptcb->thread_name, len);
577
578 return 0;
579 }
580