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