173 lines
5.3 KiB
ReStructuredText
173 lines
5.3 KiB
ReStructuredText
.. _mutexes_v2:
|
|
|
|
Mutexes
|
|
#######
|
|
|
|
A :dfn:`mutex` is a kernel object that implements a traditional
|
|
reentrant mutex. A mutex allows multiple threads to safely share
|
|
an associated hardware or software resource by ensuring mutually exclusive
|
|
access to the resource.
|
|
|
|
.. contents::
|
|
:local:
|
|
:depth: 2
|
|
|
|
Concepts
|
|
********
|
|
|
|
Any number of mutexes can be defined. Each mutex is referenced by its memory
|
|
address.
|
|
|
|
A mutex has the following key properties:
|
|
|
|
* A **lock count** that indicates the number of times the mutex has be locked
|
|
by the thread that has locked it. A count of zero indicates that the mutex
|
|
is unlocked.
|
|
|
|
* An **owning thread** that identifies the thread that has locked the mutex,
|
|
when it is locked.
|
|
|
|
A mutex must be initialized before it can be used. This sets its lock count
|
|
to zero.
|
|
|
|
A thread that needs to use a shared resource must first gain exclusive rights
|
|
to access it by **locking** the associated mutex. If the mutex is already locked
|
|
by another thread, the requesting thread may choose to wait for the mutex
|
|
to be unlocked.
|
|
|
|
After locking a mutex, the thread may safely use the associated resource
|
|
for as long as needed; however, it is considered good practice to hold the lock
|
|
for as short a time as possible to avoid negatively impacting other threads
|
|
that want to use the resource. When the thread no longer needs the resource
|
|
it must **unlock** the mutex to allow other threads to use the resource.
|
|
|
|
Any number of threads may wait on a locked mutex simultaneously.
|
|
When the mutex becomes unlocked it is then locked by the highest-priority
|
|
thread that has waited the longest.
|
|
|
|
.. note::
|
|
Mutex objects are *not* designed for use by ISRs.
|
|
|
|
Reentrant Locking
|
|
=================
|
|
|
|
A thread is permitted to lock a mutex it has already locked.
|
|
This allows the thread to access the associated resource at a point
|
|
in its execution when the mutex may or may not already be locked.
|
|
|
|
A mutex that is repeatedly locked by a thread must be unlocked an equal number
|
|
of times before the mutex becomes fully unlocked so it can be claimed
|
|
by another thread.
|
|
|
|
Priority Inheritance
|
|
====================
|
|
|
|
The thread that has locked a mutex is eligible for :dfn:`priority inheritance`.
|
|
This means the kernel will *temporarily* elevate the thread's priority
|
|
if a higher priority thread begins waiting on the mutex. This allows the owning
|
|
thread to complete its work and release the mutex more rapidly by executing
|
|
at the same priority as the waiting thread. Once the mutex has been unlocked,
|
|
the unlocking thread resets its priority to the level it had before locking
|
|
that mutex.
|
|
|
|
.. note::
|
|
The :option:`CONFIG_PRIORITY_CEILING` configuration option limits
|
|
how high the kernel can raise a thread's priority due to priority
|
|
inheritance. The default value of 0 permits unlimited elevation.
|
|
|
|
When two or more threads wait on a mutex held by a lower priority thread, the
|
|
kernel adjusts the owning thread's priority each time a thread begins waiting
|
|
(or gives up waiting). When the mutex is eventually unlocked, the unlocking
|
|
thread's priority correctly reverts to its original non-elevated priority.
|
|
|
|
The kernel does *not* fully support priority inheritance when a thread holds
|
|
two or more mutexes simultaneously. This situation can result in the thread's
|
|
priority not reverting to its original non-elevated priority when all mutexes
|
|
have been released. It is recommended that a thread lock only a single mutex
|
|
at a time when multiple mutexes are shared between threads of different
|
|
priorities.
|
|
|
|
Implementation
|
|
**************
|
|
|
|
Defining a Mutex
|
|
================
|
|
|
|
A mutex is defined using a variable of type :c:type:`struct k_mutex`.
|
|
It must then be initialized by calling :cpp:func:`k_mutex_init()`.
|
|
|
|
The following code defines and initializes a mutex.
|
|
|
|
.. code-block:: c
|
|
|
|
struct k_mutex my_mutex;
|
|
|
|
k_mutex_init(&my_mutex);
|
|
|
|
Alternatively, a mutex can be defined and initialized at compile time
|
|
by calling :c:macro:`K_MUTEX_DEFINE`.
|
|
|
|
The following code has the same effect as the code segment above.
|
|
|
|
.. code-block:: c
|
|
|
|
K_MUTEX_DEFINE(my_mutex);
|
|
|
|
Locking a Mutex
|
|
===============
|
|
|
|
A mutex is locked by calling :cpp:func:`k_mutex_lock()`.
|
|
|
|
The following code builds on the example above, and waits indefinitely
|
|
for the mutex to become available if it is already locked by another thread.
|
|
|
|
.. code-block:: c
|
|
|
|
k_mutex_lock(&my_mutex, K_FOREVER);
|
|
|
|
The following code waits up to 100 milliseconds for the mutex to become
|
|
available, and gives a warning if the mutex does not become available.
|
|
|
|
.. code-block:: c
|
|
|
|
if (k_mutex_lock(&my_mutex, K_MSEC(100)) == 0) {
|
|
/* mutex successfully locked */
|
|
} else {
|
|
printf("Cannot lock XYZ display\n");
|
|
}
|
|
|
|
Unlocking a Mutex
|
|
=================
|
|
|
|
A mutex is unlocked by calling :cpp:func:`k_mutex_unlock()`.
|
|
|
|
The following code builds on the example above,
|
|
and unlocks the mutex that was previously locked by the thread.
|
|
|
|
.. code-block:: c
|
|
|
|
k_mutex_unlock(&my_mutex);
|
|
|
|
Suggested Uses
|
|
**************
|
|
|
|
Use a mutex to provide exclusive access to a resource, such as a physical
|
|
device.
|
|
|
|
Configuration Options
|
|
*********************
|
|
|
|
Related configuration options:
|
|
|
|
* :option:`CONFIG_PRIORITY_CEILING`
|
|
|
|
APIs
|
|
****
|
|
|
|
The following mutex APIs are provided by :file:`kernel.h`:
|
|
|
|
* :c:macro:`K_MUTEX_DEFINE`
|
|
* :cpp:func:`k_mutex_init()`
|
|
* :cpp:func:`k_mutex_lock()`
|
|
* :cpp:func:`k_mutex_unlock()`
|