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  * 2010-10-26     Bernard      the first version
9  */
10 
11 #include <rtthread.h>
12 #include "pthread.h"
13 
14 #define  MUTEXATTR_SHARED_MASK 0x0010
15 #define  MUTEXATTR_TYPE_MASK   0x000f
16 
17 const pthread_mutexattr_t pthread_default_mutexattr = PTHREAD_PROCESS_PRIVATE;
18 
19 /**
20  * @brief Initializes a mutex attributes object.
21  *
22  * This function initializes a mutex attributes object pointed to by `attr` with
23  * default attribute values. Once initialized, the attributes object can be used
24  * to customize the behavior of mutexes created using it.
25  *
26  * @param[out] attr Pointer to the mutex attributes object to be initialized.
27  *
28  * @return
29  * - 0 on success.
30  * - Non-zero error code on failure.
31  *
32  * @note
33  * After initialization, the mutex attributes object must be destroyed with
34  * `pthread_mutexattr_destroy()` when it is no longer needed.
35  *
36  * @warning
37  * Using an uninitialized mutex attributes object may result in undefined behavior.
38  *
39  * @see pthread_mutexattr_destroy, pthread_mutex_init
40  */
pthread_mutexattr_init(pthread_mutexattr_t * attr)41 int pthread_mutexattr_init(pthread_mutexattr_t *attr)
42 {
43     if (attr)
44     {
45         *attr = pthread_default_mutexattr;
46 
47         return 0;
48     }
49 
50     return EINVAL;
51 }
52 RTM_EXPORT(pthread_mutexattr_init);
53 
54 /**
55  * @brief Destroys a mutex attributes object.
56  *
57  * This function releases any resources associated with the mutex attributes object
58  * pointed to by `attr`. After the attributes object is destroyed, it should not
59  * be used unless it is re-initialized with `pthread_mutexattr_init()`.
60  *
61  * @param[in,out] attr Pointer to the mutex attributes object to be destroyed.
62  *
63  * @return
64  * - 0 on success.
65  * - Non-zero error code on failure, including:
66  *   - `EINVAL`: The attributes object is invalid or uninitialized.
67  *
68  * @note
69  * Destroying an uninitialized or already destroyed attributes object results in undefined behavior.
70  *
71  * @warning
72  * Ensure that no mutexes are being initialized or created using this attributes object
73  * at the time of its destruction.
74  *
75  * @see pthread_mutexattr_init, pthread_mutex_init
76  */
pthread_mutexattr_destroy(pthread_mutexattr_t * attr)77 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
78 {
79     if (attr)
80     {
81         *attr = -1;
82 
83         return 0;
84     }
85 
86     return EINVAL;
87 }
88 RTM_EXPORT(pthread_mutexattr_destroy);
89 
90 /**
91  * @brief Retrieves the type attribute of a mutex attributes object.
92  *
93  * This function retrieves the mutex type attribute from the attributes object
94  * pointed to by `attr` and stores it in the integer pointed to by `type`.
95  *
96  * @param[in] attr Pointer to the mutex attributes object.
97  * @param[out] type Pointer to an integer where the mutex type will be stored.
98  *                  Possible values include:
99  *                  - `PTHREAD_MUTEX_NORMAL`: Default mutex type.
100  *                  - `PTHREAD_MUTEX_ERRORCHECK`: Mutex with error-checking.
101  *                  - `PTHREAD_MUTEX_RECURSIVE`: Recursive mutex.
102  *
103  * @return
104  * - 0 on success.
105  * - Non-zero error code on failure, including:
106  *   - `EINVAL`: The attributes object or the `type` pointer is invalid.
107  *
108  * @note
109  * Use this function to check the type of a mutex attributes object that has
110  * already been initialized or configured.
111  *
112  * @see pthread_mutexattr_settype, pthread_mutexattr_init
113  */
pthread_mutexattr_gettype(const pthread_mutexattr_t * attr,int * type)114 int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
115 {
116     if (attr && type)
117     {
118         int  atype = (*attr & MUTEXATTR_TYPE_MASK);
119 
120         if (atype >= PTHREAD_MUTEX_NORMAL && atype <= PTHREAD_MUTEX_ERRORCHECK)
121         {
122             *type = atype;
123 
124             return 0;
125         }
126     }
127 
128     return EINVAL;
129 }
130 RTM_EXPORT(pthread_mutexattr_gettype);
131 
132 /**
133  * @brief Sets the type attribute of a mutex attributes object.
134  *
135  * This function sets the type of the mutex to be initialized using the
136  * attributes object pointed to by `attr`. The `type` can be one of the
137  * following values:
138  * - `PTHREAD_MUTEX_NORMAL`: Default mutex type. The mutex does not allow
139  *   a thread to unlock it if it was not locked by that thread (this results
140  *   in undefined behavior).
141  * - `PTHREAD_MUTEX_ERRORCHECK`: Error-checking mutex type. A thread trying to
142  *   lock a mutex it already holds will return an error.
143  * - `PTHREAD_MUTEX_RECURSIVE`: Recursive mutex type. The same thread can lock
144  *   the mutex multiple times without causing a deadlock, but it must unlock it
145  *   the same number of times.
146  *
147  * @param[in,out] attr Pointer to the mutex attributes object.
148  * @param[in] type The type to set for the mutex. One of the following:
149  *                 - `PTHREAD_MUTEX_NORMAL`
150  *                 - `PTHREAD_MUTEX_ERRORCHECK`
151  *                 - `PTHREAD_MUTEX_RECURSIVE`
152  *
153  * @return
154  * - 0 on success.
155  * - Non-zero error code on failure, including:
156  *   - `EINVAL`: The specified type is invalid.
157  *
158  * @note
159  * The type must be set before the mutex attributes object is used to
160  * initialize a mutex with `pthread_mutex_init()`.
161  *
162  * @warning
163  * Attempting to set an invalid mutex type will result in an error.
164  *
165  * @see pthread_mutexattr_gettype, pthread_mutexattr_init, pthread_mutex_init
166  */
pthread_mutexattr_settype(pthread_mutexattr_t * attr,int type)167 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
168 {
169     if (attr && type >= PTHREAD_MUTEX_NORMAL && type <= PTHREAD_MUTEX_ERRORCHECK)
170     {
171         *attr = (*attr & ~MUTEXATTR_TYPE_MASK) | type;
172 
173         return 0;
174     }
175 
176     return EINVAL;
177 }
178 RTM_EXPORT(pthread_mutexattr_settype);
179 
180 /**
181  * @brief Sets the shared attribute of a mutex attributes object.
182  *
183  * This function sets the `pshared` attribute of the mutex attributes object
184  * pointed to by `attr`. The `pshared` attribute determines whether the mutex
185  * is shared between threads of the same process or can be shared between
186  * threads of different processes.
187  *
188  * @param[in,out] attr Pointer to the mutex attributes object.
189  * @param[in] pshared The sharing behavior of the mutex. This can be one of the following:
190  *                   - `PTHREAD_PROCESS_PRIVATE`: The mutex is only shared between threads
191  *                     of the same process (this is the default behavior).
192  *                   - `PTHREAD_PROCESS_SHARED`: The mutex can be shared between threads
193  *                     of different processes (requires the mutex to be allocated in
194  *                     shared memory).
195  *
196  * @return
197  * - 0 on success.
198  * - Non-zero error code on failure, including:
199  *   - `EINVAL`: Invalid `pshared` value or invalid attributes object.
200  *
201  * @note
202  * The `pshared` attribute must be set before the mutex attributes object is
203  * used to initialize a mutex with `pthread_mutex_init()`. For shared mutexes
204  * (`PTHREAD_PROCESS_SHARED`), the mutex object must be allocated in shared memory.
205  *
206  * @warning
207  * Attempting to set an invalid `pshared` value will result in an error.
208  *
209  * @see pthread_mutexattr_getpshared, pthread_mutexattr_init, pthread_mutex_init
210  */
pthread_mutexattr_setpshared(pthread_mutexattr_t * attr,int pshared)211 int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared)
212 {
213     if (!attr)
214         return EINVAL;
215 
216     switch (pshared)
217     {
218     case PTHREAD_PROCESS_PRIVATE:
219         *attr &= ~MUTEXATTR_SHARED_MASK;
220         return 0;
221 
222     case PTHREAD_PROCESS_SHARED:
223         *attr |= MUTEXATTR_SHARED_MASK;
224         return 0;
225     }
226 
227     return EINVAL;
228 }
229 RTM_EXPORT(pthread_mutexattr_setpshared);
230 
231 /**
232  * @brief Retrieves the shared attribute of a mutex attributes object.
233  *
234  * This function retrieves the `pshared` attribute from the mutex attributes
235  * object pointed to by `attr` and stores it in the integer pointed to by `pshared`.
236  * The `pshared` attribute indicates whether the mutex can be shared between threads
237  * of different processes or only within the same process.
238  *
239  * @param[in] attr Pointer to the mutex attributes object.
240  * @param[out] pshared Pointer to an integer where the shared attribute will be stored.
241  *                    Possible values are:
242  *                    - `PTHREAD_PROCESS_PRIVATE`: Mutex is shared only within the same process.
243  *                    - `PTHREAD_PROCESS_SHARED`: Mutex can be shared between threads of different processes.
244  *
245  * @return
246  * - 0 on success.
247  * - Non-zero error code on failure, including:
248  *   - `EINVAL`: Invalid attributes object or the `pshared` pointer is NULL.
249  *
250  * @note
251  * Use this function to check the shared attribute of an already initialized
252  * mutex attributes object.
253  *
254  * @warning
255  * Attempting to get the `pshared` attribute of an uninitialized or invalid
256  * attributes object will result in an error.
257  *
258  * @see pthread_mutexattr_setpshared, pthread_mutexattr_init, pthread_mutex_init
259  */
pthread_mutexattr_getpshared(pthread_mutexattr_t * attr,int * pshared)260 int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared)
261 {
262     if (!attr || !pshared)
263         return EINVAL;
264 
265     *pshared = (*attr & MUTEXATTR_SHARED_MASK) ? PTHREAD_PROCESS_SHARED
266                                                : PTHREAD_PROCESS_PRIVATE;
267     return 0;
268 }
269 RTM_EXPORT(pthread_mutexattr_getpshared);
270 
271 /**
272  * @brief Initializes a mutex with optional attributes.
273  *
274  * This function initializes a mutex object pointed to by `mutex`. The mutex
275  * can optionally be initialized with attributes specified by `attr`. If
276  * `attr` is `NULL`, default attributes are used.
277  *
278  * @param[in,out] mutex Pointer to the mutex to be initialized.
279  * @param[in] attr Pointer to the mutex attributes object. Pass `NULL` to use
280  *                 default attributes.
281  *
282  * @return
283  * - 0 on success.
284  * - Non-zero error code on failure, including:
285  *   - `EINVAL`: Invalid parameters or result.
286  *
287  * @note
288  * The mutex object must be destroyed using `pthread_mutex_destroy()` after it
289  * is no longer needed to free associated resources.
290  *
291  * @warning
292  * A mutex should not be re-initialized while it is already in use.
293  *
294  * @see pthread_mutex_destroy, pthread_mutex_lock, pthread_mutex_unlock
295  */
pthread_mutex_init(pthread_mutex_t * mutex,const pthread_mutexattr_t * attr)296 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
297 {
298     rt_err_t result;
299     char name[RT_NAME_MAX];
300     static rt_uint16_t pthread_mutex_number = 0;
301 
302     if (!mutex)
303         return EINVAL;
304 
305     /* build mutex name */
306     rt_snprintf(name, sizeof(name), "pmtx%02d", pthread_mutex_number ++);
307     if (attr == RT_NULL)
308         mutex->attr = pthread_default_mutexattr;
309     else
310         mutex->attr = *attr;
311 
312     /* init mutex lock */
313     result = rt_mutex_init(&(mutex->lock), name, RT_IPC_FLAG_PRIO);
314     if (result != RT_EOK)
315         return EINVAL;
316 
317     /* detach the object from system object container */
318     rt_object_detach(&(mutex->lock.parent.parent));
319     mutex->lock.parent.parent.type = RT_Object_Class_Mutex;
320 
321     return 0;
322 }
323 RTM_EXPORT(pthread_mutex_init);
324 
325 /**
326  * @brief Destroys a mutex object.
327  *
328  * This function releases any resources associated with the mutex object
329  * pointed to by `mutex`. After the mutex has been destroyed, it cannot
330  * be used unless it is re-initialized with `pthread_mutex_init()`.
331  *
332  * @param[in,out] mutex Pointer to the mutex to be destroyed.
333  *
334  * @return
335  * - 0 on success.
336  * - Non-zero error code on failure, including:
337  *   - `EBUSY`: The mutex is currently locked or being used by another thread.
338  *   - `EINVAL`: The mutex is invalid or has not been initialized.
339  *
340  * @note
341  * Before calling this function, ensure that the mutex is not locked or in use
342  * by any thread. Destroying a locked mutex results in undefined behavior.
343  *
344  * @warning
345  * Attempting to destroy a mutex that is still in use can cause resource leaks
346  * or undefined behavior.
347  *
348  * @see pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock
349  */
pthread_mutex_destroy(pthread_mutex_t * mutex)350 int pthread_mutex_destroy(pthread_mutex_t *mutex)
351 {
352     if (!mutex || mutex->attr == -1)
353         return EINVAL;
354 
355     /* it's busy */
356     if (mutex->lock.owner != RT_NULL)
357         return EBUSY;
358 
359     rt_memset(mutex, 0, sizeof(pthread_mutex_t));
360     mutex->attr = -1;
361 
362     return 0;
363 }
364 RTM_EXPORT(pthread_mutex_destroy);
365 
366 /**
367  * @brief Locks a mutex.
368  *
369  * This function locks the mutex object pointed to by `mutex`. If the mutex is
370  * already locked by another thread, the calling thread will block until the
371  * mutex becomes available.
372  *
373  * @param[in,out] mutex Pointer to the mutex to be locked.
374  *
375  * @return
376  * - 0 on success.
377  * - Non-zero error code on failure, including:
378  *   - `EDEADLK`: A deadlock condition was detected (e.g., the current thread
379  *     already holds the mutex in a recursive locking scenario).
380  *   - `EINVAL`: The mutex is invalid or uninitialized.
381  *
382  * @note
383  * If the mutex is initialized with the `PTHREAD_MUTEX_RECURSIVE` attribute,
384  * the same thread can lock the mutex multiple times without causing a deadlock.
385  * However, the mutex must be unlocked an equal number of times before it
386  * becomes available to other threads.
387  *
388  * @warning
389  * Attempting to lock an uninitialized or already destroyed mutex results in
390  * undefined behavior.
391  *
392  * @see pthread_mutex_unlock, pthread_mutex_trylock, pthread_mutex_init
393  */
pthread_mutex_lock(pthread_mutex_t * mutex)394 int pthread_mutex_lock(pthread_mutex_t *mutex)
395 {
396     int mtype;
397     rt_err_t result;
398 
399     if (!mutex)
400         return EINVAL;
401 
402     if (mutex->attr == -1)
403     {
404         /* init mutex */
405         pthread_mutex_init(mutex, RT_NULL);
406     }
407 
408     mtype = mutex->attr & MUTEXATTR_TYPE_MASK;
409     rt_enter_critical();
410     if (mutex->lock.owner == rt_thread_self() &&
411         mtype != PTHREAD_MUTEX_RECURSIVE)
412     {
413         rt_exit_critical();
414 
415         return EDEADLK;
416     }
417     rt_exit_critical();
418 
419     result = rt_mutex_take(&(mutex->lock), RT_WAITING_FOREVER);
420     if (result == RT_EOK)
421         return 0;
422 
423     return EINVAL;
424 }
425 RTM_EXPORT(pthread_mutex_lock);
426 
427 /**
428  * @brief Unlocks a mutex.
429  *
430  * This function unlocks the mutex object pointed to by `mutex`. If other threads
431  * are blocked waiting for the mutex, one of them will acquire the lock once it is
432  * released. The calling thread must hold the lock on the mutex before calling
433  * this function.
434  *
435  * @param[in,out] mutex Pointer to the mutex to be unlocked.
436  *
437  * @return
438  * - 0 on success.
439  * - Non-zero error code on failure, including:
440  *   - `EPERM`: The current thread does not hold the lock on the mutex.
441  *   - `EINVAL`: The mutex is invalid or uninitialized.
442  *
443  * @note
444  * If the mutex was initialized with the `PTHREAD_MUTEX_RECURSIVE` attribute,
445  * the mutex will only be unlocked after the calling thread unlocks it as many
446  * times as it was locked.
447  *
448  * @warning
449  * Attempting to unlock an uninitialized, destroyed, or unlocked mutex results
450  * in undefined behavior.
451  *
452  * @see pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_init
453  */
pthread_mutex_unlock(pthread_mutex_t * mutex)454 int pthread_mutex_unlock(pthread_mutex_t *mutex)
455 {
456     rt_err_t result;
457 
458     if (!mutex)
459         return EINVAL;
460     if (mutex->attr == -1)
461     {
462         /* init mutex */
463         pthread_mutex_init(mutex, RT_NULL);
464     }
465 
466     if (mutex->lock.owner != rt_thread_self())
467     {
468         int mtype;
469         mtype = mutex->attr & MUTEXATTR_TYPE_MASK;
470 
471         /* error check, return EPERM */
472         if (mtype == PTHREAD_MUTEX_ERRORCHECK)
473             return EPERM;
474 
475         /* no thread waiting on this mutex */
476         if (mutex->lock.owner == RT_NULL)
477             return 0;
478     }
479 
480     result = rt_mutex_release(&(mutex->lock));
481     if (result == RT_EOK)
482         return 0;
483 
484     return EINVAL;
485 }
486 RTM_EXPORT(pthread_mutex_unlock);
487 
488 /**
489  * @brief Attempts to lock a mutex without blocking.
490  *
491  * This function attempts to lock the mutex object pointed to by `mutex`. If the mutex
492  * is already locked by another thread, the function returns immediately with an error
493  * code instead of blocking.
494  *
495  * @param[in,out] mutex Pointer to the mutex to be locked.
496  *
497  * @return
498  * - 0 on success (the mutex was successfully locked).
499  * - Non-zero error code on failure, including:
500  *   - `EBUSY`: The mutex is already locked by another thread.
501  *   - `EINVAL`: The mutex is invalid or uninitialized.
502  *
503  * @note
504  * This function is useful for implementing non-blocking mutex acquisition. If the mutex
505  * was initialized with the `PTHREAD_MUTEX_RECURSIVE` attribute, the calling thread can
506  * lock it multiple times, but must unlock it the same number of times.
507  *
508  * @warning
509  * Attempting to trylock an uninitialized or destroyed mutex results in undefined behavior.
510  *
511  * @see pthread_mutex_lock, pthread_mutex_unlock, pthread_mutex_init
512  */
pthread_mutex_trylock(pthread_mutex_t * mutex)513 int pthread_mutex_trylock(pthread_mutex_t *mutex)
514 {
515     rt_err_t result;
516     int mtype;
517 
518     if (!mutex)
519         return EINVAL;
520     if (mutex->attr == -1)
521     {
522         /* init mutex */
523         pthread_mutex_init(mutex, RT_NULL);
524     }
525 
526     mtype = mutex->attr & MUTEXATTR_TYPE_MASK;
527     rt_enter_critical();
528     if (mutex->lock.owner == rt_thread_self() &&
529         mtype != PTHREAD_MUTEX_RECURSIVE)
530     {
531         rt_exit_critical();
532 
533         return EDEADLK;
534     }
535     rt_exit_critical();
536 
537     result = rt_mutex_take(&(mutex->lock), 0);
538     if (result == RT_EOK) return 0;
539 
540     return EBUSY;
541 }
542 RTM_EXPORT(pthread_mutex_trylock);
543 
pthread_mutexattr_getprioceiling(const pthread_mutexattr_t * attr,int * prioceiling)544 int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, int *prioceiling)
545 {
546     return EINVAL;
547 }
548 RTM_EXPORT(pthread_mutexattr_getprioceiling);
549 
pthread_mutexattr_setprioceiling(const pthread_mutexattr_t * attr,int prioceiling)550 int pthread_mutexattr_setprioceiling(const pthread_mutexattr_t *attr, int prioceiling)
551 {
552     return EINVAL;
553 }
554 RTM_EXPORT(pthread_mutexattr_setprioceiling);
555 
pthread_mutexattr_getprotocol(const pthread_mutexattr_t * attr,int * protocol)556 int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol)
557 {
558     return EINVAL;
559 }
560 RTM_EXPORT(pthread_mutexattr_getprotocol);
561 
pthread_mutexattr_setprotocol(const pthread_mutexattr_t * attr,int protocol)562 int pthread_mutexattr_setprotocol(const pthread_mutexattr_t *attr, int protocol)
563 {
564     return EINVAL;
565 }
566 RTM_EXPORT(pthread_mutexattr_setprotocol);
567 
pthread_mutex_getprioceiling(const pthread_mutex_t * mutex,int * prioceiling)568 int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling)
569 {
570     return pthread_mutexattr_getprioceiling(&mutex->attr, prioceiling);
571 }
572 RTM_EXPORT(pthread_mutex_getprioceiling);
573 
pthread_mutex_setprioceiling(pthread_mutex_t * mutex,int prioceiling,int * old_ceiling)574 int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, int *old_ceiling)
575 {
576     *old_ceiling = pthread_mutexattr_getprioceiling(&mutex->attr, old_ceiling);
577     if(*old_ceiling != 0)
578     {
579         return EINVAL;
580     }
581 
582     return pthread_mutexattr_setprioceiling(&mutex->attr, prioceiling);
583 }
584 RTM_EXPORT(pthread_mutex_setprioceiling);
585