xref: /nrf52832-nimble/rt-thread/components/libc/pthreads/pthread.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-01-26     Bernard      Fix pthread_detach issue for a none-joinable
9  *                             thread.
10  */
11 
12 #include <pthread.h>
13 #include <sched.h>
14 #include "pthread_internal.h"
15 
pthread_system_init(void)16 int pthread_system_init(void)
17 {
18     /* initialize key area */
19     pthread_key_system_init();
20     /* initialize posix mqueue */
21     posix_mq_system_init();
22     /* initialize posix semaphore */
23     posix_sem_system_init();
24 
25     return 0;
26 }
27 INIT_COMPONENT_EXPORT(pthread_system_init);
28 
_pthread_cleanup(rt_thread_t tid)29 static void _pthread_cleanup(rt_thread_t tid)
30 {
31     _pthread_data_t *ptd;
32     ptd = _pthread_get_data(tid);
33 
34     /* clear cleanup function */
35     tid->cleanup = RT_NULL;
36     if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
37     {
38         rt_sem_release(ptd->joinable_sem);
39     }
40     else
41     {
42         /* release pthread resource */
43         pthread_detach(tid);
44     }
45 }
46 
pthread_entry_stub(void * parameter)47 static void pthread_entry_stub(void *parameter)
48 {
49     _pthread_data_t *ptd;
50     void *value;
51 
52     ptd = (_pthread_data_t *)parameter;
53 
54     /* execute pthread entry */
55     value = ptd->thread_entry(ptd->thread_parameter);
56     /* set value */
57     ptd->return_value = value;
58 }
59 
pthread_create(pthread_t * tid,const pthread_attr_t * attr,void * (* start)(void *),void * parameter)60 int pthread_create(pthread_t            *tid,
61                    const pthread_attr_t *attr,
62                    void *(*start)(void *), void *parameter)
63 {
64     int result;
65     void *stack;
66     char name[RT_NAME_MAX];
67     static rt_uint16_t pthread_number = 0;
68     _pthread_data_t *ptd;
69 
70     /* tid shall be provided */
71     RT_ASSERT(tid != RT_NULL);
72 
73     /* allocate posix thread data */
74     ptd = (_pthread_data_t *)rt_malloc(sizeof(_pthread_data_t));
75     if (ptd == RT_NULL)
76         return ENOMEM;
77     /* clean posix thread data memory */
78     rt_memset(ptd, 0, sizeof(_pthread_data_t));
79     ptd->canceled = 0;
80     ptd->cancelstate = PTHREAD_CANCEL_DISABLE;
81     ptd->canceltype = PTHREAD_CANCEL_DEFERRED;
82     ptd->magic = PTHREAD_MAGIC;
83 
84     if (attr != RT_NULL)
85     {
86         ptd->attr = *attr;
87     }
88     else
89     {
90         /* use default attribute */
91         pthread_attr_init(&ptd->attr);
92     }
93 
94     rt_snprintf(name, sizeof(name), "pth%02d", pthread_number ++);
95     if (ptd->attr.stack_base == 0)
96     {
97         stack = (void *)rt_malloc(ptd->attr.stack_size);
98     }
99     else
100     {
101         stack = (void *)(ptd->attr.stack_base);
102     }
103 
104     if (stack == RT_NULL)
105     {
106         rt_free(ptd);
107 
108         return ENOMEM;
109     }
110 
111     /* pthread is a static thread object */
112     ptd->tid = (rt_thread_t) rt_malloc(sizeof(struct rt_thread));
113     if (ptd->tid == RT_NULL)
114     {
115         if (ptd->attr.stack_base == 0)
116             rt_free(stack);
117         rt_free(ptd);
118 
119         return ENOMEM;
120     }
121 
122     if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
123     {
124         ptd->joinable_sem = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
125         if (ptd->joinable_sem == RT_NULL)
126         {
127             if (ptd->attr.stack_base != 0)
128                 rt_free(stack);
129             rt_free(ptd);
130 
131             return ENOMEM;
132         }
133     }
134     else
135     {
136         ptd->joinable_sem = RT_NULL;
137     }
138 
139     /* set parameter */
140     ptd->thread_entry = start;
141     ptd->thread_parameter = parameter;
142 
143     /* initial this pthread to system */
144     if (rt_thread_init(ptd->tid, name, pthread_entry_stub, ptd,
145                        stack, ptd->attr.stack_size,
146                        ptd->attr.priority, 5) != RT_EOK)
147     {
148         if (ptd->attr.stack_base == 0)
149             rt_free(stack);
150         if (ptd->joinable_sem != RT_NULL)
151             rt_sem_delete(ptd->joinable_sem);
152         rt_free(ptd);
153 
154         return EINVAL;
155     }
156 
157     /* set pthread id */
158     *tid = ptd->tid;
159 
160     /* set pthread cleanup function and ptd data */
161     (*tid)->cleanup = _pthread_cleanup;
162     (*tid)->user_data = (rt_uint32_t)ptd;
163 
164     /* start thread */
165     result = rt_thread_startup(*tid);
166     if (result == RT_EOK)
167         return 0;
168 
169     /* start thread failed */
170     rt_thread_detach(ptd->tid);
171     if (ptd->attr.stack_base == 0)
172         rt_free(stack);
173     if (ptd->joinable_sem != RT_NULL)
174         rt_sem_delete(ptd->joinable_sem);
175 
176     rt_free(ptd);
177 
178     return EINVAL;
179 }
180 RTM_EXPORT(pthread_create);
181 
pthread_detach(pthread_t thread)182 int pthread_detach(pthread_t thread)
183 {
184     _pthread_data_t *ptd;
185 
186     ptd = _pthread_get_data(thread);
187 
188     if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED)
189     {
190         /* The implementation has detected that the value specified by thread does not refer
191          * to a joinable thread.
192          */
193         return EINVAL;
194     }
195 
196     if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
197     {
198         /* delete joinable semaphore */
199         if (ptd->joinable_sem != RT_NULL)
200             rt_sem_delete(ptd->joinable_sem);
201         /* detach thread object */
202         rt_thread_detach(ptd->tid);
203 
204         /* release thread resource */
205         if (ptd->attr.stack_base == RT_NULL)
206         {
207             /* release thread allocated stack */
208             rt_free(ptd->tid->stack_addr);
209         }
210         else
211         {
212             /* clean stack addr pointer */
213             ptd->tid->stack_addr = RT_NULL;
214         }
215 
216         /*
217          * if this thread create the local thread data,
218          * delete it
219          */
220         if (ptd->tls != RT_NULL)
221             rt_free(ptd->tls);
222         rt_free(ptd->tid);
223         rt_free(ptd);
224     }
225     else
226     {
227         rt_enter_critical();
228         /* change to detach state */
229         ptd->attr.detachstate = PTHREAD_CREATE_DETACHED;
230 
231         /* detach joinable semaphore */
232         rt_sem_delete(ptd->joinable_sem);
233         ptd->joinable_sem = RT_NULL;
234         rt_exit_critical();
235     }
236 
237     return 0;
238 }
239 RTM_EXPORT(pthread_detach);
240 
pthread_join(pthread_t thread,void ** value_ptr)241 int pthread_join(pthread_t thread, void **value_ptr)
242 {
243     _pthread_data_t *ptd;
244     rt_err_t result;
245 
246     if (thread == rt_thread_self())
247     {
248         /* join self */
249         return EDEADLK;
250     }
251 
252     ptd = _pthread_get_data(thread);
253     if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED)
254         return EINVAL; /* join on a detached pthread */
255 
256     result = rt_sem_take(ptd->joinable_sem, RT_WAITING_FOREVER);
257     if (result == RT_EOK)
258     {
259         /* get return value */
260         if (value_ptr != RT_NULL)
261             *value_ptr = ptd->return_value;
262 
263         /* release resource */
264         pthread_detach(thread);
265     }
266     else
267     {
268         return ESRCH;
269     }
270 
271     return 0;
272 }
273 RTM_EXPORT(pthread_join);
274 
pthread_exit(void * value)275 void pthread_exit(void *value)
276 {
277     _pthread_data_t *ptd;
278     _pthread_cleanup_t *cleanup;
279     extern _pthread_key_data_t _thread_keys[PTHREAD_KEY_MAX];
280 
281     ptd = _pthread_get_data(rt_thread_self());
282 
283     rt_enter_critical();
284     /* disable cancel */
285     ptd->cancelstate = PTHREAD_CANCEL_DISABLE;
286     /* set return value */
287     ptd->return_value = value;
288     rt_exit_critical();
289 
290     /* invoke pushed cleanup */
291     while (ptd->cleanup != RT_NULL)
292     {
293         cleanup = ptd->cleanup;
294         ptd->cleanup = cleanup->next;
295 
296         cleanup->cleanup_func(cleanup->parameter);
297         /* release this cleanup function */
298         rt_free(cleanup);
299     }
300 
301     /* destruct thread local key */
302     if (ptd->tls != RT_NULL)
303     {
304         void *data;
305         rt_uint32_t index;
306 
307         for (index = 0; index < PTHREAD_KEY_MAX; index ++)
308         {
309             if (_thread_keys[index].is_used)
310             {
311                 data = ptd->tls[index];
312                 if (data)
313                     _thread_keys[index].destructor(data);
314             }
315         }
316 
317         /* release tls area */
318         rt_free(ptd->tls);
319         ptd->tls = RT_NULL;
320     }
321 
322     if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
323     {
324         /* release the joinable pthread */
325         rt_sem_release(ptd->joinable_sem);
326     }
327 
328     /* detach thread */
329     rt_thread_detach(ptd->tid);
330     /* reschedule thread */
331     rt_schedule();
332 }
333 RTM_EXPORT(pthread_exit);
334 
pthread_once(pthread_once_t * once_control,void (* init_routine)(void))335 int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
336 {
337     RT_ASSERT(once_control != RT_NULL);
338     RT_ASSERT(init_routine != RT_NULL);
339 
340     rt_enter_critical();
341     if (!(*once_control))
342     {
343         /* call routine once */
344         *once_control = 1;
345         rt_exit_critical();
346 
347         init_routine();
348     }
349     rt_exit_critical();
350 
351     return 0;
352 }
353 RTM_EXPORT(pthread_once);
354 
pthread_atfork(void (* prepare)(void),void (* parent)(void),void (* child)(void))355 int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
356 {
357     return EOPNOTSUPP;
358 }
359 RTM_EXPORT(pthread_atfork);
360 
pthread_kill(pthread_t thread,int sig)361 int pthread_kill(pthread_t thread, int sig)
362 {
363 #ifdef RT_USING_SIGNALS
364     return rt_thread_kill(thread, sig);
365 #else
366     return ENOSYS;
367 #endif
368 }
369 RTM_EXPORT(pthread_kill);
370 
371 #ifdef RT_USING_SIGNALS
pthread_sigmask(int how,const sigset_t * set,sigset_t * oset)372 int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
373 {
374     return sigprocmask(how, set, oset);
375 }
376 #endif
377 
pthread_cleanup_pop(int execute)378 void pthread_cleanup_pop(int execute)
379 {
380     _pthread_data_t *ptd;
381     _pthread_cleanup_t *cleanup;
382 
383     /* get posix thread data */
384     ptd = _pthread_get_data(rt_thread_self());
385     RT_ASSERT(ptd != RT_NULL);
386 
387     if (execute)
388     {
389         rt_enter_critical();
390         cleanup = ptd->cleanup;
391         if (cleanup)
392             ptd->cleanup = cleanup->next;
393         rt_exit_critical();
394 
395         if (cleanup)
396         {
397             cleanup->cleanup_func(cleanup->parameter);
398 
399             rt_free(cleanup);
400         }
401     }
402 }
403 RTM_EXPORT(pthread_cleanup_pop);
404 
pthread_cleanup_push(void (* routine)(void *),void * arg)405 void pthread_cleanup_push(void (*routine)(void *), void *arg)
406 {
407     _pthread_data_t *ptd;
408     _pthread_cleanup_t *cleanup;
409 
410     /* get posix thread data */
411     ptd = _pthread_get_data(rt_thread_self());
412     RT_ASSERT(ptd != RT_NULL);
413 
414     cleanup = (_pthread_cleanup_t *)rt_malloc(sizeof(_pthread_cleanup_t));
415     if (cleanup != RT_NULL)
416     {
417         cleanup->cleanup_func = routine;
418         cleanup->parameter = arg;
419 
420         rt_enter_critical();
421         cleanup->next = ptd->cleanup;
422         ptd->cleanup = cleanup;
423         rt_exit_critical();
424     }
425 }
426 RTM_EXPORT(pthread_cleanup_push);
427 
428 /*
429  * According to IEEE Std 1003.1, 2004 Edition , following pthreads
430  * interface support cancellation point:
431  * mq_receive()
432  * mq_send()
433  * mq_timedreceive()
434  * mq_timedsend()
435  * msgrcv()
436  * msgsnd()
437  * msync()
438  * pthread_cond_timedwait()
439  * pthread_cond_wait()
440  * pthread_join()
441  * pthread_testcancel()
442  * sem_timedwait()
443  * sem_wait()
444  *
445  * A cancellation point may also occur when a thread is
446  * executing the following functions:
447  * pthread_rwlock_rdlock()
448  * pthread_rwlock_timedrdlock()
449  * pthread_rwlock_timedwrlock()
450  * pthread_rwlock_wrlock()
451  *
452  * The pthread_cancel(), pthread_setcancelstate(), and pthread_setcanceltype()
453  * functions are defined to be async-cancel safe.
454  */
455 
pthread_setcancelstate(int state,int * oldstate)456 int pthread_setcancelstate(int state, int *oldstate)
457 {
458     _pthread_data_t *ptd;
459 
460     /* get posix thread data */
461     ptd = _pthread_get_data(rt_thread_self());
462     RT_ASSERT(ptd != RT_NULL);
463 
464     if ((state == PTHREAD_CANCEL_ENABLE) || (state == PTHREAD_CANCEL_DISABLE))
465     {
466         if (oldstate)
467             *oldstate = ptd->cancelstate;
468         ptd->cancelstate = state;
469 
470         return 0;
471     }
472 
473     return EINVAL;
474 }
475 RTM_EXPORT(pthread_setcancelstate);
476 
pthread_setcanceltype(int type,int * oldtype)477 int pthread_setcanceltype(int type, int *oldtype)
478 {
479     _pthread_data_t *ptd;
480 
481     /* get posix thread data */
482     ptd = _pthread_get_data(rt_thread_self());
483     RT_ASSERT(ptd != RT_NULL);
484 
485     if ((type != PTHREAD_CANCEL_DEFERRED) && (type != PTHREAD_CANCEL_ASYNCHRONOUS))
486         return EINVAL;
487 
488     if (oldtype)
489         *oldtype = ptd->canceltype;
490     ptd->canceltype = type;
491 
492     return 0;
493 }
494 RTM_EXPORT(pthread_setcanceltype);
495 
pthread_testcancel(void)496 void pthread_testcancel(void)
497 {
498     int cancel = 0;
499     _pthread_data_t *ptd;
500 
501     /* get posix thread data */
502     ptd = _pthread_get_data(rt_thread_self());
503     RT_ASSERT(ptd != RT_NULL);
504 
505     if (ptd->cancelstate == PTHREAD_CANCEL_ENABLE)
506         cancel = ptd->canceled;
507     if (cancel)
508         pthread_exit((void *)PTHREAD_CANCELED);
509 }
510 RTM_EXPORT(pthread_testcancel);
511 
pthread_cancel(pthread_t thread)512 int pthread_cancel(pthread_t thread)
513 {
514     _pthread_data_t *ptd;
515 
516     /* cancel self */
517     if (thread == rt_thread_self())
518         return 0;
519 
520     /* get posix thread data */
521     ptd = _pthread_get_data(thread);
522     RT_ASSERT(ptd != RT_NULL);
523 
524     /* set canceled */
525     if (ptd->cancelstate == PTHREAD_CANCEL_ENABLE)
526     {
527         ptd->canceled = 1;
528         if (ptd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
529         {
530             /*
531              * to detach thread.
532              * this thread will be removed from scheduler list
533              * and because there is a cleanup function in the
534              * thread (pthread_cleanup), it will move to defunct
535              * thread list and wait for handling in idle thread.
536              */
537             rt_thread_detach(thread);
538         }
539     }
540 
541     return 0;
542 }
543 RTM_EXPORT(pthread_cancel);
544