zephyr/doc/kernel/services/synchronization/mutexes.rst

189 lines
5.9 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 (limited only by available RAM). 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 been 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 :kconfig: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.
The owning thread's base priority is saved in the mutex when it obtains the
lock. Each time a higher priority thread waits on a mutex, the kernel adjusts
the owning thread's priority. When the owning thread releases the lock (or if
the high priority waiting thread times out), the kernel restores the thread's
base priority from the value saved in the mutex.
This works well for priority inheritance as long as only one locked mutex is
involved. However, if multiple mutexes are involved, sub-optimal behavior will
be observed if the mutexes are not unlocked in the reverse order to which the
owning thread's priority was previously raised. Consequently 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:struct:`k_mutex`.
It must then be initialized by calling :c: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 :c: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 :c: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:
* :kconfig:option:`CONFIG_PRIORITY_CEILING`
API Reference
*************
.. doxygengroup:: mutex_apis
Futex API Reference
*******************
k_futex is a lightweight mutual exclusion primitive designed to minimize
kernel involvement. Uncontended operation relies only on atomic access
to shared memory. k_futex are tracked as kernel objects and can live in
user memory so that any access bypasses the kernel object permission
management mechanism.
.. doxygengroup:: futex_apis
User Mode Mutex API Reference
*****************************
sys_mutex behaves almost exactly like k_mutex, with the added advantage
that a sys_mutex instance can reside in user memory. When user mode isn't
enabled, sys_mutex behaves like k_mutex.
.. doxygengroup:: user_mutex_apis