Revises and supplements information on concepts and use cases. Change-Id: I1ae8f9382b6fd98af59f2a8ef555830981807d5d Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
293 lines
9.9 KiB
ReStructuredText
293 lines
9.9 KiB
ReStructuredText
.. _microkernel_semaphores:
|
|
|
|
Semaphores
|
|
##########
|
|
|
|
Concepts
|
|
********
|
|
|
|
The microkernel's semaphore objects are an implementation of traditional
|
|
counting semaphores.
|
|
|
|
Any number of semaphores can be defined in a microkernel system. Each semaphore
|
|
has a name that uniquely identifies it.
|
|
|
|
A semaphore starts off with a count of zero. This count is incremented each
|
|
time the semaphore is given, and is decremented each time the semaphore
|
|
is taken. However, a semaphore cannot be taken if it is unavailable
|
|
(i.e. has a count of zero).
|
|
|
|
Semaphores may be given by tasks, fibers, or ISRs.
|
|
|
|
.. note::
|
|
|
|
When a semaphore is given by a fiber or an ISR the caller must provide
|
|
a statically-allocated command packet for the microkernel server fiber
|
|
to use, since the kernel cannot create one on the caller's stack as it
|
|
does when the semaphore is given by a task. In cases where the fiber
|
|
or ISR can give the semaphore again before the microkernel server finishes
|
|
processing the earlier request the caller must provide a separate command
|
|
packet for each concurrent give operation.
|
|
|
|
The kernel provides the :c:macro:`CMD_PKT_SET_INSTANCE()` API to allow
|
|
a fiber or ISR to define a *command packet set* containing one or more
|
|
command packets. The :c:macro:`CMD_PKT_SET()` API is then used to reference
|
|
the command packet set when giving a semaphore;
|
|
see :ref:`isr_gives_semaphore`.
|
|
|
|
Semaphores may only be taken by tasks. A task that attempts to take
|
|
an unavailable semaphore may choose to wait for the semaphore to be given.
|
|
Any number of tasks may wait on an unavailable semaphore simultaneously;
|
|
when the semaphore becomes available it is given to the highest priority task
|
|
that has waited the longest.
|
|
|
|
The kernel allows a task to give multiple semaphores in a single
|
|
operation using a *semaphore group*. The task specifies the members of
|
|
a semaphore group using an array of semaphore names, terminated by the
|
|
symbol :c:macro:`ENDLIST`. This technique allows the task to give the semaphores
|
|
more efficiently than giving them individually.
|
|
|
|
A task can also use a semaphore group to take a single semaphore from a set
|
|
of semaphores in a single operation. This technique allows the task to
|
|
monitor multiple synchronization sources at the same time, similar to the way
|
|
:c:func:`select()` can be used to read input from a set of file descriptors
|
|
in a POSIX-compliant operating system. The kernel does *not* define the order
|
|
in which semaphores are taken when more than one semaphore in a semaphore group
|
|
is available; the semaphore that is taken by the task may not be the one
|
|
that was given first.
|
|
|
|
There is no limit on the number of semaphore groups used by a task, or
|
|
on the number of semaphores belonging to any given semaphore group. Semaphore
|
|
groups may also be shared by multiple tasks, if desired.
|
|
|
|
|
|
Purpose
|
|
*******
|
|
|
|
Use a semaphore to control access to a set of resources by multiple tasks.
|
|
|
|
Use a semaphore synchronize processing between a producing task, fiber,
|
|
or ISR and one or more consuming tasks.
|
|
|
|
Use a semaphore group to allow a task to signal or to monitor multiple
|
|
semaphores simultaneously.
|
|
|
|
|
|
Usage
|
|
*****
|
|
|
|
Defining a Semaphore
|
|
====================
|
|
|
|
The following parameters must be defined:
|
|
|
|
*name*
|
|
This specifies a unique name for the semaphore.
|
|
|
|
|
|
Public Semaphore
|
|
----------------
|
|
|
|
Define the semaphore in the application's .MDEF file using the following syntax:
|
|
|
|
.. code-block:: console
|
|
|
|
SEMA name
|
|
|
|
For example, the file :file:`projName.mdef` defines two semaphores as follows:
|
|
|
|
.. code-block:: console
|
|
|
|
% SEMA NAME
|
|
% ================
|
|
SEMA INPUT_DATA
|
|
SEMA WORK_DONE
|
|
|
|
A public semaphore can be referenced from any source file that includes
|
|
the file :file:`zephyr.h`.
|
|
|
|
|
|
Private Semaphore
|
|
-----------------
|
|
|
|
Define the semaphore a source file using the following syntax:
|
|
|
|
.. code-block:: c
|
|
|
|
DEFINE_SEMAPHORE(name);
|
|
|
|
For example, the following code defines a private semaphore named ``PRIV_SEM``.
|
|
|
|
.. code-block:: c
|
|
|
|
DEFINE_SEMAPHORE(PRIV_SEM);
|
|
|
|
To utilize this semaphore from a different source file use the following syntax:
|
|
|
|
.. code-block:: c
|
|
|
|
extern const ksem_t PRIV_SEM;
|
|
|
|
|
|
Example: Giving a Semaphore from a Task
|
|
=======================================
|
|
|
|
This code uses a semaphore to indicate that a unit of data
|
|
is available for processing by a consumer task.
|
|
|
|
.. code-block:: c
|
|
|
|
void producer_task(void)
|
|
{
|
|
/* save data item in a buffer */
|
|
...
|
|
|
|
/* notify task that an additional data item is available */
|
|
task_sem_give(INPUT_DATA);
|
|
|
|
...
|
|
}
|
|
|
|
.. _isr_gives_semaphore:
|
|
|
|
Example: Giving a Semaphore from an ISR
|
|
=======================================
|
|
|
|
This code uses a semaphore to indicate that a unit of data
|
|
is available for processing by a consumer task.
|
|
|
|
.. code-block:: c
|
|
|
|
/*
|
|
* reserve 2 command packets for semaphore updates
|
|
*
|
|
* note: this assumes that input data arrives at a rate that allows
|
|
* the microkernel server fiber to finish the semaphore give operation
|
|
* for data item "N" before the ISR begins working on data item "N+2"
|
|
* (i.e. data arrives in bursts of at most one unit)
|
|
*/
|
|
static CMD_PKT_SET_INSTANCE(cmd_packets, 2);
|
|
|
|
void input_data_interrupt_handler(void *arg)
|
|
{
|
|
/* save data item in a buffer */
|
|
...
|
|
|
|
/* notify task that an additional data item is available */
|
|
isr_sem_give(INPUT_DATA, &CMD_PKT_SET(cmd_packets));
|
|
|
|
...
|
|
}
|
|
|
|
Example: Taking a Semaphore with a Conditional Time-out
|
|
=======================================================
|
|
|
|
This code waits up to 500 ticks for a semaphore to be given,
|
|
and gives a warning if it is not obtained in that time.
|
|
|
|
.. code-block:: c
|
|
|
|
void consumer_task(void)
|
|
{
|
|
...
|
|
|
|
if (task_sem_take_wait_timeout(INPUT_DATA, 500) == RC_TIME) {
|
|
printf("Input data not available!");
|
|
} else {
|
|
/* extract saved data item from buffer and process it */
|
|
...
|
|
}
|
|
...
|
|
}
|
|
|
|
Example: Monitoring Multiple Semaphores at Once
|
|
===============================================
|
|
|
|
This code waits on two semaphores simultaneously, and then takes
|
|
action depending on which one was given.
|
|
|
|
.. code-block:: c
|
|
|
|
ksem_t my_sem_group[3] = { INPUT_DATA, WORK_DONE, ENDLIST };
|
|
|
|
void consumer_task(void)
|
|
{
|
|
ksem_t sem_id;
|
|
...
|
|
|
|
sem_id = task_sem_group_take_wait(my_sem_group);
|
|
if (sem_id == WORK_DONE) {
|
|
printf("Shutting down!");
|
|
return;
|
|
} else {
|
|
/* process input data */
|
|
...
|
|
}
|
|
...
|
|
}
|
|
|
|
Example: Giving Multiple Semaphores at Once
|
|
===========================================
|
|
|
|
This code uses a semaphore group to allow a controlling task to signal
|
|
the semaphores used by four other tasks in a single operation.
|
|
|
|
.. code-block:: c
|
|
|
|
ksem_t my_sem_group[5] = { SEM1, SEM2, SEM3, SEM4, ENDLIST };
|
|
|
|
void control_task(void)
|
|
{
|
|
...
|
|
task_semaphore_group_give(my_sem_group);
|
|
...
|
|
}
|
|
|
|
|
|
APIs
|
|
****
|
|
|
|
The following APIs for an individual semaphore are provided by microkernel.h.
|
|
|
|
+----------------------------------------+------------------------------------+
|
|
| Call | Description |
|
|
+========================================+====================================+
|
|
| :c:func:`isr_sem_give()` | Signal a semaphore from an ISR. |
|
|
+----------------------------------------+------------------------------------+
|
|
| :c:func:`fiber_sem_give()` | Signal a semaphore from a fiber. |
|
|
+----------------------------------------+------------------------------------+
|
|
| :c:func:`task_sem_give()` | Signal a semaphore from a task. |
|
|
+----------------------------------------+------------------------------------+
|
|
| :c:func:`task_sem_take()` | Test a semaphore without waiting. |
|
|
+----------------------------------------+------------------------------------+
|
|
| :c:func:`task_sem_take_wait()` | Wait on a semaphore. |
|
|
+----------------------------------------+------------------------------------+
|
|
| :c:func:`task_sem_take_wait_timeout()` | Wait on a semaphore for a |
|
|
| | specified time period. |
|
|
+----------------------------------------+------------------------------------+
|
|
| :c:func:`task_sem_reset()` | Sets the semaphore count to zero. |
|
|
+----------------------------------------+------------------------------------+
|
|
| :c:func:`task_sem_count_get()` | Read signal count for a semaphore. |
|
|
+----------------------------------------+------------------------------------+
|
|
|
|
|
|
The following APIs for semaphore groups are provided by microkernel.h.
|
|
|
|
+----------------------------------------------+------------------------------+
|
|
| Call | Description |
|
|
+==============================================+==============================+
|
|
| :c:func:`task_sem_group_give()` | Signal a set of semaphores. |
|
|
+----------------------------------------------+------------------------------+
|
|
| :c:func:`task_sem_group_take()` | Test a set of semaphores |
|
|
| | without waiting. |
|
|
+----------------------------------------------+------------------------------+
|
|
| :c:func:`task_sem_group_take_wait()` | Wait on a set of semaphores. |
|
|
+----------------------------------------------+------------------------------+
|
|
| :c:func:`task_sem_group_take_wait_timeout()` | Wait on a set of semaphores |
|
|
| | for a specified time period. |
|
|
+----------------------------------------------+------------------------------+
|
|
| :c:func:`task_sem_group_reset()` | Sets the semaphore count to |
|
|
| | to zero for a set of |
|
|
| | semaphores. |
|
|
+----------------------------------------------+------------------------------+
|