Semaphore

A semaphore is a structure used for gaining exclusive access (much like a mutex), synchronizing task operations and/or use in a “producer/consumer” roles. Semaphores like the ones used by the myNewt OS are called “counting” semaphores as they are allowed to have more than one token (explained below).

Description

A semaphore is a fairly simple construct consisting of a queue for waiting tasks and the number of tokens currently owned by the semaphore. A semaphore can be obtained as long as there are tokens in the semaphore. Any task can add tokens to the semaphore and any task can request the semaphore, thereby removing tokens. When creating the semaphore, the initial number of tokens can be set as well.

When used for exclusive access to a shared resource the semaphore only needs a single token. In this case, a single task “creates” the semaphore by calling os_sem_init() with a value of one (1) for the token. When a task desires exclusive access to the shared resource it requests the semaphore by calling os_sem_pend(). If there is a token the requesting task will acquire the semaphore and continue operation. If no tokens are available the task will be put to sleep until there is a token. A common “problem” with using a semaphore for exclusive access is called priority inversion. Consider the following scenario: a high and low priority task both share a resource which is locked using a semaphore. If the low priority task obtains the semaphore and then the high priority task requests the semaphore, the high priority task is now blocked until the low priority task releases the semaphore. Now suppose that there are tasks between the low priority task and the high priority task that want to run. These tasks will preempt the low priority task which owns the semaphore. Thus, the high priority task is blocked waiting for the low priority task to finish using the semaphore but the low priority task cannot run since other tasks are running. Thus, the high priority tasks is “inverted” in priority; in effect running at a much lower priority as normally it would preempt the other (lower priority) tasks. If this is an issue a mutex should be used instead of a semaphore.

Semaphores can also be used for task synchronization. A simple example of this would be the following. A task creates a semaphore and initializes it with no tokens. The task then waits on the semaphore, and since there are no tokens, the task is put to sleep. When other tasks want to wake up the sleeping task they simply add a token by calling os_sem_release(). This will cause the sleeping task to wake up (instantly if no other higher priority tasks want to run).

The other common use of a counting semaphore is in what is commonly called a “producer/consumer” relationship. The producer adds tokens (by calling os_sem_release()) and the consumer consumes them by calling os_sem_pend(). In this relationship, the producer has work for the consumer to do. Each token added to the semaphore will cause the consumer to do whatever work is required. A simple example could be the following: every time a button is pressed there is some work to do (ring a bell). Each button press causes the producer to add a token. Each token consumed rings the bell. There will exactly the same number of bell rings as there are button presses. In other words, each call to os_sem_pend() subtracts exactly one token and each call to os_sem_release() adds exactly one token.

API

os_error_t os_sem_init(struct os_sem *sem, uint16_t tokens)

Initialize a semaphore.

Return

os_error_t OS_INVALID_PARM Semaphore passed in was NULL. OS_OK no error.

Parameters
  • sem: Pointer to semaphore

  • tokens: # of tokens the semaphore should contain initially.

os_error_t os_sem_release(struct os_sem *sem)

Release a semaphore.

Return

os_error_t OS_INVALID_PARM Semaphore passed in was NULL. OS_OK No error

Parameters
  • sem: Pointer to the semaphore to be released

os_error_t os_sem_pend(struct os_sem *sem, os_time_t timeout)

Pend (wait) for a semaphore.

Return

os_error_t OS_INVALID_PARM Semaphore passed in was NULL. OS_TIMEOUT Semaphore was owned by another task and timeout=0 OS_OK no error.

Parameters
  • sem: Pointer to semaphore.

  • timeout: Timeout, in os ticks. A timeout of 0 means do not wait if not available. A timeout of OS_TIMEOUT_NEVER means wait forever.

static inline uint16_t os_sem_get_count(struct os_sem *sem)

Get current semaphore’s count.

struct os_sem
#include <os_sem.h>

Structure representing an OS semaphore.

Public Functions

SLIST_HEAD (, os_task) sem_head

Semaphore head.

Public Members

uint16_t _pad

Padding for alignment.

uint16_t sem_tokens

Number of tokens.