blob: 04fbff62bc73d38eefb9ba23edff70ac255bd5f9 [file] [log] [blame]
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 :c:func:`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 :c:func:`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
:c:func:`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 :c:func:`os_sem_release()`) and the consumer consumes them by calling
:c:func:`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
:c:func:`os_sem_pend()` subtracts exactly one token and each call to
:c:func:`os_sem_release()` adds exactly one token.
API
----
.. doxygengroup:: OSSem
:content-only:
:members: