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