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