zephyr/doc/kernel/microkernel/microkernel_semaphores.rst
Anas Nashif bcbea5dbc7 doc: remove doxygen index and add groups instead
Instead of loading all indexed doxygen symbols in one page, we
use the defined groups to load the API documentation in the specific
sections and reduce the trash coming from doxygen to just what we
need.

Change-Id: I030e3de33e8cc26871f95cd45a50af0cae1bb942
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
2016-02-05 20:15:58 -05:00

294 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 by name 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 |
+========================================+====================================+
| :cpp:func:`isr_sem_give()` | Signal a semaphore from an ISR. |
+----------------------------------------+------------------------------------+
| :cpp:func:`fiber_sem_give()` | Signal a semaphore from a fiber. |
+----------------------------------------+------------------------------------+
| :cpp: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. |
+----------------------------------------+------------------------------------+
| :cpp:func:`task_sem_reset()` | Sets the semaphore count to zero. |
+----------------------------------------+------------------------------------+
| :cpp:func:`task_sem_count_get()` | Read signal count for a semaphore. |
+----------------------------------------+------------------------------------+
The following APIs for semaphore groups are provided by microkernel.h.
+----------------------------------------------+------------------------------+
| Call | Description |
+==============================================+==============================+
| :cpp:func:`task_sem_group_give()` | Signal a set of semaphores. |
+----------------------------------------------+------------------------------+
| :cpp: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. |
+----------------------------------------------+------------------------------+
| :cpp:func:`task_sem_group_reset()` | Sets the semaphore count to |
| | to zero for a set of |
| | semaphores. |
+----------------------------------------------+------------------------------+