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).
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
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
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.
os_sem_init(struct os_sem * sem, uint16_t tokens)¶
Initialize a semaphore.
- os_error_t OS_INVALID_PARM Semaphore passed in was NULL. OS_OK no error.
sem: Pointer to semaphore tokens: # of tokens the semaphore should contain initially.
os_sem_release(struct os_sem * sem)¶
Release a semaphore.
- os_error_t OS_INVALID_PARM Semaphore passed in was NULL. OS_OK No error
sem: Pointer to the semaphore to be released
os_sem_pend(struct os_sem * sem, os_time_t timeout)¶
os sem pend
Pend (wait) for a semaphore.
- 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.
mu: 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.