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