ChibiOS/RT 7.0.6
chmtx.c
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
3
4 This file is part of ChibiOS.
5
6 ChibiOS is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation version 3 of the License.
9
10 ChibiOS is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file rt/src/chmtx.c
21 * @brief Mutexes code.
22 *
23 * @addtogroup mutexes
24 * @details Mutexes related APIs and services.
25 * <h2>Operation mode</h2>
26 * A mutex is a threads synchronization object that can be in two
27 * distinct states:
28 * - Not owned (unlocked).
29 * - Owned by a thread (locked).
30 * .
31 * Operations defined for mutexes:
32 * - <b>Lock</b>: The mutex is checked, if the mutex is not owned by
33 * some other thread then it is associated to the locking thread
34 * else the thread is queued on the mutex in a list ordered by
35 * priority.
36 * - <b>Unlock</b>: The mutex is released by the owner and the highest
37 * priority thread waiting in the queue, if any, is resumed and made
38 * owner of the mutex.
39 * .
40 * <h2>Constraints</h2>
41 * In ChibiOS/RT the Unlock operations must always be performed
42 * in lock-reverse order. This restriction both improves the
43 * performance and is required for an efficient implementation
44 * of the priority inheritance mechanism.<br>
45 * Operating under this restriction also ensures that deadlocks
46 * are no possible.
47 *
48 * <h2>Recursive mode</h2>
49 * By default mutexes are not recursive, this mean that it is not
50 * possible to take a mutex already owned by the same thread.
51 * It is possible to enable the recursive behavior by enabling the
52 * option @p CH_CFG_USE_MUTEXES_RECURSIVE.
53 *
54 * <h2>The priority inversion problem</h2>
55 * The mutexes in ChibiOS/RT implements the <b>full</b> priority
56 * inheritance mechanism in order handle the priority inversion
57 * problem.<br>
58 * When a thread is queued on a mutex, any thread, directly or
59 * indirectly, holding the mutex gains the same priority of the
60 * waiting thread (if their priority was not already equal or higher).
61 * The mechanism works with any number of nested mutexes and any
62 * number of involved threads. The algorithm complexity (worst case)
63 * is N with N equal to the number of nested mutexes.
64 * @pre In order to use the mutex APIs the @p CH_CFG_USE_MUTEXES option
65 * must be enabled in @p chconf.h.
66 * @post Enabling mutexes requires 5-12 (depending on the architecture)
67 * extra bytes in the @p thread_t structure.
68 * @{
69 */
70
71#include "ch.h"
72
73#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__)
74
75/*===========================================================================*/
76/* Module exported variables. */
77/*===========================================================================*/
78
79/*===========================================================================*/
80/* Module local types. */
81/*===========================================================================*/
82
83/*===========================================================================*/
84/* Module local variables. */
85/*===========================================================================*/
86
87/*===========================================================================*/
88/* Module local functions. */
89/*===========================================================================*/
90
91/*===========================================================================*/
92/* Module exported functions. */
93/*===========================================================================*/
94
95/**
96 * @brief Initializes s @p mutex_t structure.
97 *
98 * @param[out] mp pointer to a @p mutex_t structure
99 *
100 * @init
101 */
103
104 chDbgCheck(mp != NULL);
105
106 ch_queue_init(&mp->queue);
107 mp->owner = NULL;
108#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
109 mp->cnt = (cnt_t)0;
110#endif
111}
112
113/**
114 * @brief Locks the specified mutex.
115 * @post The mutex is locked and inserted in the per-thread stack of owned
116 * mutexes.
117 *
118 * @param[in] mp pointer to the @p mutex_t structure
119 *
120 * @api
121 */
123
124 chSysLock();
125 chMtxLockS(mp);
126 chSysUnlock();
127}
128
129/**
130 * @brief Locks the specified mutex.
131 * @post The mutex is locked and inserted in the per-thread stack of owned
132 * mutexes.
133 *
134 * @param[in] mp pointer to the @p mutex_t structure
135 *
136 * @sclass
137 */
139 thread_t *currtp = chThdGetSelfX();
140
142 chDbgCheck(mp != NULL);
143
144 /* Is the mutex already locked? */
145 if (mp->owner != NULL) {
146#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
147
148 chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive");
149
150 /* If the mutex is already owned by this thread, the counter is increased
151 and there is no need of more actions.*/
152 if (mp->owner == currtp) {
153 mp->cnt++;
154 }
155 else {
156#endif
157 /* Priority inheritance protocol; explores the thread-mutex dependencies
158 boosting the priority of all the affected threads to equal the
159 priority of the running thread requesting the mutex.*/
160 thread_t *tp = mp->owner;
161
162 /* Does the running thread have higher priority than the mutex
163 owning thread? */
164 while (tp->hdr.pqueue.prio < currtp->hdr.pqueue.prio) {
165 /* Make priority of thread tp match the running thread's priority.*/
166 tp->hdr.pqueue.prio = currtp->hdr.pqueue.prio;
167
168 /* The following states need priority queues reordering.*/
169 switch (tp->state) {
170 case CH_STATE_WTMTX:
171 /* Re-enqueues the mutex owner with its new priority.*/
174 tp = tp->u.wtmtxp->owner;
175 /*lint -e{9042} [16.1] Continues the while.*/
176 continue;
177#if (CH_CFG_USE_CONDVARS == TRUE) || \
178 ((CH_CFG_USE_SEMAPHORES == TRUE) && \
179 (CH_CFG_USE_SEMAPHORES_PRIORITY == TRUE))
180#if CH_CFG_USE_CONDVARS == TRUE
181 case CH_STATE_WTCOND:
182#endif
183#if (CH_CFG_USE_SEMAPHORES == TRUE) && \
184 (CH_CFG_USE_SEMAPHORES_PRIORITY == TRUE)
185 case CH_STATE_WTSEM:
186#endif
187 /* Re-enqueues tp with its new priority on the queue.*/
190 break;
191#endif
192#if (CH_CFG_USE_MESSAGES == TRUE) && \
193 (CH_CFG_USE_MESSAGES_PRIORITY == TRUE)
194 case CH_STATE_SNDMSGQ:
195 /* Re-enqueues tp using the receiver message queue back-pointer. */
198 break;
199#endif
200 case CH_STATE_READY:
201#if CH_DBG_ENABLE_ASSERTS == TRUE
202 /* Prevents an assertion in chSchReadyI().*/
204#endif
205 /* Re-enqueues tp with its new priority on the ready list.*/
207 break;
208 default:
209 /* Nothing to do for other states.*/
210 break;
211 }
212 break;
213 }
214
215 /* Sleep on the mutex.*/
216 ch_sch_prio_insert(&mp->queue, &currtp->hdr.queue);
217 currtp->u.wtmtxp = mp;
219
220 /* It is assumed that the thread performing the unlock operation assigns
221 the mutex to this thread.*/
222 chDbgAssert(mp->owner == currtp, "not owner");
223 chDbgAssert(currtp->mtxlist == mp, "not owned");
224#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
225 chDbgAssert(mp->cnt == (cnt_t)1, "counter is not one");
226 }
227#endif
228 }
229 else {
230#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
231 chDbgAssert(mp->cnt == (cnt_t)0, "counter is not zero");
232
233 mp->cnt++;
234#endif
235 /* It was not owned, inserted in the owned mutexes list.*/
236 mp->owner = currtp;
237 mp->next = currtp->mtxlist;
238 currtp->mtxlist = mp;
239 }
240}
241
242/**
243 * @brief Tries to lock a mutex.
244 * @details This function attempts to lock a mutex, if the mutex is already
245 * locked by another thread then the function exits without waiting.
246 * @post The mutex is locked and inserted in the per-thread stack of owned
247 * mutexes.
248 * @note This function does not have any overhead related to the
249 * priority inheritance mechanism because it does not try to
250 * enter a sleep state.
251 *
252 * @param[in] mp pointer to the @p mutex_t structure
253 * @return The operation status.
254 * @retval true if the mutex has been successfully acquired
255 * @retval false if the lock attempt failed.
256 *
257 * @api
258 */
260 bool b;
261
262 chSysLock();
263 b = chMtxTryLockS(mp);
264 chSysUnlock();
265
266 return b;
267}
268
269/**
270 * @brief Tries to lock a mutex.
271 * @details This function attempts to lock a mutex, if the mutex is already
272 * taken by another thread then the function exits without waiting.
273 * @post The mutex is locked and inserted in the per-thread stack of owned
274 * mutexes.
275 * @note This function does not have any overhead related to the
276 * priority inheritance mechanism because it does not try to
277 * enter a sleep state.
278 *
279 * @param[in] mp pointer to the @p mutex_t structure
280 * @return The operation status.
281 * @retval true if the mutex has been successfully acquired
282 * @retval false if the lock attempt failed.
283 *
284 * @sclass
285 */
287 thread_t *currtp = chThdGetSelfX();
288
290 chDbgCheck(mp != NULL);
291
292 if (mp->owner != NULL) {
293#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
294
295 chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive");
296
297 if (mp->owner == currtp) {
298 mp->cnt++;
299 return true;
300 }
301#endif
302 return false;
303 }
304#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
305
306 chDbgAssert(mp->cnt == (cnt_t)0, "counter is not zero");
307
308 mp->cnt++;
309#endif
310 mp->owner = currtp;
311 mp->next = currtp->mtxlist;
312 currtp->mtxlist = mp;
313 return true;
314}
315
316/**
317 * @brief Unlocks the specified mutex.
318 * @note Mutexes must be unlocked in reverse lock order. Violating this
319 * rules will result in a panic if assertions are enabled.
320 * @pre The invoking thread <b>must</b> have at least one owned mutex.
321 * @post The mutex is unlocked and removed from the per-thread stack of
322 * owned mutexes.
323 *
324 * @param[in] mp pointer to the @p mutex_t structure
325 *
326 * @api
327 */
329 thread_t *currtp = chThdGetSelfX();
330 mutex_t *lmp;
331
332 chDbgCheck(mp != NULL);
333
334 chSysLock();
335
336 chDbgAssert(currtp->mtxlist != NULL, "owned mutexes list empty");
337 chDbgAssert(currtp->mtxlist->owner == currtp, "ownership failure");
338#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
339 chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive");
340
341 if (--mp->cnt == (cnt_t)0) {
342#endif
343
344 chDbgAssert(currtp->mtxlist == mp, "not next in list");
345
346 /* Removes the top mutex from the thread's owned mutexes list and marks
347 it as not owned. Note, it is assumed to be the same mutex passed as
348 parameter of this function.*/
349 currtp->mtxlist = mp->next;
350
351 /* If a thread is waiting on the mutex then the fun part begins.*/
352 if (chMtxQueueNotEmptyS(mp)) {
353 thread_t *tp;
354
355 /* Recalculates the optimal thread priority by scanning the owned
356 mutexes list.*/
357 tprio_t newprio = currtp->realprio;
358 lmp = currtp->mtxlist;
359 while (lmp != NULL) {
360 /* If the highest priority thread waiting in the mutexes list has a
361 greater priority than the current thread base priority then the
362 final priority will have at least that priority.*/
363 if (chMtxQueueNotEmptyS(lmp) &&
364 ((threadref(lmp->queue.next))->hdr.pqueue.prio > newprio)) {
365 newprio = (threadref(lmp->queue.next))->hdr.pqueue.prio;
366 }
367 lmp = lmp->next;
368 }
369
370 /* Assigns to the current thread the highest priority among all the
371 waiting threads.*/
372 currtp->hdr.pqueue.prio = newprio;
373
374 /* Awakens the highest priority thread waiting for the unlocked mutex and
375 assigns the mutex to it.*/
376#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
377 mp->cnt = (cnt_t)1;
378#endif
380 mp->owner = tp;
381 mp->next = tp->mtxlist;
382 tp->mtxlist = mp;
383
384 /* Note, not using chSchWakeupS() because that function expects the
385 current thread to have the higher or equal priority than the ones
386 in the ready list. This is not necessarily true here because we
387 just changed priority.*/
388 (void) chSchReadyI(tp);
390 }
391 else {
392 mp->owner = NULL;
393 }
394#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
395 }
396#endif
397
398 chSysUnlock();
399}
400
401/**
402 * @brief Unlocks the specified mutex.
403 * @note Mutexes must be unlocked in reverse lock order. Violating this
404 * rules will result in a panic if assertions are enabled.
405 * @pre The invoking thread <b>must</b> have at least one owned mutex.
406 * @post The mutex is unlocked and removed from the per-thread stack of
407 * owned mutexes.
408 * @post This function does not reschedule so a call to a rescheduling
409 * function must be performed before unlocking the kernel.
410 *
411 * @param[in] mp pointer to the @p mutex_t structure
412 *
413 * @sclass
414 */
416 thread_t *currtp = chThdGetSelfX();
417 mutex_t *lmp;
418
420 chDbgCheck(mp != NULL);
421
422 chDbgAssert(currtp->mtxlist != NULL, "owned mutexes list empty");
423 chDbgAssert(currtp->mtxlist->owner == currtp, "ownership failure");
424#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
425 chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive");
426
427 if (--mp->cnt == (cnt_t)0) {
428#endif
429
430 chDbgAssert(currtp->mtxlist == mp, "not next in list");
431
432 /* Removes the top mutex from the thread's owned mutexes list and marks
433 it as not owned. Note, it is assumed to be the same mutex passed as
434 parameter of this function.*/
435 currtp->mtxlist = mp->next;
436
437 /* If a thread is waiting on the mutex then the fun part begins.*/
438 if (chMtxQueueNotEmptyS(mp)) {
439 thread_t *tp;
440
441 /* Recalculates the optimal thread priority by scanning the owned
442 mutexes list.*/
443 tprio_t newprio = currtp->realprio;
444 lmp = currtp->mtxlist;
445 while (lmp != NULL) {
446 /* If the highest priority thread waiting in the mutexes list has a
447 greater priority than the current thread base priority then the
448 final priority will have at least that priority.*/
449 if (chMtxQueueNotEmptyS(lmp) &&
450 ((threadref(lmp->queue.next))->hdr.pqueue.prio > newprio)) {
451 newprio = threadref(lmp->queue.next)->hdr.pqueue.prio;
452 }
453 lmp = lmp->next;
454 }
455
456 /* Assigns to the current thread the highest priority among all the
457 waiting threads.*/
458 currtp->hdr.pqueue.prio = newprio;
459
460 /* Awakens the highest priority thread waiting for the unlocked mutex and
461 assigns the mutex to it.*/
462#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
463 mp->cnt = (cnt_t)1;
464#endif
466 mp->owner = tp;
467 mp->next = tp->mtxlist;
468 tp->mtxlist = mp;
469 (void) chSchReadyI(tp);
470 }
471 else {
472 mp->owner = NULL;
473 }
474#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
475 }
476#endif
477}
478
479/**
480 * @brief Unlocks all mutexes owned by the invoking thread.
481 * @post The stack of owned mutexes is emptied and all the found
482 * mutexes are unlocked.
483 * @post This function does not reschedule so a call to a rescheduling
484 * function must be performed before unlocking the kernel.
485 * @note This function is <b>MUCH MORE</b> efficient than releasing the
486 * mutexes one by one and not just because the call overhead,
487 * this function does not have any overhead related to the priority
488 * inheritance mechanism.
489 *
490 * @sclass
491 */
492void chMtxUnlockAllS(void) {
493 thread_t *currtp = chThdGetSelfX();
494
495 if (currtp->mtxlist != NULL) {
496 do {
497 mutex_t *mp = currtp->mtxlist;
498 currtp->mtxlist = mp->next;
499 if (chMtxQueueNotEmptyS(mp)) {
500 thread_t *tp;
501#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
502 mp->cnt = (cnt_t)1;
503#endif
505 mp->owner = tp;
506 mp->next = tp->mtxlist;
507 tp->mtxlist = mp;
508 (void) chSchReadyI(tp);
509 }
510 else {
511#if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
512 mp->cnt = (cnt_t)0;
513#endif
514 mp->owner = NULL;
515 }
516 } while (currtp->mtxlist != NULL);
517 currtp->hdr.pqueue.prio = currtp->realprio;
519 }
520}
521
522/**
523 * @brief Unlocks all mutexes owned by the invoking thread.
524 * @post The stack of owned mutexes is emptied and all the found
525 * mutexes are unlocked.
526 * @note This function is <b>MUCH MORE</b> efficient than releasing the
527 * mutexes one by one and not just because the call overhead,
528 * this function does not have any overhead related to the priority
529 * inheritance mechanism.
530 *
531 * @api
532 */
533void chMtxUnlockAll(void) {
534
535 chSysLock();
537 chSysUnlock();
538}
539
540#endif /* CH_CFG_USE_MUTEXES == TRUE */
541
542/** @} */
ChibiOS/RT main include file.
#define chDbgAssert(c, r)
Condition assertion.
Definition chdebug.h:143
#define chDbgCheck(c)
Function parameters check.
Definition chdebug.h:117
#define chDbgCheckClassS()
Definition chdebug.h:99
void chMtxUnlock(mutex_t *mp)
Unlocks the specified mutex.
Definition chmtx.c:328
void chMtxUnlockAllS(void)
Unlocks all mutexes owned by the invoking thread.
Definition chmtx.c:492
bool chMtxTryLockS(mutex_t *mp)
Tries to lock a mutex.
Definition chmtx.c:286
void chMtxUnlockAll(void)
Unlocks all mutexes owned by the invoking thread.
Definition chmtx.c:533
static bool chMtxQueueNotEmptyS(mutex_t *mp)
Returns true if the mutex queue contains at least a waiting thread.
Definition chmtx.h:127
struct ch_mutex mutex_t
Type of a mutex structure.
Definition chmtx.h:51
bool chMtxTryLock(mutex_t *mp)
Tries to lock a mutex.
Definition chmtx.c:259
void chMtxLockS(mutex_t *mp)
Locks the specified mutex.
Definition chmtx.c:138
void chMtxUnlockS(mutex_t *mp)
Unlocks the specified mutex.
Definition chmtx.c:415
void chMtxObjectInit(mutex_t *mp)
Initializes s mutex_t structure.
Definition chmtx.c:102
void chMtxLock(mutex_t *mp)
Locks the specified mutex.
Definition chmtx.c:122
static void ch_queue_init(ch_queue_t *qp)
Queue initialization.
Definition chlists.h:221
static ch_queue_t * ch_queue_dequeue(ch_queue_t *p)
Removes an element from a queue and returns it.
Definition chlists.h:317
struct ch_queue ch_queue_t
Type of a generic bidirectional linked list header and element.
Definition chlists.h:62
static ch_queue_t * ch_queue_fifo_remove(ch_queue_t *qp)
Removes the first-out element from a queue and returns it.
Definition chlists.h:279
#define threadref(p)
Safe cast of a queue pointer to a thread pointer.
Definition chearly.h:205
int32_t cnt_t
Definition chearly.h:91
uint32_t tprio_t
Definition chearly.h:86
struct ch_thread thread_t
Type of a thread structure.
Definition chearly.h:132
#define CH_STATE_READY
Waiting on the ready list.
Definition chschd.h:61
#define CH_STATE_CURRENT
Currently running.
Definition chschd.h:63
void chSchRescheduleS(void)
Performs a reschedule if a higher priority thread is runnable.
Definition chschd.c:457
#define CH_STATE_WTCOND
On a cond.variable.
Definition chschd.h:69
thread_t * chSchReadyI(thread_t *tp)
Inserts a thread in the Ready List placing it behind its peers.
Definition chschd.c:279
void chSchGoSleepS(tstate_t newstate)
Puts the current thread to sleep into the specified state.
Definition chschd.c:304
void ch_sch_prio_insert(ch_queue_t *qp, ch_queue_t *tp)
Inserts a thread into a priority ordered queue.
Definition chschd.c:249
#define CH_STATE_WTMTX
On a mutex.
Definition chschd.h:68
#define CH_STATE_SNDMSGQ
Sending a message, in queue.
Definition chschd.h:74
#define CH_STATE_WTSEM
On a semaphore.
Definition chschd.h:67
static void chSysLock(void)
Enters the kernel lock state.
Definition chsys.h:406
static void chSysUnlock(void)
Leaves the kernel lock state.
Definition chsys.h:420
static thread_t * chThdGetSelfX(void)
Returns a pointer to the current thread_t.
Definition chthreads.h:340
cnt_t cnt
Mutex recursion counter.
Definition chmtx.h:64
thread_t * owner
Owner thread_t pointer or NULL.
Definition chmtx.h:59
mutex_t * next
Next mutex_t into an owner-list or NULL.
Definition chmtx.h:61
ch_queue_t queue
Queue of the threads sleeping on this mutex.
Definition chmtx.h:57
tprio_t prio
Priority of this element.
Definition chlists.h:87
ch_queue_t * next
Next in the list/queue.
Definition chlists.h:69
ch_queue_t queue
Threads queues element.
Definition chobjects.h:165
union ch_thread::@065317322233202114332352372014266163076165303275 hdr
Shared list headers.
struct ch_mutex * wtmtxp
Pointer to a generic mutex object.
Definition chobjects.h:277
tprio_t realprio
Thread's own, non-inherited, priority.
Definition chobjects.h:333
struct ch_mutex * mtxlist
List of the mutexes owned by this thread.
Definition chobjects.h:329
void * wtobjp
Pointer to a generic "wait" object.
Definition chobjects.h:253
tstate_t state
Current thread state.
Definition chobjects.h:203
ch_priority_queue_t pqueue
Threads ordered queues element.
Definition chobjects.h:169
union ch_thread::@250330312022121344252011223135034045240103044261 u
State-specific fields.