1 /*
2 * Copyright (c) 2006-2024 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 * 2019-02-07 Bernard Add _pthread_destroy to release pthread resource.
11 * 2022-05-10 xiangxistu Modify the recycle logic about resource of pthread.
12 * 2024-04-15 atwww Modify the recycle logic of TLS in function _pthread_data_destroy,
13 * make it safe for C++11's thread_local destructors.
14 */
15
16 #include <rthw.h>
17 #include <pthread.h>
18 #include <sched.h>
19 #include <sys/time.h>
20 #include "pthread_internal.h"
21
22 RT_DEFINE_HW_SPINLOCK(pth_lock);
23 _pthread_data_t *pth_table[PTHREAD_NUM_MAX] = {NULL};
24 static int concurrency_level;
25
26 /**
27 * @brief Retrieves the private data structure of a specified thread
28 *
29 * This function locates and validates the thread-private data structure associated with the given thread ID.
30 * It uses a spinlock to synchronize access to the global thread table, ensuring data consistency in multithreaded environments.
31 * A magic number validation is performed before returning to guarantee structural integrity.
32 *
33 * @param thread Thread ID used to index into the global thread data table
34 *
35 * @return Pointer to the thread's private data structure on success; NULL if thread ID is invalid or data not initialized
36 *
37 * @note
38 * - Protects global thread table access with spinlock to prevent data race conditions
39 * - Magic number validation (magic) prevents access to released or corrupted thread data
40 * - Internal interface typically used by other POSIX thread library functions
41 */
_pthread_get_data(pthread_t thread)42 _pthread_data_t *_pthread_get_data(pthread_t thread)
43 {
44 _pthread_data_t *ptd;
45
46 if (thread >= PTHREAD_NUM_MAX) return NULL;
47
48 rt_hw_spin_lock(&pth_lock);
49 ptd = pth_table[thread];
50 rt_hw_spin_unlock(&pth_lock);
51
52 if (ptd && ptd->magic == PTHREAD_MAGIC) return ptd;
53
54 return NULL;
55 }
56
57 /**
58 * @brief Get the index position of a thread data in the global thread table
59 *
60 * This function searches for matching thread data index by traversing the global thread table.
61 * Uses spinlock synchronization mechanism to ensure safe access to the global thread table
62 * in multi-threaded environments.
63 *
64 * @param ptd Pointer to the target thread data structure
65 *
66 * @return Index value of type pthread_t
67 * - Returns corresponding index (0~PTHREAD_NUM_MAX-1) when found
68 * - Returns PTHREAD_NUM_MAX when no match is found
69 *
70 * @note
71 * - Protects global thread table access with spinlock to prevent data races from concurrent access
72 * - Uses sequential traversal method to find matching thread data
73 * - Return value of PTHREAD_NUM_MAX indicates no match found in the global table
74 */
_pthread_data_get_pth(_pthread_data_t * ptd)75 pthread_t _pthread_data_get_pth(_pthread_data_t *ptd)
76 {
77 int index;
78
79 rt_hw_spin_lock(&pth_lock);
80 for (index = 0; index < PTHREAD_NUM_MAX; index ++)
81 {
82 if (pth_table[index] == ptd) break;
83 }
84 rt_hw_spin_unlock(&pth_lock);
85
86 return index;
87 }
88
89 /**
90 * @brief Create and initialize a new thread data structure with index allocation
91 *
92 * This function allocates memory for a new thread data structure, initializes its default state,
93 * and registers it in the global thread table. Uses spinlock synchronization to ensure safe
94 * access to the global thread table in multi-threaded environments.
95 *
96 * @return Allocated index value of type pthread_t
97 * - Returns valid index (0~PTHREAD_NUM_MAX-1) on successful allocation
98 * - Returns PTHREAD_NUM_MAX when memory allocation fails or no space available in thread table
99 *
100 * @note
101 * - Protects global thread table access with spinlock to prevent data races
102 * - Uses sequential search to find first available slot in thread table
103 * - Cleans up allocated memory and returns error when no available slots
104 * - Initializes thread data with default states:
105 * - Cancellation disabled (PTHREAD_CANCEL_DISABLE)
106 * - Deferred cancellation type (PTHREAD_CANCEL_DEFERRED)
107 * - Magic number validation (PTHREAD_MAGIC) for structure integrity
108 */
_pthread_data_create(void)109 pthread_t _pthread_data_create(void)
110 {
111 int index;
112 _pthread_data_t *ptd = NULL;
113
114 ptd = (_pthread_data_t*)rt_malloc(sizeof(_pthread_data_t));
115 if (!ptd) return PTHREAD_NUM_MAX;
116
117 memset(ptd, 0x0, sizeof(_pthread_data_t));
118 ptd->canceled = 0;
119 ptd->cancelstate = PTHREAD_CANCEL_DISABLE;
120 ptd->canceltype = PTHREAD_CANCEL_DEFERRED;
121 ptd->magic = PTHREAD_MAGIC;
122
123 rt_hw_spin_lock(&pth_lock);
124 for (index = 0; index < PTHREAD_NUM_MAX; index ++)
125 {
126 if (pth_table[index] == NULL)
127 {
128 pth_table[index] = ptd;
129 break;
130 }
131 }
132 rt_hw_spin_unlock(&pth_lock);
133
134 /* full of pthreads, clean magic and release ptd */
135 if (index == PTHREAD_NUM_MAX)
136 {
137 ptd->magic = 0x0;
138 rt_free(ptd);
139 }
140
141 return index;
142 }
143
144 /**
145 * @brief Destroy thread local storage item at specified index
146 *
147 * This function cleans up thread-local storage data by:
148 * 1. Checking if the key at given index is active
149 * 2. If TLS data exists and a destructor is registered, invoking the destructor
150 * 3. Properly releasing resources associated with the TLS slot
151 *
152 * @param index Index into the thread keys array (0 to PTHREAD_KEY_MAX-1)
153 * @param ptd Pointer to thread data structure containing TLS information
154 *
155 * @note
156 * - Relies on external spinlock protection when accessing shared data
157 * - Only processes valid keys that have been initialized
158 * - Safely handles NULL pointers and missing destructors
159 * - Designed to be called during thread cleanup or explicit TLS destruction
160 * - Matches POSIX thread standard requirements for TLS destructor invocation
161 */
_destroy_item(int index,_pthread_data_t * ptd)162 static inline void _destroy_item(int index, _pthread_data_t *ptd)
163 {
164 extern _pthread_key_data_t _thread_keys[PTHREAD_KEY_MAX];
165 void *data;
166
167 if (_thread_keys[index].is_used)
168 {
169 data = ptd->tls[index];
170 if (data && _thread_keys[index].destructor)
171 {
172 _thread_keys[index].destructor(data);
173 }
174 }
175 }
176
177 #ifdef RT_USING_CPLUSPLUS11
178 #define NOT_USE_CXX_TLS -1
179 #endif
180
181
182 /**
183 * @brief Destroy and clean up a thread data structure along with associated resources
184 *
185 * This function releases all resources associated with a thread data structure including:
186 * - Thread-local storage (TLS) destructors execution
187 * - Joinable semaphore deletion
188 * - Global thread table entry cleanup
189 * - Memory deallocation after proper resource release
190 *
191 * @param ptd Pointer to the thread data structure to be destroyed
192 *
193 * @note
194 * - Protects global thread table access with spinlock to prevent data races
195 * - Handles TLS destruction differently based on C++11 support:
196 * - C++11: Reverse iteration from emutls key position for safe destructor calls
197 * - C-only: Forward iteration through all thread keys
198 * - Maintains strict resource cleanup order:
199 * 1. TLS destructors -> 2. Global table removal -> 3. Semaphore deletion -> 4. Memory release
200 * - Uses magic number validation to prevent double-free and invalid access
201 * - Explicitly clears magic number before final memory release
202 * - Nullifies pointer references after freeing to prevent dangling references
203 * - Thread-safe operation through spinlock protection during critical sections
204 */
_pthread_data_destroy(_pthread_data_t * ptd)205 void _pthread_data_destroy(_pthread_data_t *ptd)
206 {
207 pthread_t pth;
208
209 if (ptd)
210 {
211 /* if this thread create the local thread data,
212 * destruct thread local key
213 */
214 if (ptd->tls != RT_NULL)
215 {
216 int index;
217 #ifdef RT_USING_CPLUSPLUS11
218 /* If C++11 is enabled and emutls is used,
219 * destructors of C++ object must be called safely.
220 */
221 extern pthread_key_t emutls_get_pthread_key(void);
222 pthread_key_t emutls_pthread_key = emutls_get_pthread_key();
223
224 if (emutls_pthread_key != NOT_USE_CXX_TLS)
225 {
226 /* If execution reaches here, C++ 'thread_local' may be used.
227 * Destructors of c++ class object must be called before emutls_key_destructor.
228 */
229 int start = ((emutls_pthread_key - 1 + PTHREAD_KEY_MAX) % PTHREAD_KEY_MAX);
230 int i = 0;
231 for (index = start; i < PTHREAD_KEY_MAX; index = (index - 1 + PTHREAD_KEY_MAX) % PTHREAD_KEY_MAX, i ++)
232 {
233 _destroy_item(index, ptd);
234 }
235 }
236 else
237 #endif
238 {
239 /* If only C TLS is used, that is, POSIX TLS or __Thread_local,
240 * just iterate the _thread_keys from index 0.
241 */
242 for (index = 0; index < PTHREAD_KEY_MAX; index ++)
243 {
244 _destroy_item(index, ptd);
245 }
246 }
247 /* release tls area */
248 rt_free(ptd->tls);
249 ptd->tls = RT_NULL;
250 }
251
252 pth = _pthread_data_get_pth(ptd);
253 /* remove from pthread table */
254 rt_hw_spin_lock(&pth_lock);
255 pth_table[pth] = NULL;
256 rt_hw_spin_unlock(&pth_lock);
257
258 /* delete joinable semaphore */
259 if (ptd->joinable_sem != RT_NULL)
260 {
261 rt_sem_delete(ptd->joinable_sem);
262 ptd->joinable_sem = RT_NULL;
263 }
264
265 /* clean magic */
266 ptd->magic = 0x0;
267
268 /* free ptd */
269 rt_free(ptd);
270 }
271 }
272
273 /**
274 * @brief Perform final cleanup of thread resources during thread termination
275 *
276 * This function handles the complete resource cleanup for a terminated thread, including:
277 * - Clearing cleanup handlers
278 * - Releasing thread stack memory
279 * - Detaching thread data structures
280 * - Final deallocation of thread control block
281 *
282 * @param tid Thread control block pointer to be cleaned up
283 *
284 * @note
285 * - Must be called as the final cleanup step after thread termination
286 * - Follows strict resource release order:
287 * 1. Clear cleanup handlers -> 2. Release stack -> 3. Detach data -> 4. Free control block
288 * - Explicitly nullifies pointers after freeing to prevent dangling references
289 * - Thread-safe operation assumes caller has handled synchronization
290 * - Handles both joinable and detached thread cleanup scenarios
291 * - Designed to work with thread detachment and join completion mechanisms
292 */
_pthread_cleanup(rt_thread_t tid)293 static void _pthread_cleanup(rt_thread_t tid)
294 {
295 /* clear cleanup function */
296 tid->cleanup = RT_NULL;
297
298 /* restore tid stack */
299 rt_free(tid->stack_addr);
300
301 /* clear the "ptd->tid->pthread_data" */
302 tid->pthread_data = RT_NULL;
303
304 /* restore tid control block */
305 rt_free(tid);
306 }
307
308 /**
309 * @brief Thread entry point stub that manages thread execution and resource cleanup
310 *
311 * This function serves as the entry point for POSIX threads, executing the thread's main
312 * function and handling post-exit resource management based on the thread's detach state.
313 *
314 * @param parameter Thread parameter containing the _pthread_data_t structure pointer
315 *
316 * @note
317 * - Executes thread's main function through thread_entry callback
318 * - Handles two resource management scenarios:
319 * - JOINABLE threads: Store return value and release join semaphore
320 * - DETACHED threads: Immediately destroy thread resources
321 * - Maintains strict execution flow:
322 * 1. Execute user thread function -> 2. Check detach state -> 3. Handle cleanup
323 * - Properly coordinates with joinable semaphore mechanism for synchronous termination
324 * - Assumes thread data structure remains valid until cleanup completion
325 * - Thread-safe operation relies on proper synchronization in resource destruction
326 * - Integrates with pthread lifecycle management system for complete resource recovery
327 */
pthread_entry_stub(void * parameter)328 static void pthread_entry_stub(void *parameter)
329 {
330 void *value;
331 _pthread_data_t *ptd;
332
333 ptd = (_pthread_data_t *)parameter;
334
335 /* execute pthread entry */
336 value = ptd->thread_entry(ptd->thread_parameter);
337
338 /* According to "detachstate" to whether or not to recycle resource immediately */
339 if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
340 {
341 /* set value */
342 ptd->return_value = value;
343 rt_sem_release(ptd->joinable_sem);
344 }
345 else
346 {
347 /* release pthread resource */
348 _pthread_data_destroy(ptd);
349 }
350 }
351
352 /**
353 * @brief Creates a new thread in a POSIX-compliant system.
354 *
355 * The `pthread_create` function initializes a new thread in the calling process. The new thread starts execution
356 * by invoking the function specified by the `start` parameter. The thread runs concurrently with the calling thread.
357 *
358 * @param[out] pid
359 * A pointer to a `pthread_t` object where the ID of the newly created thread will be stored.
360 * This ID can be used to refer to the thread in subsequent function calls.
361 *
362 * @param[in] attr
363 * A pointer to a `pthread_attr_t` object that specifies attributes for the new thread, or `NULL` for default attributes.
364 * Default attributes include:
365 * - Detached state: joinable.
366 * - Stack size: implementation-defined default.
367 *
368 * @param[in] start
369 * A pointer to the function that the new thread will execute. This function must have the following signature:
370 * `void *start(void *parameter)`.
371 *
372 * @param[in] parameter
373 * A pointer to data passed as an argument to the `start` function. The meaning and handling of this data is determined
374 * by the `start` function.
375 *
376 * @return
377 * Returns 0 on success. On failure, a non-zero error code is returned, indicating the error condition:
378 * - `EAGAIN`: Insufficient resources to create another thread.
379 * - `EINVAL`: Invalid attributes specified in `attr`.
380 * - `EPERM`: Insufficient permissions to set the requested attributes.
381 *
382 * @note
383 * It is the caller's responsibility to manage the lifetime of any resources associated with the new thread.
384 * If the thread is not detached, it must be joined using `pthread_join` to avoid resource leaks.
385 *
386 * @see pthread_join, pthread_exit, pthread_attr_init
387 */
pthread_create(pthread_t * pid,const pthread_attr_t * attr,void * (* start)(void *),void * parameter)388 int pthread_create(pthread_t *pid,
389 const pthread_attr_t *attr,
390 void *(*start)(void *), void *parameter)
391 {
392 int ret = 0;
393 void *stack;
394 char name[RT_NAME_MAX];
395 static rt_uint16_t pthread_number = 0;
396
397 pthread_t pth_id;
398 _pthread_data_t *ptd;
399
400 /* pid shall be provided */
401 RT_ASSERT(pid != RT_NULL);
402
403 /* allocate posix thread data */
404 pth_id = _pthread_data_create();
405 if (pth_id == PTHREAD_NUM_MAX)
406 {
407 ret = ENOMEM;
408 goto __exit;
409 }
410 /* get pthread data */
411 ptd = _pthread_get_data(pth_id);
412
413 RT_ASSERT(ptd != RT_NULL);
414
415 if (attr != RT_NULL)
416 {
417 ptd->attr = *attr;
418 }
419 else
420 {
421 /* use default attribute */
422 pthread_attr_init(&ptd->attr);
423 }
424
425 if (ptd->attr.stacksize == 0)
426 {
427 ret = EINVAL;
428 goto __exit;
429 }
430
431 rt_snprintf(name, sizeof(name), "pth%02d", pthread_number ++);
432
433 /* pthread is a static thread object */
434 ptd->tid = (rt_thread_t) rt_malloc(sizeof(struct rt_thread));
435 if (ptd->tid == RT_NULL)
436 {
437 ret = ENOMEM;
438 goto __exit;
439 }
440 memset(ptd->tid, 0, sizeof(struct rt_thread));
441
442 if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
443 {
444 ptd->joinable_sem = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
445 if (ptd->joinable_sem == RT_NULL)
446 {
447 ret = ENOMEM;
448 goto __exit;
449 }
450 }
451 else
452 {
453 ptd->joinable_sem = RT_NULL;
454 }
455
456 /* set parameter */
457 ptd->thread_entry = start;
458 ptd->thread_parameter = parameter;
459
460 /* stack */
461 if (ptd->attr.stackaddr == 0)
462 {
463 stack = (void *)rt_malloc(ptd->attr.stacksize);
464 }
465 else
466 {
467 stack = (void *)(ptd->attr.stackaddr);
468 }
469
470 if (stack == RT_NULL)
471 {
472 ret = ENOMEM;
473 goto __exit;
474 }
475
476 /* initial this pthread to system */
477 if (rt_thread_init(ptd->tid, name, pthread_entry_stub, ptd,
478 stack, ptd->attr.stacksize,
479 ptd->attr.schedparam.sched_priority, 20) != RT_EOK)
480 {
481 ret = EINVAL;
482 goto __exit;
483 }
484
485 /* set pthread id */
486 *pid = pth_id;
487
488 /* set pthread cleanup function and ptd data */
489 ptd->tid->cleanup = _pthread_cleanup;
490 ptd->tid->pthread_data = (void *)ptd;
491
492 /* start thread */
493 if (rt_thread_startup(ptd->tid) == RT_EOK)
494 return 0;
495
496 /* start thread failed */
497 rt_thread_detach(ptd->tid);
498 ret = EINVAL;
499
500 __exit:
501 if (pth_id != PTHREAD_NUM_MAX)
502 {
503 _pthread_data_destroy(ptd);
504 }
505 return ret;
506 }
507 RTM_EXPORT(pthread_create);
508
509
510
511 /**
512 * @brief Marks a thread as detached, allowing its resources to be automatically released upon termination.
513 *
514 * The `pthread_detach` function separates the specified thread from the calling thread. Once a thread is detached,
515 * its resources will be automatically reclaimed by the system upon the thread's termination. A detached thread cannot
516 * be joined using `pthread_join`.
517 *
518 * @param[in] thread
519 * The thread ID of the thread to be detached. This must be a valid thread ID returned by `pthread_create`.
520 *
521 * @return
522 * Returns 0 on success. On failure, an error code is returned:
523 * - `EINVAL`: The specified thread is not joinable or is already detached.
524 * - `ESRCH`: No thread with the specified ID could be found.
525 *
526 * @note
527 * - Detaching a thread allows it to run independently. Once detached, the thread's termination status cannot
528 * be retrieved, and it cannot be joined.
529 * - Threads can be created in a detached state using attributes set with `pthread_attr_setdetachstate`.
530 *
531 * @see pthread_create, pthread_join, pthread_attr_setdetachstate
532 */
pthread_detach(pthread_t thread)533 int pthread_detach(pthread_t thread)
534 {
535 int ret = 0;
536 _pthread_data_t *ptd = _pthread_get_data(thread);
537 if (ptd == RT_NULL)
538 {
539 /* invalid pthread id */
540 ret = EINVAL;
541 goto __exit;
542 }
543
544 if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED)
545 {
546 /* The implementation has detected that the value specified by thread does not refer
547 * to a joinable thread.
548 */
549 ret = EINVAL;
550 goto __exit;
551 }
552
553 if ((RT_SCHED_CTX(ptd->tid).stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
554 {
555 /* destroy this pthread */
556 _pthread_data_destroy(ptd);
557 goto __exit;
558 }
559 else
560 {
561 /* change to detach state */
562 ptd->attr.detachstate = PTHREAD_CREATE_DETACHED;
563
564 /* detach joinable semaphore */
565 if (ptd->joinable_sem)
566 {
567 rt_sem_delete(ptd->joinable_sem);
568 ptd->joinable_sem = RT_NULL;
569 }
570 }
571
572 __exit:
573 return ret;
574 }
575 RTM_EXPORT(pthread_detach);
576
577 /**
578 * @brief Waits for the specified thread to terminate and retrieves its exit status.
579 *
580 * The `pthread_join` function blocks the calling thread until the specified thread terminates.
581 * If the specified thread has already terminated, it returns immediately. The exit status of
582 * the terminated thread can optionally be retrieved via the `value_ptr` parameter.
583 *
584 * @param[in] thread
585 * The thread ID of the thread to wait for. This must be a joinable thread created with `pthread_create`.
586 *
587 * @param[out] value_ptr
588 * A pointer to a location where the exit status of the terminated thread will be stored.
589 * If the thread terminated by calling `pthread_exit`, the value passed to `pthread_exit`
590 * will be stored at this location. If this parameter is `NULL`, the exit status is ignored.
591 *
592 * @return
593 * Returns 0 on success. On failure, an error code is returned:
594 * - `ESRCH`: The specified thread does not exist.
595 * - `EINVAL`: The specified thread is not joinable.
596 * - `EDEADLK`: A deadlock was detected (e.g., a thread tries to join itself).
597 *
598 * @note
599 * - Threads must not be detached to use `pthread_join`.
600 * - If `pthread_join` is not called for joinable threads, their resources are not released, leading to resource leaks.
601 *
602 * @see pthread_create, pthread_exit, pthread_detach
603 */
pthread_join(pthread_t thread,void ** value_ptr)604 int pthread_join(pthread_t thread, void **value_ptr)
605 {
606 _pthread_data_t *ptd;
607 rt_err_t result;
608
609 ptd = _pthread_get_data(thread);
610
611 if (ptd == RT_NULL)
612 {
613 return EINVAL; /* invalid pthread id */
614 }
615
616 if (ptd->tid == rt_thread_self())
617 {
618 /* join self */
619 return EDEADLK;
620 }
621
622 if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED)
623 {
624 return EINVAL; /* join on a detached pthread */
625 }
626
627 result = rt_sem_take(ptd->joinable_sem, RT_WAITING_FOREVER);
628 if (result == RT_EOK)
629 {
630 /* get return value */
631 if (value_ptr != RT_NULL)
632 *value_ptr = ptd->return_value;
633
634 /* destroy this pthread */
635 _pthread_data_destroy(ptd);
636 }
637 else
638 {
639 return ESRCH;
640 }
641
642 return 0;
643 }
644 RTM_EXPORT(pthread_join);
645
646 /**
647 * @brief Returns the thread ID of the calling thread.
648 *
649 * The `pthread_self` function returns the thread ID of the calling thread. The thread ID is unique to the
650 * thread within a process and can be used to identify the calling thread in the context of multithreading.
651 *
652 * The value returned by `pthread_self` can be compared with the thread IDs of other threads to determine
653 * if two threads are the same.
654 *
655 * @return
656 * The thread ID of the calling thread.
657 *
658 * @note
659 * - The thread ID returned by `pthread_self` is not the same as the operating system's thread ID.
660 * - This function does not affect the calling thread's state or execution.
661 * - The thread ID returned by `pthread_self` is only meaningful in the context of the current process.
662 *
663 * @see pthread_create, pthread_equal, pthread_join
664 */
pthread_self(void)665 pthread_t pthread_self (void)
666 {
667 rt_thread_t tid;
668 _pthread_data_t *ptd;
669
670 tid = rt_thread_self();
671 if (tid == NULL) return PTHREAD_NUM_MAX;
672
673 /* get pthread data from pthread_data of thread */
674 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
675 RT_ASSERT(ptd != RT_NULL);
676
677 return _pthread_data_get_pth(ptd);
678 }
679 RTM_EXPORT(pthread_self);
680
681 /**
682 * @brief Retrieves the clock ID for the specified thread.
683 *
684 * The `pthread_getcpuclockid` function retrieves the clock ID associated with the CPU time used
685 * by the specified thread.
686 *
687 * @param[in] thread
688 * The thread whose CPU clock ID is to be retrieved. If the thread is the calling thread,
689 * the current thread's ID is used.
690 *
691 * @param[out] clock_id
692 * A pointer to a `clockid_t` variable that will be filled with the clock ID associated
693 * with the specified thread.
694 *
695 * @return
696 * - `0` on success.
697 * - `EINVAL` if the `thread` is not a valid thread identifier.
698 * - `ESRCH` if the specified thread does not exist.
699 *
700 * @note
701 * The clock returned by this function is specific to the thread and is different from the
702 * system-wide clock. It measures the CPU time consumed by the specified thread, not wall-clock
703 * time. The thread's CPU time can be obtained using `clock_gettime` with the returned `clock_id`.
704 *
705 * @see clock_gettime, pthread_create, pthread_self
706 */
pthread_getcpuclockid(pthread_t thread,clockid_t * clock_id)707 int pthread_getcpuclockid(pthread_t thread, clockid_t *clock_id)
708 {
709 if(_pthread_get_data(thread) == NULL)
710 {
711 return EINVAL;
712 }
713
714 *clock_id = (clockid_t)rt_tick_get();
715
716 return 0;
717 }
718 RTM_EXPORT(pthread_getcpuclockid);
719
720 /**
721 * @brief Retrieves the current concurrency level of the program.
722 *
723 * The `pthread_getconcurrency` function returns the current concurrency level of the program.
724 * This value represents the number of threads that can run concurrently in the program,
725 * based on the current settings of the pthreads library. It is used to help tune the behavior
726 * of thread scheduling in some systems.
727 *
728 * @return
729 * The current concurrency level of the program.
730 * - The value is an integer representing the number of threads that are permitted to run
731 * concurrently in the system, based on the library's current configuration.
732 * - A return value of `0` typically means that the system is using the default concurrency
733 * level, which may be determined automatically by the system or by thread creation behavior.
734 *
735 * @note
736 * - The behavior and meaning of concurrency levels can be implementation-dependent,
737 * and it may vary across different systems or environments.
738 * - The function is typically used for diagnostic purposes, and its behavior may not
739 * affect thread execution directly.
740 *
741 * @see pthread_setconcurrency
742 */
pthread_getconcurrency(void)743 int pthread_getconcurrency(void)
744 {
745 return concurrency_level;
746 }
747 RTM_EXPORT(pthread_getconcurrency);
748
749 /**
750 * @brief Sets the concurrency level of the program.
751 *
752 * The `pthread_setconcurrency` function sets the number of threads that are allowed to run concurrently.
753 * The concurrency level defines the maximum number of threads that can be executed in parallel by the system.
754 * This is useful for tuning thread behavior and controlling system resource usage, especially in environments
755 * with limited resources (e.g., CPU cores).
756 *
757 * @param[in] new_level
758 * The new concurrency level to be set. This value represents the number of threads that can execute concurrently.
759 * - A value of `0` typically means that the system will automatically determine the concurrency level based on
760 * the system's configuration and available resources.
761 * - A non-zero value explicitly sets the maximum number of threads that can run concurrently.
762 *
763 * @return
764 * - `0` on success.
765 * - `EINVAL` if the `new_level` is invalid or if the system does not support this functionality.
766 *
767 * @note
768 * - The behavior of this function is system-dependent. Some systems may ignore the concurrency setting
769 * and automatically manage the concurrency based on available resources (e.g., CPU cores).
770 * - This function may not have any effect on systems that do not support concurrency settings at the library level.
771 * - The concurrency level controls thread scheduling policies and is intended to influence how the thread library
772 * manages threads, not how the operating system schedules them at the kernel level.
773 *
774 * @see pthread_getconcurrency
775 */
pthread_setconcurrency(int new_level)776 int pthread_setconcurrency(int new_level)
777 {
778 concurrency_level = new_level;
779
780 return 0;
781 }
782 RTM_EXPORT(pthread_setconcurrency);
783
784 /**
785 * @brief Retrieves the scheduling policy and parameters of a thread.
786 *
787 * The `pthread_getschedparam` function retrieves the scheduling policy and the scheduling parameters
788 * (such as priority) for the specified thread. This allows you to check the scheduling settings of a thread
789 * and can be useful for thread management and performance tuning in a multithreaded application.
790 *
791 * @param[in] thread
792 * The thread whose scheduling policy and parameters are to be retrieved. This is typically a valid
793 * `pthread_t` identifier of a thread that has already been created.
794 *
795 * @param[out] policy
796 * A pointer to an integer where the scheduling policy of the specified thread will be stored. The
797 * value will be one of the following constants defined in `<sched.h>`:
798 * - `SCHED_FIFO`: First-in, first-out scheduling policy.
799 * - `SCHED_RR`: Round-robin scheduling policy.
800 * - `SCHED_OTHER`: Default policy, which is typically used by non-realtime threads.
801 * - `SCHED_IDLE`: For idle threads (system-level threads that do minimal work).
802 * - `SCHED_BATCH`: For threads that should be scheduled with lower priority than interactive threads.
803 * - `SCHED_DEADLINE`: A policy that allows specifying real-time deadlines (on systems that support it).
804 *
805 * @param[out] param
806 * A pointer to a `struct sched_param` where the scheduling parameters (e.g., priority) for the thread
807 * will be stored. The `sched_param` structure typically contains:
808 * - `sched_priority`: The priority value associated with the thread's scheduling policy.
809 *
810 * @return
811 * - `0` on success.
812 * - `ESRCH` if the specified thread does not exist.
813 * - `EINVAL` if an invalid argument is provided, such as an invalid thread ID or null pointers for the policy or parameters.
814 *
815 * @note
816 * - This function retrieves the current scheduling settings for a thread. These settings can be used
817 * to monitor or adjust thread behavior.
818 * - The scheduling policies and priorities may be platform-dependent and subject to system configuration.
819 *
820 * @see pthread_setschedparam, sched_getparam
821 */
pthread_getschedparam(pthread_t thread,int * policy,struct sched_param * param)822 int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param)
823 {
824 _pthread_data_t *ptd;
825
826 ptd = _pthread_get_data(thread);
827 pthread_attr_getschedpolicy(&ptd->attr, policy);
828 pthread_attr_getschedparam(&ptd->attr, param);
829
830 return 0;
831 }
832 RTM_EXPORT(pthread_getschedparam);
833
834 /**
835 * @brief Sets the scheduling policy and parameters for a thread.
836 *
837 * The `pthread_setschedparam` function sets the scheduling policy and scheduling parameters (such as priority)
838 * for the specified thread. This allows you to control how the thread is scheduled by the operating system.
839 * It is useful for adjusting thread behavior, especially for real-time or performance-sensitive applications.
840 *
841 * @param[in] thread
842 * The thread whose scheduling policy and parameters are to be set. This is a valid `pthread_t` identifier.
843 *
844 * @param[in] policy
845 * The scheduling policy to be set for the thread. This can be one of the following values:
846 * - `SCHED_FIFO`: First-in, first-out scheduling policy, where threads are scheduled based on their arrival time.
847 * - `SCHED_RR`: Round-robin scheduling policy, where each thread is allocated a fixed time slice and scheduled cyclically.
848 * - `SCHED_OTHER`: Default policy for non-realtime threads.
849 * - `SCHED_IDLE`: For threads intended to run only when no other threads are runnable.
850 * - `SCHED_BATCH`: For threads that should run with lower priority than interactive threads.
851 * - `SCHED_DEADLINE`: For real-time threads that have a specified deadline (if supported).
852 *
853 * @param[in] param
854 * A pointer to a `struct sched_param`, which contains the scheduling parameters, typically the thread's priority.
855 * The `sched_priority` field is the most commonly used parameter, and it controls the thread's priority within
856 * the specified scheduling policy.
857 *
858 * @return
859 * - `0` on success.
860 * - `EINVAL` if an invalid policy or parameter is provided.
861 * - `ESRCH` if the specified thread does not exist.
862 * - `EPERM` if the caller does not have permission to modify the thread's scheduling attributes.
863 *
864 * @note
865 * - The `sched_param` structure's `sched_priority` field specifies the priority of the thread. The priority
866 * range depends on the policy used. For example, for `SCHED_FIFO` and `SCHED_RR`, higher priority values
867 * correspond to higher priority threads, while for `SCHED_OTHER`, priorities are not as strictly enforced.
868 * - Changing a thread's scheduling parameters may affect its execution behavior, including how it competes with
869 * other threads for CPU time.
870 * - The system may not allow you to modify scheduling parameters for all threads, depending on system configuration
871 * and privileges.
872 *
873 * @see pthread_getschedparam
874 */
pthread_setschedparam(pthread_t thread,int policy,const struct sched_param * param)875 int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param)
876 {
877 _pthread_data_t *ptd;
878
879 ptd = _pthread_get_data(thread);
880 pthread_attr_setschedpolicy(&ptd->attr, policy);
881 pthread_attr_setschedparam(&ptd->attr, param);
882
883 return 0;
884 }
885 RTM_EXPORT(pthread_setschedparam);
886
887 /**
888 * @brief Sets the scheduling priority for a thread.
889 *
890 * The `pthread_setschedprio` function adjusts the priority of the specified thread while leaving its
891 * scheduling policy unchanged. This is useful for fine-tuning thread behavior in multithreaded applications.
892 *
893 * @param[in] thread
894 * The thread whose scheduling priority is to be changed. This must be a valid `pthread_t` identifier.
895 *
896 * @param[in] prio
897 * The new scheduling priority for the thread. The priority must fall within the valid range for the
898 * thread's current scheduling policy, as defined by `sched_get_priority_min` and `sched_get_priority_max`.
899 *
900 * @return
901 * - `0` on success.
902 * - `EINVAL` if the specified priority is invalid for the thread's current scheduling policy.
903 * - `ESRCH` if the specified thread does not exist.
904 * - `EPERM` if the calling process lacks the necessary privileges to set the thread's priority.
905 *
906 * @note
907 * - Changing a thread's priority may require elevated privileges (e.g., root) on certain systems, especially
908 * for real-time priorities.
909 * - The priority range and behavior depend on the thread's current scheduling policy. For example:
910 * - `SCHED_FIFO` and `SCHED_RR`: Priorities are used for strict scheduling.
911 * - `SCHED_OTHER`: Priorities may have minimal or no effect.
912 * - The behavior of this function is platform-dependent and may vary between different operating systems.
913 *
914 * @see pthread_setschedparam, pthread_getschedparam
915 */
pthread_setschedprio(pthread_t thread,int prio)916 int pthread_setschedprio(pthread_t thread, int prio)
917 {
918 _pthread_data_t *ptd;
919 struct sched_param param;
920
921 ptd = _pthread_get_data(thread);
922 param.sched_priority = prio;
923 pthread_attr_setschedparam(&ptd->attr, ¶m);
924
925 return 0;
926 }
927 RTM_EXPORT(pthread_setschedprio);
928
929 /**
930 * @brief Terminates the calling thread and optionally returns a value.
931 *
932 * The `pthread_exit` function terminates the calling thread. It can optionally provide an exit status that can be
933 * retrieved by other threads that join the calling thread using `pthread_join`. If the thread is detached, the
934 * exit status is ignored and the system automatically reclaims resources once the thread terminates.
935 *
936 * @param[in] value
937 * A pointer to a value that will be returned to any thread that calls `pthread_join` on this thread.
938 * If `NULL`, no value is returned.
939 *
940 * @note
941 * - This function does not terminate the process. It only terminates the calling thread.
942 * - If the calling thread is the main thread, `pthread_exit` allows other threads to continue execution.
943 * - If a thread terminates without calling `pthread_exit`, it returns control to the system when the thread's function ends.
944 *
945 * @see pthread_join, pthread_create
946 */
pthread_exit(void * value)947 void pthread_exit(void *value)
948 {
949 _pthread_data_t *ptd;
950 _pthread_cleanup_t *cleanup;
951 rt_thread_t tid;
952
953 if (rt_thread_self() == RT_NULL)
954 {
955 return;
956 }
957
958 /* get pthread data from pthread_data of thread */
959 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
960
961 rt_enter_critical();
962 /* disable cancel */
963 ptd->cancelstate = PTHREAD_CANCEL_DISABLE;
964 /* set return value */
965 ptd->return_value = value;
966 rt_exit_critical();
967
968 /*
969 * When use pthread_exit to exit.
970 * invoke pushed cleanup
971 */
972 while (ptd->cleanup != RT_NULL)
973 {
974 cleanup = ptd->cleanup;
975 ptd->cleanup = cleanup->next;
976
977 cleanup->cleanup_func(cleanup->parameter);
978 /* release this cleanup function */
979 rt_free(cleanup);
980 }
981
982 /* get the info aboult "tid" early */
983 tid = ptd->tid;
984
985 /* According to "detachstate" to whether or not to recycle resource immediately */
986 if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
987 {
988 /* set value */
989 rt_sem_release(ptd->joinable_sem);
990 }
991 else
992 {
993 /* release pthread resource */
994 _pthread_data_destroy(ptd);
995 }
996
997 /*
998 * second: detach thread.
999 * this thread will be removed from scheduler list
1000 * and because there is a cleanup function in the
1001 * thread (pthread_cleanup), it will move to defunct
1002 * thread list and wait for handling in idle thread.
1003 */
1004 rt_thread_detach(tid);
1005
1006 /* reschedule thread */
1007 rt_schedule();
1008 }
1009 RTM_EXPORT(pthread_exit);
1010
1011 /**
1012 * @brief Executes a routine once in a multithreaded environment.
1013 *
1014 * The `pthread_once` function ensures that the specified initialization routine is executed exactly once,
1015 * even if multiple threads attempt to execute it simultaneously. It is typically used for one-time
1016 * initialization tasks in a multithreaded program.
1017 *
1018 * @param[in] once_control
1019 * A pointer to a `pthread_once_t` control variable. The init_routine can only be excuted
1020 * when (*once_control) is zero.
1021 *
1022 * @param[in] init_routine
1023 * A pointer to the initialization routine to be executed. This routine takes no arguments and
1024 * returns no value. It is guaranteed to be executed exactly once.
1025 *
1026 * @return
1027 * - `0` on success.
1028 *
1029 * @note
1030 * - The `pthread_once` function is thread-safe and guarantees that the `init_routine` is called only once.
1031 * - The `once_control` variable must remain valid and should not be modified by the application after
1032 * initialization.
1033 * - If the initialization routine fails or encounters an error, it is the responsibility of the routine
1034 * to handle it appropriately.
1035 *
1036 * @see pthread_mutex_lock, pthread_mutex_unlock
1037 */
pthread_once(pthread_once_t * once_control,void (* init_routine)(void))1038 int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
1039 {
1040 RT_ASSERT(once_control != RT_NULL);
1041 RT_ASSERT(init_routine != RT_NULL);
1042
1043 rt_enter_critical();
1044 if (!(*once_control))
1045 {
1046 /* call routine once */
1047 *once_control = 1;
1048 rt_exit_critical();
1049
1050 init_routine();
1051 }
1052 rt_exit_critical();
1053
1054 return 0;
1055 }
1056 RTM_EXPORT(pthread_once);
1057
pthread_atfork(void (* prepare)(void),void (* parent)(void),void (* child)(void))1058 int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
1059 {
1060 return EOPNOTSUPP;
1061 }
1062 RTM_EXPORT(pthread_atfork);
1063
1064 /**
1065 * @brief Sends a signal to a specific thread.
1066 *
1067 * The `pthread_kill` function sends the specified signal to the target thread. This allows fine-grained
1068 * control over signal handling in multithreaded applications.
1069 *
1070 * @param[in] thread
1071 * The target thread to which the signal is sent. This is a valid `pthread_t` identifier.
1072 *
1073 * @param[in] sig
1074 * The signal to be sent. This can be any valid signal, such as those defined in `<signal.h>`. For example:
1075 * - `SIGTERM`: Request thread termination.
1076 * - `SIGUSR1` or `SIGUSR2`: User-defined signals.
1077 * - `0`: Used to check if the thread is still valid without sending a signal.
1078 *
1079 * @return
1080 * - `0` on success.
1081 * - `ESRCH` if the specified thread does not exist or is invalid.
1082 * - `EINVAL` if the signal number `sig` is invalid.
1083 *
1084 * @note
1085 * - The signal is delivered to the specified thread only if the thread has the appropriate signal handlers
1086 * set up. Unhandled signals might result in the default action for that signal.
1087 * - If `sig` is `0`, no signal is sent, but the function checks if the thread is valid and exists.
1088 * - Signal handling behavior is shared across threads in a process. For example, blocking or ignoring a signal
1089 * in one thread affects the entire process.
1090 *
1091 * @see pthread_sigmask, sigaction
1092 */
pthread_kill(pthread_t thread,int sig)1093 int pthread_kill(pthread_t thread, int sig)
1094 {
1095 #ifdef RT_USING_SIGNALS
1096 _pthread_data_t *ptd;
1097 int ret;
1098
1099 ptd = _pthread_get_data(thread);
1100 if (ptd)
1101 {
1102 ret = rt_thread_kill(ptd->tid, sig);
1103 if (ret == -RT_EINVAL)
1104 {
1105 return EINVAL;
1106 }
1107
1108 return ret;
1109 }
1110
1111 return ESRCH;
1112 #else
1113 return ENOSYS;
1114 #endif
1115 }
1116 RTM_EXPORT(pthread_kill);
1117
1118 #ifdef RT_USING_SIGNALS
1119 /**
1120 * @brief Modifies or retrieves the signal mask of the calling thread.
1121 *
1122 * The `pthread_sigmask` function allows a thread to block, unblock, or examine the signals in its signal mask.
1123 * Signals that are blocked are not delivered to the thread until they are unblocked.
1124 *
1125 * @param[in] how
1126 * Specifies how the signal mask is modified. Possible values:
1127 * - `SIG_BLOCK`: Add the signals in `set` to the current signal mask.
1128 * - `SIG_UNBLOCK`: Remove the signals in `set` from the current signal mask.
1129 * - `SIG_SETMASK`: Replace the current signal mask with the signals in `set`.
1130 *
1131 * @param[in] set
1132 * A pointer to a `sigset_t` containing the signals to be modified in the mask. Can be `NULL` if no change is needed.
1133 *
1134 * @param[out] oset
1135 * A pointer to a `sigset_t` where the previous signal mask will be stored. Can be `NULL` if the previous mask is not required.
1136 *
1137 * @return
1138 * - `0` on success.
1139 *
1140 * @note
1141 * - Signal masks are thread-specific in a multithreaded program.
1142 * - The `pthread_sigmask` function is designed for multithreaded programs, whereas `sigprocmask` should not be used.
1143 * - Blocking a signal prevents it from being delivered to the thread until unblocked.
1144 *
1145 * @see sigprocmask, sigaction, pthread_kill
1146 */
pthread_sigmask(int how,const sigset_t * set,sigset_t * oset)1147 int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
1148 {
1149 return sigprocmask(how, set, oset);
1150 }
1151 #endif
1152
1153 /**
1154 * @brief Unregisters a cleanup handler and optionally executes it.
1155 *
1156 * The `pthread_cleanup_pop` function unregisters a cleanup handler that was previously registered
1157 * using `pthread_cleanup_push`. If the `execute` parameter is non-zero, the cleanup handler is executed
1158 * at the point where the thread terminates or is canceled.
1159 *
1160 * If `execute` is zero, the handler is unregistered without being executed. This allows the handler
1161 * to be removed from the cleanup stack without performing any actions.
1162 *
1163 * @param[in] execute
1164 * If non-zero, the cleanup handler is executed when the thread terminates or is canceled.
1165 * If zero, the handler is simply removed from the stack without executing it.
1166 *
1167 * @note
1168 * - Cleanup handlers are executed in the reverse order of their registration (i.e., last-in, first-out).
1169 * - It is important to use `pthread_cleanup_push` to register cleanup handlers and `pthread_cleanup_pop`
1170 * to ensure they are properly unregistered and executed if needed.
1171 * - This function should be paired with `pthread_cleanup_push` to manage cleanup handlers effectively.
1172 *
1173 * @see pthread_cleanup_push, pthread_exit, pthread_cancel
1174 */
pthread_cleanup_pop(int execute)1175 void pthread_cleanup_pop(int execute)
1176 {
1177 _pthread_data_t *ptd;
1178 _pthread_cleanup_t *cleanup;
1179
1180 if (rt_thread_self() == NULL) return;
1181
1182 /* get pthread data from pthread_data of thread */
1183 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
1184 RT_ASSERT(ptd != RT_NULL);
1185
1186 if (execute)
1187 {
1188 rt_enter_critical();
1189 cleanup = ptd->cleanup;
1190 if (cleanup)
1191 ptd->cleanup = cleanup->next;
1192 rt_exit_critical();
1193
1194 if (cleanup)
1195 {
1196 cleanup->cleanup_func(cleanup->parameter);
1197
1198 rt_free(cleanup);
1199 }
1200 }
1201 }
1202 RTM_EXPORT(pthread_cleanup_pop);
1203
1204 /**
1205 * @brief Registers a cleanup handler to be executed when the calling thread terminates.
1206 *
1207 * The `pthread_cleanup_push` function registers a cleanup handler that is executed when the calling thread
1208 * is canceled or exits (either normally or via `pthread_exit`). The cleanup handler will be executed
1209 * in the reverse order of their registration.
1210 *
1211 * The cleanup handler can be used to release resources such as memory or file descriptors when the thread
1212 * is terminated, whether it terminates normally or is canceled.
1213 *
1214 * @param[in] routine
1215 * A pointer to the cleanup handler function. The function must have the following signature:
1216 * `void routine(void* arg);`. It is invoked when the thread terminates or is canceled.
1217 *
1218 * @param[in] arg
1219 * A pointer to the argument that will be passed to the cleanup handler (`routine`).
1220 * This allows the handler to perform actions with the passed argument.
1221 *
1222 * @note
1223 * - The cleanup handler is automatically invoked when a thread terminates or is canceled.
1224 * - The cleanup handlers are executed in the reverse order of their registration, similar to how
1225 * destructors are executed in a stack-based fashion.
1226 * - `pthread_cleanup_pop` must be called to unregister the cleanup handler. It ensures that the handler
1227 * is only invoked during the thread's termination process.
1228 *
1229 * @see pthread_cleanup_pop, pthread_cancel, pthread_exit
1230 */
pthread_cleanup_push(void (* routine)(void *),void * arg)1231 void pthread_cleanup_push(void (*routine)(void *), void *arg)
1232 {
1233 _pthread_data_t *ptd;
1234 _pthread_cleanup_t *cleanup;
1235
1236 if (rt_thread_self() == NULL) return;
1237
1238 /* get pthread data from pthread_data of thread */
1239 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
1240 RT_ASSERT(ptd != RT_NULL);
1241
1242 cleanup = (_pthread_cleanup_t *)rt_malloc(sizeof(_pthread_cleanup_t));
1243 if (cleanup != RT_NULL)
1244 {
1245 cleanup->cleanup_func = routine;
1246 cleanup->parameter = arg;
1247
1248 rt_enter_critical();
1249 cleanup->next = ptd->cleanup;
1250 ptd->cleanup = cleanup;
1251 rt_exit_critical();
1252 }
1253 }
1254 RTM_EXPORT(pthread_cleanup_push);
1255
1256 /*
1257 * According to IEEE Std 1003.1, 2004 Edition , following pthreads
1258 * interface support cancellation point:
1259 * mq_receive()
1260 * mq_send()
1261 * mq_timedreceive()
1262 * mq_timedsend()
1263 * msgrcv()
1264 * msgsnd()
1265 * msync()
1266 * pthread_cond_timedwait()
1267 * pthread_cond_wait()
1268 * pthread_join()
1269 * pthread_testcancel()
1270 * sem_timedwait()
1271 * sem_wait()
1272 *
1273 * A cancellation point may also occur when a thread is
1274 * executing the following functions:
1275 * pthread_rwlock_rdlock()
1276 * pthread_rwlock_timedrdlock()
1277 * pthread_rwlock_timedwrlock()
1278 * pthread_rwlock_wrlock()
1279 *
1280 * The pthread_cancel(), pthread_setcancelstate(), and pthread_setcanceltype()
1281 * functions are defined to be async-cancel safe.
1282 */
1283
1284 /**
1285 * @brief Sets the cancelability state of the calling thread.
1286 *
1287 * The `pthread_setcancelstate` function allows a thread to enable or disable its ability to be canceled
1288 * by another thread. Cancelability determines if and when a thread responds to a cancellation request.
1289 *
1290 * @param[in] state
1291 * The new cancelability state for the calling thread. Possible values:
1292 * - `PTHREAD_CANCEL_ENABLE`: The thread can be canceled.
1293 * - `PTHREAD_CANCEL_DISABLE`: The thread cannot be canceled.
1294 *
1295 * @param[out] oldstate
1296 * A pointer to an integer where the previous cancelability state will be stored. Can be `NULL` if
1297 * the previous state is not needed.
1298 *
1299 * @return
1300 * - `0` on success.
1301 * - `EINVAL` if the `state` is not a valid cancelability state.
1302 *
1303 * @note
1304 * - The cancelability state affects how the thread responds to cancellation requests:
1305 * - In the `PTHREAD_CANCEL_DISABLE` state, cancellation requests are held pending until the state is changed to `PTHREAD_CANCEL_ENABLE`.
1306 * - Cancelability is distinct from the cancelability type, which controls the timing of cancellation (deferred or asynchronous).
1307 * - By default, threads are created with `PTHREAD_CANCEL_ENABLE`.
1308 *
1309 * @see pthread_cancel, pthread_setcanceltype
1310 */
pthread_setcancelstate(int state,int * oldstate)1311 int pthread_setcancelstate(int state, int *oldstate)
1312 {
1313 _pthread_data_t *ptd;
1314
1315 if (rt_thread_self() == NULL) return EINVAL;
1316
1317 /* get pthread data from pthread_data of thread */
1318 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
1319 RT_ASSERT(ptd != RT_NULL);
1320
1321 if ((state == PTHREAD_CANCEL_ENABLE) || (state == PTHREAD_CANCEL_DISABLE))
1322 {
1323 if (oldstate)
1324 *oldstate = ptd->cancelstate;
1325 ptd->cancelstate = state;
1326
1327 return 0;
1328 }
1329
1330 return EINVAL;
1331 }
1332 RTM_EXPORT(pthread_setcancelstate);
1333
1334 /**
1335 * @brief Sets the cancellation type of the calling thread.
1336 *
1337 * The `pthread_setcanceltype` function allows a thread to specify when it should respond to
1338 * a cancellation request. The cancellation type can be set to deferred or asynchronous.
1339 *
1340 * @param[in] type
1341 * The new cancellation type for the calling thread. Possible values:
1342 * - `PTHREAD_CANCEL_DEFERRED`: Cancellation occurs at cancellation points (default behavior).
1343 * - `PTHREAD_CANCEL_ASYNCHRONOUS`: Cancellation occurs immediately when a request is received.
1344 *
1345 * @param[out] oldtype
1346 * A pointer to an integer where the previous cancellation type will be stored. Can be `NULL`
1347 * if the previous type is not required.
1348 *
1349 * @return
1350 * - `0` on success.
1351 * - `EINVAL` if the `type` is not a valid cancellation type.
1352 *
1353 * @note
1354 * - The cancellation type determines when a thread processes a cancellation request:
1355 * - **Deferred**: The thread responds to cancellation only at well-defined cancellation points.
1356 * - **Asynchronous**: The thread can be canceled immediately, which may lead to resource inconsistencies.
1357 * - By default, threads use `PTHREAD_CANCEL_DEFERRED`.
1358 * - Asynchronous cancellation should be used cautiously as it can interrupt a thread at any point.
1359 *
1360 * @see pthread_cancel, pthread_setcancelstate, pthread_testcancel
1361 */
pthread_setcanceltype(int type,int * oldtype)1362 int pthread_setcanceltype(int type, int *oldtype)
1363 {
1364 _pthread_data_t *ptd;
1365
1366 if (rt_thread_self() == NULL) return EINVAL;
1367
1368 /* get pthread data from pthread_data of thread */
1369 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
1370 RT_ASSERT(ptd != RT_NULL);
1371
1372 if ((type != PTHREAD_CANCEL_DEFERRED) && (type != PTHREAD_CANCEL_ASYNCHRONOUS))
1373 return EINVAL;
1374
1375 if (oldtype)
1376 *oldtype = ptd->canceltype;
1377 ptd->canceltype = type;
1378
1379 return 0;
1380 }
1381 RTM_EXPORT(pthread_setcanceltype);
1382
1383 /**
1384 * @brief Explicitly checks for pending cancellation requests in the calling thread.
1385 *
1386 * The `pthread_testcancel` function allows a thread to determine if it has a pending
1387 * cancellation request. If a cancellation request is pending and the thread's cancelability
1388 * state is set to `PTHREAD_CANCEL_ENABLE`, the thread will terminate immediately.
1389 *
1390 * @note
1391 * - This function is a cancellation point, meaning it checks for cancellation and responds if applicable.
1392 * - If the thread's cancelability state is `PTHREAD_CANCEL_DISABLE`, the function has no effect.
1393 * - The thread will invoke any cleanup handlers registered with `pthread_cleanup_push` before termination.
1394 *
1395 * @return
1396 * This function does not return if a cancellation is performed. Otherwise, it returns normally.
1397 *
1398 * @see pthread_setcancelstate, pthread_setcanceltype, pthread_cancel
1399 */
pthread_testcancel(void)1400 void pthread_testcancel(void)
1401 {
1402 int cancel = 0;
1403 _pthread_data_t *ptd;
1404
1405 if (rt_thread_self() == NULL) return;
1406
1407 /* get pthread data from pthread_data of thread */
1408 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
1409 RT_ASSERT(ptd != RT_NULL);
1410
1411 if (ptd->cancelstate == PTHREAD_CANCEL_ENABLE)
1412 cancel = ptd->canceled;
1413 if (cancel)
1414 pthread_exit((void *)PTHREAD_CANCELED);
1415 }
1416 RTM_EXPORT(pthread_testcancel);
1417
1418 /**
1419 * @brief Sends a cancellation request to a specified thread.
1420 *
1421 * The `pthread_cancel` function requests the cancellation of the thread identified by `thread`.
1422 * The actual response to the request depends on the target thread's cancelability state and type.
1423 *
1424 * @param[in] thread
1425 * The identifier of the thread to be canceled.
1426 *
1427 * @return
1428 * - `0` on success.
1429 * - `EINVAL` if the specified thread does not exist.
1430 *
1431 * @note
1432 * - Cancellation is an asynchronous mechanism. The thread may not terminate immediately or at all
1433 * if its cancelability state is set to `PTHREAD_CANCEL_DISABLE`.
1434 * - If the thread is cancelable and terminates, it invokes cleanup handlers registered with
1435 * `pthread_cleanup_push` before termination.
1436 * - The thread's cancellation type determines when it processes the cancellation request:
1437 * - `PTHREAD_CANCEL_DEFERRED` (default): At specific cancellation points.
1438 * - `PTHREAD_CANCEL_ASYNCHRONOUS`: Immediately upon receipt of the request.
1439 *
1440 * @see pthread_setcancelstate, pthread_setcanceltype, pthread_testcancel
1441 */
pthread_cancel(pthread_t thread)1442 int pthread_cancel(pthread_t thread)
1443 {
1444 _pthread_data_t *ptd;
1445 _pthread_cleanup_t *cleanup;
1446 rt_thread_t tid;
1447
1448 /* get posix thread data */
1449 ptd = _pthread_get_data(thread);
1450 if (ptd == RT_NULL)
1451 {
1452 return EINVAL;
1453 }
1454 tid = ptd->tid;
1455
1456 /* cancel self */
1457 if (ptd->tid == rt_thread_self())
1458 return 0;
1459
1460 /* set canceled */
1461 if (ptd->cancelstate == PTHREAD_CANCEL_ENABLE)
1462 {
1463 ptd->canceled = 1;
1464 if (ptd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
1465 {
1466 /*
1467 * When use pthread_cancel to exit.
1468 * invoke pushed cleanup
1469 */
1470 while (ptd->cleanup != RT_NULL)
1471 {
1472 cleanup = ptd->cleanup;
1473 ptd->cleanup = cleanup->next;
1474
1475 cleanup->cleanup_func(cleanup->parameter);
1476 /* release this cleanup function */
1477 rt_free(cleanup);
1478 }
1479
1480 /* According to "detachstate" to whether or not to recycle resource immediately */
1481 if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
1482 {
1483 /* set value */
1484 rt_sem_release(ptd->joinable_sem);
1485 }
1486 else
1487 {
1488 /* release pthread resource */
1489 _pthread_data_destroy(ptd);
1490 }
1491
1492 /*
1493 * second: detach thread.
1494 * this thread will be removed from scheduler list
1495 * and because there is a cleanup function in the
1496 * thread (pthread_cleanup), it will move to defunct
1497 * thread list and wait for handling in idle thread.
1498 */
1499 rt_thread_detach(tid);
1500 }
1501 }
1502
1503 return 0;
1504 }
1505 RTM_EXPORT(pthread_cancel);
1506
1507