zephyr/doc/kernel/microkernel/microkernel_semaphores.rst
L.S. Cook 6a1d23f4f9 doc: Edit microkernel_semaphores.rst for consist structure and APIs.
To have consistency throughout the docs, APIs should all be in
imperative verb. Updating this, and adding other consistent structure
to match the rest of microkernel section docs.

Change-Id: Ic2285496895ae9edfcc523f8fc2f99bcb935227f
Signed-off-by: L.S. Cook <leonax.cook@intel.com>
2016-03-03 09:49:24 +00:00

230 lines
6.0 KiB
ReStructuredText

.. _microkernel_semaphores:
Semaphores
##########
Concepts
********
The microkernel's :dfn:`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 when it has a count of zero; this makes
it unavailable.
Semaphores may be given by tasks, fibers, or ISRs.
Semaphores may be taken by tasks only. A task that attempts to take an unavailable
semaphore may wait for the semaphore to be given. Any number of tasks may wait on
an unavailable semaphore simultaneously; and 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 with 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 with 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 in 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 reference 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);
...
}
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(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(my_sem_group, TICKS_UNLIMITED);
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
****
All of the following APIs are provided by :file:`microkernel.h`:
APIs for an individual semaphore
================================
:cpp:func:`isr_sem_give()`
Give a semaphore (from an ISR).
:cpp:func:`fiber_sem_give()`
Give a semaphore (from a fiber).
:cpp:func:`task_sem_give()`
Give a semaphore.
:cpp:func:`task_sem_take()`
Take a semaphore, with time limited waiting.
:cpp:func:`task_sem_reset()`
Set the semaphore count to zero.
:cpp:func:`task_sem_count_get()`
Read the count for a semaphore.
APIs for semaphore groups
=========================
:cpp:func:`task_sem_group_give()`
Give each semaphore in a group.
:cpp:func:`task_sem_group_take()`
Wait up to a specified time period for a semaphore from a group.
:cpp:func:`task_sem_group_reset()`
Set the count to zero for each semaphore in a group.