| ============================= |
| Counting Semaphore Interfaces |
| ============================= |
| |
| **Semaphores**. Semaphores are the basis for synchronization and mutual |
| exclusion in NuttX. NuttX supports POSIX semaphores. |
| |
| Semaphores are the preferred mechanism for gaining exclusive access to a |
| resource. sched_lock() and sched_unlock() can also be used for this |
| purpose. However, sched_lock() and sched_unlock() have other undesirable |
| side-effects in the operation of the system: sched_lock() also prevents |
| higher-priority tasks from running that do not depend upon the |
| semaphore-managed resource and, as a result, can adversely affect system |
| response times. |
| |
| Priority Inversion. Proper use of semaphores avoids the issues of |
| ``sched_lock()``. However, consider the following example: |
| |
| #. Some low-priority task, *Task C*, acquires a semaphore in order to |
| get exclusive access to a protected resource. |
| #. *Task C* is suspended to allow some high-priority task, |
| #. *Task A* attempts to acquire the semaphore held by *Task C* and gets |
| blocked until *Task C* relinquishes the semaphore. |
| #. *Task C* is allowed to execute again, but gets suspended by some |
| medium-priority *Task B*. |
| |
| At this point, the high-priority *Task A* cannot execute until *Task B* |
| (and possibly other medium-priority tasks) completes and until *Task C* |
| relinquishes the semaphore. In effect, the high-priority task, *Task A* |
| behaves as though it were lower in priority than the low-priority task, |
| *Task C*! This phenomenon is called *priority inversion*. |
| |
| Some operating systems avoid priority inversion by *automatically* |
| increasing the priority of the low-priority *Task C* (the operable |
| buzz-word for this behavior is *priority inheritance*). NuttX supports |
| this behavior, but only if ``CONFIG_PRIORITY_INHERITANCE`` is defined in |
| your OS configuration file. If ``CONFIG_PRIORITY_INHERITANCE`` is not |
| defined, then it is left to the designer to provide implementations that |
| will not suffer from priority inversion. The designer may, as examples: |
| |
| - Implement all tasks that need the semaphore-managed resources at the |
| same priority level, |
| - Boost the priority of the low-priority task before the semaphore is |
| acquired, or |
| - Use sched_lock() in the low-priority task. |
| |
| Priority Inheritance. As mentioned, NuttX does support *priority |
| inheritance* provided that ``CONFIG_PRIORITY_INHERITANCE`` is defined in |
| your OS configuration file. However, the implementation and |
| configuration of the priority inheritance feature is sufficiently |
| complex that more needs to be said. How can a feature that can be |
| described by a single, simple sentence require such a complex |
| implementation: |
| |
| - ``CONFIG_SEM_PREALLOCHOLDERS``. First of all, in NuttX priority |
| inheritance is implement on POSIX counting semaphores. The reason for |
| this is that these semaphores are the most primitive waiting |
| mechanism in NuttX; Most other waiting facilities are based on |
| semaphores. So if priority inheritance is implemented for POSIX |
| counting semaphores, then most NuttX waiting mechanisms will have |
| this capability. |
| |
| Complexity arises because counting semaphores can have numerous |
| holders of semaphore counts. Therefore, in order to implement |
| priority inheritance across all holders, then internal data |
| structures must be allocated to manage the various holders associated |
| with a semaphore. The setting ``CONFIG_SEM_PREALLOCHOLDERS`` defines |
| the maximum number of different threads (minus one per semaphore |
| instance) that can take counts on a semaphore with priority |
| inheritance support. This setting defines the size of a single pool |
| of pre-allocated structures. It may be set to zero if priority |
| inheritance is disabled OR if you are only using semaphores as |
| mutexes (only one holder) OR if no more than two threads participate |
| using a counting semaphore. |
| |
| The cost associated with setting ``CONFIG_SEM_PREALLOCHOLDERS`` is |
| slightly increased code size and around 6-12 bytes times the value of |
| ``CONFIG_SEM_PREALLOCHOLDERS``. |
| |
| - **Increased Susceptibility to Bad Thread Behavior**. These various |
| structures tie the semaphore implementation more tightly to the |
| behavior of the implementation. For examples, if a thread executes |
| while holding counts on a semaphore, or if a thread exits without |
| call ``sem_destroy()`` then. Or what if the thread with the boosted |
| priority re-prioritizes itself? The NuttX implement of priority |
| inheritance attempts to handle all of these types of corner cases, |
| but it is very likely that some are missed. The worst case result is |
| that memory could by stranded within the priority inheritance logic. |
| |
| Locking versus Signaling Semaphores. Semaphores (and mutexes) may be |
| used for many different purposes. One typical use is for mutual |
| exclusion and locking of resources: In this usage, the thread that needs |
| exclusive access to a resources takes the semaphore to get access to the |
| resource. The same thread subsequently releases the semaphore count when |
| it no longer needs exclusive access. Priority inheritance is intended |
| just for this usage case. |
| |
| In a different usage case, a semaphore may to be used to signal an |
| event: One thread A waits on a semaphore for an event to occur. When the |
| event occurs, another thread B will post the semaphore waking the |
| waiting thread A. This is a completely different usage model; notice |
| that in the mutual exclusion case, the same thread takes and posts the |
| semaphore. In the signaling case, one thread takes the semaphore and a |
| different thread posts the semaphore. Priority inheritance should |
| *never* be used in this signaling case. Subtle, strange behaviors may |
| result. |
| |
| Semaphore does not support priority inheritance by default. If you need to |
| use a semaphore as a mutex you need to change its default behavior. |
| |
| Each semaphore is assigned a default maximum value defined by SEM_VALUE_MAX. |
| If ``CONFIG_CUSTOM_SEMAPHORE_MAXVALUE`` is enabled, applications may override this limit. |
| In such cases, the function ``nxsem_setmaxvalue()`` can be used to specify a custom maximum value |
| for an individual semaphore. |
| |
| In user space, it is recommended to use pthread_mutex instead of |
| semaphore for resource protection |
| |
| When priority inheritance is enabled with |
| ``CONFIG_PRIORITY_INHERITANCE``, the default *protocol* for the |
| semaphore will be to use priority inheritance. For signaling semaphores, |
| priority inheritance must be explicitly disabled by calling |
| ```sem_setprotocol`` <#semsetprotocol>`__ with ``SEM_PRIO_NONE``. For |
| the case of pthread mutexes, |
| ```pthread_mutexattr_setprotocol`` <#pthreadmutexattrsetprotocol>`__ |
| with ``PTHREAD_PRIO_NONE``. |
| |
| This is discussed in much more detail on this `Wiki |
| page <https://cwiki.apache.org/confluence/display/NUTTX/Signaling+Semaphores+and+Priority+Inheritance>`__. |
| |
| **POSIX semaphore interfaces:** |
| |
| - :c:func:`sem_init` |
| - :c:func:`sem_destroy` |
| - :c:func:`sem_open` |
| - :c:func:`sem_close` |
| - :c:func:`sem_unlink` |
| - :c:func:`sem_wait` |
| - :c:func:`sem_timedwait` |
| - :c:func:`sem_trywait` |
| - :c:func:`sem_post` |
| - :c:func:`sem_getvalue` |
| - :c:func:`sem_getprotocol` |
| - :c:func:`sem_setprotocol` |
| |
| .. c:function:: int sem_init(sem_t *sem, int pshared, unsigned int value) |
| |
| Initializes the UN-NAMED semaphore sem. |
| Following a successful call to sem_init(), the semaphore may be used in |
| subsequent calls to sem_wait(), sem_post(), and sem_trywait(). The |
| semaphore remains usable until it is destroyed. |
| |
| Only ``sem`` itself may be used for performing synchronization. The |
| result of referring to copies of ``sem`` in calls to ``sem_wait()``, |
| ``sem_trywait()``, ``sem_post()``, and ``sem_destroy()``, is not |
| defined. |
| |
| :param sem: Semaphore to be initialized |
| :param pshared: Process sharing (not used) |
| :param value: Semaphore initialization value |
| |
| :return: 0 (``OK``), or -1 (``ERROR``) if unsuccessful. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. Differences from the full POSIX implementation include: |
| |
| - pshared is not used. |
| |
| .. c:function:: int sem_destroy(sem_t *sem) |
| |
| Used to destroy the un-named semaphore |
| indicated by ``sem``. Only a semaphore that was created using |
| ``sem_init()`` may be destroyed using ``sem_destroy()``. The effect of |
| calling ``sem_destroy()`` with a named semaphore is undefined. The |
| effect of subsequent use of the semaphore ``sem`` is undefined until |
| ``sem`` is re-initialized by another call to ``sem_init()``. |
| |
| The effect of destroying a semaphore upon which other tasks are |
| currently blocked is undefined. |
| |
| :param sem: Semaphore to be destroyed. |
| :return: 0 (``OK``), or -1 (``ERROR``) if unsuccessful. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. |
| |
| .. c:function:: sem_t *sem_open(const char *name, int oflag, ...) |
| |
| Establishes a connection between named |
| semaphores and a task. Following a call to sem_open() with the semaphore |
| name, the task may reference the semaphore associated with name using |
| the address returned by this call. The semaphore may be used in |
| subsequent calls to sem_wait(), sem_trywait(), and sem_post(). The |
| semaphore remains usable until the semaphore is closed by a successful |
| call to sem_close(). |
| |
| If a task makes multiple calls to sem_open() with the same name, then |
| the same semaphore address is returned (provided there have been no |
| calls to sem_unlink()). |
| |
| **Input Parameters:** |
| |
| :param name: Semaphore name |
| :param oflag: Semaphore creation options. This may one of the following |
| bit settings: |
| |
| - ``oflag`` = 0: Connect to the semaphore only if it already exists. |
| - ``oflag`` = O_CREAT: Connect to the semaphore if it exists, |
| otherwise create the semaphore. |
| - ``oflag`` = O_CREAT with O_EXCL (O_CREAT|O_EXCL): Create a new |
| semaphore unless one of this name already exists. |
| |
| :param ``...``: **Optional parameters**. NOTE: When the O_CREAT flag is specified, |
| POSIX requires that a third and fourth parameter be supplied: |
| |
| - ``mode``. The mode parameter is of type mode_t. This parameter is |
| required but not used in the present implementation. |
| - ``value``. The value parameter is type unsigned int. The semaphore |
| is created with an initial value of ``value``. Valid initial |
| values for semaphores must be less than or equal to |
| ``SEM_VALUE_MAX`` (defined in ``include/limits.h``). |
| |
| :return: A pointer to sem_t or ``SEM_FAILED`` if unsuccessful. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. Differences from the full POSIX implementation include: |
| |
| - Treatment of links/connections is highly simplified. It is just a |
| counting semaphore. |
| |
| .. c:function:: int sem_close(sem_t *sem) |
| |
| This function is called to indicate that the calling |
| task is finished with the specified named semaphore, sem. The |
| sem_close() deallocates any system resources allocated by the system for |
| this named semaphore. |
| |
| If the semaphore has not been removed with a call to sem_unlink(), then |
| sem_close() has no effect on the named semaphore. However, when the |
| named semaphore has been fully unlinked, the semaphore will vanish when |
| the last task closes it. |
| |
| Care must be taken to avoid risking the deletion of a semaphore that |
| another calling task has already locked. |
| |
| :param sem: Semaphore descriptor |
| :return: 0 (``OK``), or -1 (``ERROR``) if unsuccessful. |
| |
| **Assumptions/Limitations:** |
| |
| - Care must be taken to avoid deletion of a semaphore that another task |
| has already locked. |
| - sem_close() must not be called with an un-named semaphore. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. |
| |
| .. c:function:: int sem_unlink(const char *name) |
| |
| This function will remove the semaphore named by the |
| input name parameter. If one or more tasks have the semaphore named by |
| name open when sem_unlink() is called, destruction of the semaphore will |
| be postponed until all references have been destroyed by calls to |
| sem_close(). |
| |
| :param name: Semaphore name |
| :return: 0 (``OK``), or -1 (``ERROR``) if unsuccessful. |
| |
| **Assumptions/Limitations:** |
| |
| - Care must be taken to avoid deletion of a semaphore that another task |
| has already locked. |
| - sem_unlink() must not be called with an un-named semaphore. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. Differences from the full POSIX implementation include: |
| |
| - Treatment of links/connections is highly simplified. It is just a |
| counting semaphore. |
| - Calls to sem_open() to re-create or re-connect to the semaphore may |
| refer to the same semaphore; POSIX specifies that a new semaphore |
| with the same name should be created after sem_unlink() is called. |
| |
| .. c:function:: int sem_wait(sem_t *sem) |
| |
| This function attempts to lock the semaphore referenced |
| by sem. If the semaphore as already locked by another task, the calling |
| task will not return until it either successfully acquires the lock or |
| the call is interrupted by a signal. |
| |
| :param sem: Semaphore descriptor. |
| :return: 0 (``OK``), or -1 (``ERROR``) is unsuccessful |
| |
| If ``sem_wait`` returns -1 (``ERROR``) then the cause of the failure |
| will be indicated by the thread-specific ```errno`` <#ErrnoAccess>`__. |
| The following lists the possible values for |
| ```errno`` <#ErrnoAccess>`__: |
| |
| - ``EINVAL``: Indicates that the ``sem`` input parameter is not valid. |
| - ``EINTR``: Indicates that the wait was interrupt by a signal received |
| by this task. In this case, the semaphore has not be acquired. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. |
| |
| .. c:function:: int sem_timedwait(sem_t *sem, const struct timespec *abstime) |
| |
| This function will lock the semaphore referenced by sem |
| as in the ``sem_wait()`` function. However, if the semaphore cannot be |
| locked without waiting for another process or thread to unlock the |
| semaphore by performing a ``sem_post()`` function, this wait will be |
| terminated when the specified timeout expires. |
| |
| The timeout will expire when the absolute time specified by ``abstime`` |
| passes, as measured by the clock on which timeouts are based (that is, |
| when the value of that clock equals or exceeds abstime), or if the |
| absolute time specified by abstime has already been passed at the time |
| of the call. This function attempts to lock the semaphore referenced by |
| ``sem``. If the semaphore is already locked by another task, the calling |
| task will not return until it either successfully acquires the lock or |
| the call is interrupted by a signal. |
| |
| **Input Parameters:** |
| |
| :param sem: Semaphore descriptor. |
| :param abstime: The absolute time to wait until a timeout is declared. |
| :return: 0 (``OK``), or -1 (``ERROR``) is unsuccessful |
| |
| If ``sem_timedwait`` returns -1 (``ERROR``) then the cause of the |
| failure will be indicated by the thread-specific |
| ```errno`` <#ErrnoAccess>`__. The following lists the possible values |
| for ```errno`` <#ErrnoAccess>`__: |
| |
| ``EINVAL``: Indicates that the ``sem`` input parameter is not valid or |
| the thread would have blocked, and the abstime parameter specified a |
| nanoseconds field value less than zero or greater than or equal to 1000 |
| million. |
| |
| ``ETIMEDOUT``: The semaphore could not be locked before the specified |
| timeout expired. |
| |
| ``EDEADLK``: A deadlock condition was detected. |
| |
| ``EINTR``: Indicates that the wait was interrupt by a signal received by |
| this task. In this case, the semaphore has not be acquired. |
| |
| **POSIX Compatibility:** Derived from IEEE Std 1003.1d-1999. |
| |
| .. c:function:: int sem_trywait(sem_t *sem) |
| |
| This function locks the specified semaphore only if the |
| semaphore is currently not locked. In any event, the call returns |
| without blocking. |
| |
| :param sem: The semaphore descriptor |
| :return: 0 (``OK``) or -1 (``ERROR``) if unsuccessful |
| |
| If ``sem_trywait`` returns -1 (``ERROR``) then the cause of the failure |
| will be indicated by the thread-specific ```errno`` <#ErrnoAccess>`__. |
| The following lists the possible values for |
| ```errno`` <#ErrnoAccess>`__: |
| |
| - ``EINVAL``: Indicates that the ``sem`` input parameter is not valid. |
| - ``EAGAIN``: Indicates that the semaphore was not acquired. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. |
| |
| .. c:function:: int sem_post(sem_t *sem) |
| |
| When a task has finished with a semaphore, it will call |
| sem_post(). This function unlocks the semaphore referenced by ``sem`` by |
| performing the semaphore unlock operation. |
| |
| If the semaphore value resulting from this operation is positive, then |
| no tasks were blocked waiting for the semaphore to become unlocked; The |
| semaphore value is simply incremented. |
| |
| If the value of the semaphore resulting from this operation is zero, |
| then on of the tasks blocked waiting for the semaphore will be allowed |
| to return successfully from its call to ``sem_wait()``. |
| |
| .. note:: ``sem_post()`` may be called from an interrupt handler. |
| |
| :param sem: Semaphore descriptor |
| :return: 0 (``OK``) or -1 (``ERROR``) if unsuccessful. |
| |
| **Assumptions/Limitations:**. When called from an interrupt handler, it |
| will appear as though the interrupt task is the one that is performing |
| the unlock. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. |
| |
| .. c:function:: int sem_getvalue(sem_t *sem, int *sval) |
| |
| This function updates the location referenced by sval |
| argument to have the value of the semaphore referenced by sem without |
| effecting the state of the semaphore. The updated value represents the |
| actual semaphore value that occurred at some unspecified time during the |
| call, but may not reflect the actual value of the semaphore when it is |
| returned to the calling task. |
| |
| If sem is locked, the value return by sem_getvalue() will either be zero |
| or a negative number whose absolute value represents the number of tasks |
| waiting for the semaphore. |
| |
| :param sem: Semaphore descriptor |
| :param sval: Buffer by which the value is returned |
| |
| :return: 0 (``OK``) or -1 (``ERROR``) if unsuccessful. |
| |
| **POSIX Compatibility:** Comparable to the POSIX interface of the same |
| name. |
| |
| .. c:function:: int sem_getprotocol(FAR const pthread_mutexattr_t *attr, FAR int *protocol) |
| |
| Return the value of the semaphore protocol attribute. |
| |
| :param attr: A pointer to the semaphore to be queried |
| :param protocol: The user provided location in which to store the |
| protocol value. May be one of ``SEM_PRIO_NONE``, or |
| ``SEM_PRIO_INHERIT``, ``SEM_PRIO_PROTECT``. |
| :return: If successful, the ``sem_getprotocol()`` function will return zero |
| (``OK``). Otherwise, an -1 (``ERROR``) will be returned and the |
| ``errno`` value will be set to indicate the nature of the error. |
| |
| **POSIX Compatibility:** Non-standard NuttX interface. Should not be |
| used in portable code. Analogous to |
| ``pthread_muxtexattr_getprotocol()``. |
| |
| .. c:function:: int sem_setprotocol(FAR pthread_mutexattr_t *attr, int protocol) |
| |
| Set semaphore protocol attribute. See the paragraph |
| `Locking versus Signaling Semaphores <#lockingvssignaling>`__ for some |
| important information about the use of this interface. |
| |
| :param attr: A pointer to the semaphore to be modified |
| :param protocol: The new protocol to use. One of ``SEM_PRIO_NONE``, or |
| ``SEM_PRIO_INHERIT``, ``SEM_PRIO_PROTECT``. ``SEM_PRIO_INHERIT`` is |
| supported only if ``CONFIG_PRIORITY_INHERITANCE`` is defined; |
| ``SEM_PRIO_PROTECT`` is not currently supported in any configuration. |
| :return: If successful, the ``sem_setprotocol()`` function will return zero |
| (``OK``). Otherwise, an -1 (``ERROR``) will be returned and the |
| ``errno`` value will be set to indicate the nature of the error. |
| |
| **POSIX Compatibility:** Non-standard NuttX interface. Should not be |
| used in portable code. Analogous to |
| ``pthread_muxtexattr_setprotocol()``. |