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