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, &param);
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