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