189 lines
5.9 KiB
ReStructuredText
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 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 :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
|