ChibiOS/RT  5.1.0
chmtx.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 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; either version 3 of the License, or
9  (at your option) any later version.
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 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  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  */
123 void chMtxLock(mutex_t *mp) {
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  */
139 void chMtxLockS(mutex_t *mp) {
140  thread_t *ctp = currp;
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 == ctp) {
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->prio < ctp->prio) {
166  /* Make priority of thread tp match the running thread's priority.*/
167  tp->prio = ctp->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.*/
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  ((CH_CFG_USE_MESSAGES == TRUE) && \
181  (CH_CFG_USE_MESSAGES_PRIORITY == TRUE))
182 #if CH_CFG_USE_CONDVARS == TRUE
183  case CH_STATE_WTCOND:
184 #endif
185 #if (CH_CFG_USE_SEMAPHORES == TRUE) && \
186  (CH_CFG_USE_SEMAPHORES_PRIORITY == TRUE)
187  case CH_STATE_WTSEM:
188 #endif
189 #if (CH_CFG_USE_MESSAGES == TRUE) && (CH_CFG_USE_MESSAGES_PRIORITY == TRUE)
190  case CH_STATE_SNDMSGQ:
191 #endif
192  /* Re-enqueues tp with its new priority on the queue.*/
194  break;
195 #endif
196  case CH_STATE_READY:
197 #if CH_DBG_ENABLE_ASSERTS == TRUE
198  /* Prevents an assertion in chSchReadyI().*/
199  tp->state = CH_STATE_CURRENT;
200 #endif
201  /* Re-enqueues tp with its new priority on the ready list.*/
202  (void) chSchReadyI(queue_dequeue(tp));
203  break;
204  default:
205  /* Nothing to do for other states.*/
206  break;
207  }
208  break;
209  }
210 
211  /* Sleep on the mutex.*/
212  queue_prio_insert(ctp, &mp->queue);
213  ctp->u.wtmtxp = mp;
215 
216  /* It is assumed that the thread performing the unlock operation assigns
217  the mutex to this thread.*/
218  chDbgAssert(mp->owner == ctp, "not owner");
219  chDbgAssert(ctp->mtxlist == mp, "not owned");
220 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
221  chDbgAssert(mp->cnt == (cnt_t)1, "counter is not one");
222  }
223 #endif
224  }
225  else {
226 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
227  chDbgAssert(mp->cnt == (cnt_t)0, "counter is not zero");
228 
229  mp->cnt++;
230 #endif
231  /* It was not owned, inserted in the owned mutexes list.*/
232  mp->owner = ctp;
233  mp->next = ctp->mtxlist;
234  ctp->mtxlist = mp;
235  }
236 }
237 
238 /**
239  * @brief Tries to lock a mutex.
240  * @details This function attempts to lock a mutex, if the mutex is already
241  * locked by another thread then the function exits without waiting.
242  * @post The mutex is locked and inserted in the per-thread stack of owned
243  * mutexes.
244  * @note This function does not have any overhead related to the
245  * priority inheritance mechanism because it does not try to
246  * enter a sleep state.
247  *
248  * @param[in] mp pointer to the @p mutex_t structure
249  * @return The operation status.
250  * @retval true if the mutex has been successfully acquired
251  * @retval false if the lock attempt failed.
252  *
253  * @api
254  */
256  bool b;
257 
258  chSysLock();
259  b = chMtxTryLockS(mp);
260  chSysUnlock();
261 
262  return b;
263 }
264 
265 /**
266  * @brief Tries to lock a mutex.
267  * @details This function attempts to lock a mutex, if the mutex is already
268  * taken by another thread then the function exits without waiting.
269  * @post The mutex is locked and inserted in the per-thread stack of owned
270  * mutexes.
271  * @note This function does not have any overhead related to the
272  * priority inheritance mechanism because it does not try to
273  * enter a sleep state.
274  *
275  * @param[in] mp pointer to the @p mutex_t structure
276  * @return The operation status.
277  * @retval true if the mutex has been successfully acquired
278  * @retval false if the lock attempt failed.
279  *
280  * @sclass
281  */
283 
285  chDbgCheck(mp != NULL);
286 
287  if (mp->owner != NULL) {
288 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
289 
290  chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive");
291 
292  if (mp->owner == currp) {
293  mp->cnt++;
294  return true;
295  }
296 #endif
297  return false;
298  }
299 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
300 
301  chDbgAssert(mp->cnt == (cnt_t)0, "counter is not zero");
302 
303  mp->cnt++;
304 #endif
305  mp->owner = currp;
306  mp->next = currp->mtxlist;
307  currp->mtxlist = mp;
308  return true;
309 }
310 
311 /**
312  * @brief Unlocks the specified mutex.
313  * @note Mutexes must be unlocked in reverse lock order. Violating this
314  * rules will result in a panic if assertions are enabled.
315  * @pre The invoking thread <b>must</b> have at least one owned mutex.
316  * @post The mutex is unlocked and removed from the per-thread stack of
317  * owned mutexes.
318  *
319  * @param[in] mp pointer to the @p mutex_t structure
320  *
321  * @api
322  */
323 void chMtxUnlock(mutex_t *mp) {
324  thread_t *ctp = currp;
325  mutex_t *lmp;
326 
327  chDbgCheck(mp != NULL);
328 
329  chSysLock();
330 
331  chDbgAssert(ctp->mtxlist != NULL, "owned mutexes list empty");
332  chDbgAssert(ctp->mtxlist->owner == ctp, "ownership failure");
333 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
334  chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive");
335 
336  if (--mp->cnt == (cnt_t)0) {
337 #endif
338 
339  chDbgAssert(ctp->mtxlist == mp, "not next in list");
340 
341  /* Removes the top mutex from the thread's owned mutexes list and marks
342  it as not owned. Note, it is assumed to be the same mutex passed as
343  parameter of this function.*/
344  ctp->mtxlist = mp->next;
345 
346  /* If a thread is waiting on the mutex then the fun part begins.*/
347  if (chMtxQueueNotEmptyS(mp)) {
348  thread_t *tp;
349 
350  /* Recalculates the optimal thread priority by scanning the owned
351  mutexes list.*/
352  tprio_t newprio = ctp->realprio;
353  lmp = ctp->mtxlist;
354  while (lmp != NULL) {
355  /* If the highest priority thread waiting in the mutexes list has a
356  greater priority than the current thread base priority then the
357  final priority will have at least that priority.*/
358  if (chMtxQueueNotEmptyS(lmp) &&
359  (lmp->queue.next->prio > newprio)) {
360  newprio = lmp->queue.next->prio;
361  }
362  lmp = lmp->next;
363  }
364 
365  /* Assigns to the current thread the highest priority among all the
366  waiting threads.*/
367  ctp->prio = newprio;
368 
369  /* Awakens the highest priority thread waiting for the unlocked mutex and
370  assigns the mutex to it.*/
371 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
372  mp->cnt = (cnt_t)1;
373 #endif
374  tp = queue_fifo_remove(&mp->queue);
375  mp->owner = tp;
376  mp->next = tp->mtxlist;
377  tp->mtxlist = mp;
378 
379  /* Note, not using chSchWakeupS() becuase that function expects the
380  current thread to have the higher or equal priority than the ones
381  in the ready list. This is not necessarily true here because we
382  just changed priority.*/
383  (void) chSchReadyI(tp);
385  }
386  else {
387  mp->owner = NULL;
388  }
389 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
390  }
391 #endif
392 
393  chSysUnlock();
394 }
395 
396 /**
397  * @brief Unlocks the specified mutex.
398  * @note Mutexes must be unlocked in reverse lock order. Violating this
399  * rules will result in a panic if assertions are enabled.
400  * @pre The invoking thread <b>must</b> have at least one owned mutex.
401  * @post The mutex is unlocked and removed from the per-thread stack of
402  * owned mutexes.
403  * @post This function does not reschedule so a call to a rescheduling
404  * function must be performed before unlocking the kernel.
405  *
406  * @param[in] mp pointer to the @p mutex_t structure
407  *
408  * @sclass
409  */
411  thread_t *ctp = currp;
412  mutex_t *lmp;
413 
415  chDbgCheck(mp != NULL);
416 
417  chDbgAssert(ctp->mtxlist != NULL, "owned mutexes list empty");
418  chDbgAssert(ctp->mtxlist->owner == ctp, "ownership failure");
419 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
420  chDbgAssert(mp->cnt >= (cnt_t)1, "counter is not positive");
421 
422  if (--mp->cnt == (cnt_t)0) {
423 #endif
424 
425  chDbgAssert(ctp->mtxlist == mp, "not next in list");
426 
427  /* Removes the top mutex from the thread's owned mutexes list and marks
428  it as not owned. Note, it is assumed to be the same mutex passed as
429  parameter of this function.*/
430  ctp->mtxlist = mp->next;
431 
432  /* If a thread is waiting on the mutex then the fun part begins.*/
433  if (chMtxQueueNotEmptyS(mp)) {
434  thread_t *tp;
435 
436  /* Recalculates the optimal thread priority by scanning the owned
437  mutexes list.*/
438  tprio_t newprio = ctp->realprio;
439  lmp = ctp->mtxlist;
440  while (lmp != NULL) {
441  /* If the highest priority thread waiting in the mutexes list has a
442  greater priority than the current thread base priority then the
443  final priority will have at least that priority.*/
444  if (chMtxQueueNotEmptyS(lmp) &&
445  (lmp->queue.next->prio > newprio)) {
446  newprio = lmp->queue.next->prio;
447  }
448  lmp = lmp->next;
449  }
450 
451  /* Assigns to the current thread the highest priority among all the
452  waiting threads.*/
453  ctp->prio = newprio;
454 
455  /* Awakens the highest priority thread waiting for the unlocked mutex and
456  assigns the mutex to it.*/
457 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
458  mp->cnt = (cnt_t)1;
459 #endif
460  tp = queue_fifo_remove(&mp->queue);
461  mp->owner = tp;
462  mp->next = tp->mtxlist;
463  tp->mtxlist = mp;
464  (void) chSchReadyI(tp);
465  }
466  else {
467  mp->owner = NULL;
468  }
469 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
470  }
471 #endif
472 }
473 
474 /**
475  * @brief Unlocks all mutexes owned by the invoking thread.
476  * @post The stack of owned mutexes is emptied and all the found
477  * mutexes are unlocked.
478  * @post This function does not reschedule so a call to a rescheduling
479  * function must be performed before unlocking the kernel.
480  * @note This function is <b>MUCH MORE</b> efficient than releasing the
481  * mutexes one by one and not just because the call overhead,
482  * this function does not have any overhead related to the priority
483  * inheritance mechanism.
484  *
485  * @sclass
486  */
487 void chMtxUnlockAllS(void) {
488  thread_t *ctp = currp;
489 
490  while (ctp->mtxlist != NULL) {
491  mutex_t *mp = ctp->mtxlist;
492  ctp->mtxlist = mp->next;
493  if (chMtxQueueNotEmptyS(mp)) {
494 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
495  mp->cnt = (cnt_t)1;
496 #endif
497  thread_t *tp = queue_fifo_remove(&mp->queue);
498  mp->owner = tp;
499  mp->next = tp->mtxlist;
500  tp->mtxlist = mp;
501  (void) chSchReadyI(tp);
502  }
503  else {
504 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
505  mp->cnt = (cnt_t)0;
506 #endif
507  mp->owner = NULL;
508  }
509  }
510  ctp->prio = ctp->realprio;
511 }
512 
513 /**
514  * @brief Unlocks all mutexes owned by the invoking thread.
515  * @post The stack of owned mutexes is emptied and all the found
516  * mutexes are unlocked.
517  * @note This function is <b>MUCH MORE</b> efficient than releasing the
518  * mutexes one by one and not just because the call overhead,
519  * this function does not have any overhead related to the priority
520  * inheritance mechanism.
521  *
522  * @api
523  */
524 void chMtxUnlockAll(void) {
525  thread_t *ctp = currp;
526 
527  chSysLock();
528  if (ctp->mtxlist != NULL) {
529  do {
530  mutex_t *mp = ctp->mtxlist;
531  ctp->mtxlist = mp->next;
532  if (chMtxQueueNotEmptyS(mp)) {
533 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
534  mp->cnt = (cnt_t)1;
535 #endif
536  thread_t *tp = queue_fifo_remove(&mp->queue);
537  mp->owner = tp;
538  mp->next = tp->mtxlist;
539  tp->mtxlist = mp;
540  (void) chSchReadyI(tp);
541  }
542  else {
543 #if CH_CFG_USE_MUTEXES_RECURSIVE == TRUE
544  mp->cnt = (cnt_t)0;
545 #endif
546  mp->owner = NULL;
547  }
548  } while (ctp->mtxlist != NULL);
549  ctp->prio = ctp->realprio;
551  }
552  chSysUnlock();
553 }
554 
555 #endif /* CH_CFG_USE_MUTEXES == TRUE */
556 
557 /** @} */
bool chMtxTryLock(mutex_t *mp)
Tries to lock a mutex.
Definition: chmtx.c:255
void chDbgCheckClassS(void)
S-class functions context check.
Definition: chdebug.c:250
threads_queue_t queue
Queue of the threads sleeping on this mutex.
Definition: chmtx.h:58
void chMtxUnlockS(mutex_t *mp)
Unlocks the specified mutex.
Definition: chmtx.c:410
static void chSysLock(void)
Enters the kernel lock state.
Definition: chsys.h:353
tprio_t prio
Thread priority.
Definition: chschd.h:155
struct ch_mutex * mtxlist
List of the mutexes owned by this thread.
Definition: chschd.h:294
#define CH_STATE_WTMTX
On a mutex.
Definition: chschd.h:73
thread_t * chSchReadyI(thread_t *tp)
Inserts a thread in the Ready List placing it behind its peers.
Definition: chschd.c:218
void queue_prio_insert(thread_t *tp, threads_queue_t *tqp)
Inserts a thread into a priority ordered queue.
Definition: chschd.c:86
#define CH_STATE_CURRENT
Currently running.
Definition: chschd.h:68
#define currp
Current thread pointer access macro.
Definition: chschd.h:459
static void chSysUnlock(void)
Leaves the kernel lock state.
Definition: chsys.h:365
struct ch_mutex * wtmtxp
Pointer to a generic mutex object.
Definition: chschd.h:260
#define CH_STATE_WTCOND
On a cond.variable.
Definition: chschd.h:74
void chMtxUnlockAllS(void)
Unlocks all mutexes owned by the invoking thread.
Definition: chmtx.c:487
#define CH_STATE_SNDMSGQ
Sending a message, in queue.
Definition: chschd.h:79
Mutex structure.
Definition: chmtx.h:57
thread_t * owner
Owner thread_t pointer or NULL.
Definition: chmtx.h:60
void chSchRescheduleS(void)
Performs a reschedule if a higher priority thread is runnable.
Definition: chschd.c:456
thread_t * next
Next in the list/queue.
Definition: chschd.h:143
mutex_t * next
Next mutex_t into an owner-list or NULL.
Definition: chmtx.h:62
void chSchGoSleepS(tstate_t newstate)
Puts the current thread to sleep into the specified state.
Definition: chschd.c:289
static void queue_init(threads_queue_t *tqp)
Threads queue initialization.
Definition: chschd.h:548
#define chDbgCheck(c)
Function parameters check.
Definition: chdebug.h:101
void chMtxLock(mutex_t *mp)
Locks the specified mutex.
Definition: chmtx.c:123
bool chMtxTryLockS(mutex_t *mp)
Tries to lock a mutex.
Definition: chmtx.c:282
#define chDbgAssert(c, r)
Condition assertion.
Definition: chdebug.h:127
#define CH_STATE_READY
Waiting on the ready list.
Definition: chschd.h:65
tstate_t state
Current thread state.
Definition: chschd.h:180
#define CH_STATE_WTSEM
On a semaphore.
Definition: chschd.h:72
static bool chMtxQueueNotEmptyS(mutex_t *mp)
Returns true if the mutex queue contains at least a waiting thread.
Definition: chmtx.h:129
union ch_thread::@0 u
State-specific fields.
thread_t * queue_dequeue(thread_t *tp)
Removes a thread from a queue and returns it.
Definition: chschd.c:162
void chMtxObjectInit(mutex_t *mp)
Initializes s mutex_t structure.
Definition: chmtx.c:103
cnt_t cnt
Mutex recursion counter.
Definition: chmtx.h:65
void chMtxUnlock(mutex_t *mp)
Unlocks the specified mutex.
Definition: chmtx.c:323
ChibiOS/RT main include file.
thread_t * queue_fifo_remove(threads_queue_t *tqp)
Removes the first-out thread from a queue and returns it.
Definition: chschd.c:124
tprio_t realprio
Thread&#39;s own, non-inherited, priority.
Definition: chschd.h:298
void chMtxUnlockAll(void)
Unlocks all mutexes owned by the invoking thread.
Definition: chmtx.c:524
void chMtxLockS(mutex_t *mp)
Locks the specified mutex.
Definition: chmtx.c:139
Structure representing a thread.
Definition: chschd.h:153