zephyr/doc/kernel/microkernel/microkernel_mutexes.rst

195 lines
5.6 KiB
ReStructuredText

.. _microkernel_mutexes:
Mutexes
#######
Concepts
********
The microkernel's :dfn:`mutex` objects provide reentrant mutex
capabilities with basic priority inheritance.
Each mutex allows multiple tasks to safely share an associated
resource by ensuring mutual exclusivity while the resource is
being accessed by a task.
Any number of mutexes can be defined in a microkernel system.
Each mutex needs a **name** that uniquely identifies it. Typically,
the name should relate to the resource being shared, although this
is not a requirement.
A task that needs to use a shared resource must first gain exclusive
access by locking the associated mutex. If the mutex is already locked
by another task, the requesting task can wait for the mutex to be
unlocked.
After obtaining the mutex, the task may safely use the shared
resource for as long as needed. And when the task no longer needs
the resource, it must release the associated mutex to allow
other tasks to use the resource.
Any number of tasks may wait on a locked mutex. When more than one
task is waiting, the mutex locks the resource for the highest-priority
task that has waited the longest first; this is known as
:dfn:`priority-based waiting`. The order is decided when a task decides
to wait on the object: it is queued in priority order.
The task currently owning the mutex is also eligible for :dfn:`priority inheritance`.
Priority inheritance is the concept by which a task of lower priority gets its
priority *temporarily* elevated to the priority of the highest-priority
task that is waiting on a mutex held by the lower priority task. Thus, the
lower-priority task can complete its work and release the mutex as quickly
as possible. Once the mutex has been released, the lower-priority task resets
its task priority to the priority it had before locking that mutex.
.. note::
The :option:`CONFIG_PRIORITY_CEILING` configuration option limits
how high the kernel can raise a task's priority due to priority
inheritance. The default value of 0 permits unlimited elevation.
When two or more tasks wait on a mutex held by a lower priority task, the
kernel adjusts the owning task's priority each time a task begins waiting
(or gives up waiting). When the mutex is eventually released, the owning
task's priority correctly reverts to its original non-elevated priority.
The kernel does *not* fully support priority inheritance when a task holds
two or more mutexes simultaneously. This situation can result in the task's
priority not reverting to its original non-elevated priority when all mutexes
have been released. Preferably, a task holds only a single mutex when multiple
mutexes are shared between tasks of different priorities.
The microkernel also allows a task to repeatedly lock a mutex it has already
locked. This ensures that the task can access the resource at a point in its
execution when the resource may or may not already be locked. A mutex that is
repeatedly locked must be unlocked an equal number of times before the mutex
can release the resource completely.
Purpose
*******
Use mutexes to provide exclusive access to a resource, such as a physical
device.
Usage
*****
Defining a Mutex
================
The following parameters must be defined:
*name*
This specifies a unique name for the mutex.
Public Mutex
------------
Define the mutex in the application's MDEF file with the following syntax:
.. code-block:: console
MUTEX name
For example, the file :file:`projName.mdef` defines a single mutex as follows:
.. code-block:: console
% MUTEX NAME
% ===============
MUTEX DEVICE_X
A public mutex can be referenced by name from any source file that includes
the file :file:`zephyr.h`.
Private Mutex
-------------
Define the mutex in a source file using the following syntax:
.. code-block:: c
DEFINE_MUTEX(name);
For example, the following code defines a private mutex named ``XYZ``.
.. code-block:: c
DEFINE_MUTEX(XYZ);
The following syntax allows this mutex to be accessed from a different
source file:
.. code-block:: c
extern const kmutex_t XYZ;
Example: Locking a Mutex with No Conditions
===========================================
This code waits indefinitely for the mutex to become available if the
mutex is in use.
.. code-block:: c
task_mutex_lock(XYZ, TICKS_UNLIMITED);
moveto(100,100);
lineto(200,100);
task_mutex_unlock(XYZ);
Example: Locking a Mutex with a Conditional Timeout
===================================================
This code waits for a mutex to become available for a specified
time, and gives a warning if the mutex does not become available
in the specified amount of time.
.. code-block:: c
if (task_mutex_lock(XYZ, 100) == RC_OK)
{
moveto(100,100);
lineto(200,100);
task_mutex_unlock(XYZ);
}
else
{
printf("Cannot lock XYZ display\n");
}
Example: Locking a Mutex with a No Blocking Condition
=====================================================
This code gives an immediate warning when a mutex is in use.
.. code-block:: c
if (task_mutex_lock(XYZ, TICKS_NONE) == RC_OK);
{
do_something();
task_mutex_unlock(XYZ); /* and unlock mutex*/
}
else
{
display_warning(); /* and do not unlock mutex*/
}
APIs
****
Mutex APIs provided by :file:`microkernel.h`
============================================
:cpp:func:`task_mutex_lock()`
Wait on a locked mutex for the period of time defined by the timeout
parameter. Lock the mutex and increment the lock count if the mutex
becomes available during that period.
:cpp:func:`task_mutex_unlock()`
Decrement a mutex lock count, and unlock the mutex when the count
reaches zero.