.. _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. | +----------------------------------------------+------------------------------+