This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 2532a79c13df898ed50978185430047b6f6f590c
Author: Ludovic Vanasse <ludovicvana...@gmail.com>
AuthorDate: Sun Oct 27 16:18:51 2024 -0400

    Doc: Migration Signaling Semaphores and priority inheritance
    
    Migrate
    
https://cwiki.apache.org/confluence/display/NUTTX/Signaling+Semaphores+and+Priority+Inheritance
    to official wiki
    
    Signed-off-by: Ludovic Vanasse <ludovicvana...@gmail.com>
---
 Documentation/guides/index.rst                     |   1 +
 .../guides/signaling_sem_priority_inheritance.rst  | 212 +++++++++++++++++++++
 2 files changed, 213 insertions(+)

diff --git a/Documentation/guides/index.rst b/Documentation/guides/index.rst
index 5286df48b3..b88788b225 100644
--- a/Documentation/guides/index.rst
+++ b/Documentation/guides/index.rst
@@ -53,3 +53,4 @@ Guides
   semihosting.rst
   renode.rst
   signal_events_interrupt_handlers.rst  
+  signaling_sem_priority_inheritance.rst
\ No newline at end of file
diff --git a/Documentation/guides/signaling_sem_priority_inheritance.rst 
b/Documentation/guides/signaling_sem_priority_inheritance.rst
new file mode 100644
index 0000000000..b1d37de500
--- /dev/null
+++ b/Documentation/guides/signaling_sem_priority_inheritance.rst
@@ -0,0 +1,212 @@
+=============================================
+Signaling Semaphores and Priority Inheritance
+=============================================
+
+.. warning:: Migrated from 
+    
https://cwiki.apache.org/confluence/display/NUTTX/Signaling+Semaphores+and+Priority+Inheritance
+
+Locking vs Signaling Semaphores
+===============================
+
+Locking Semaphores
+------------------
+POSIX counting semaphores have multiple uses. The typical usage is where 
+the semaphore is used as lock on one or more resources. In this typical 
+case, priority inheritance works perfectly: The holder of a semaphore 
+count must be remembered so that its priority can be boosted if a higher 
+priority task requires a count from the semaphore. It remains the 
+holder until the same task calls ``sem_post()`` to release the count on 
+the semaphore.
+
+Mutual Exclusion Example
+------------------------
+This usage is very common for providing mutual exclusion. The semaphore 
+is initialized to a value of one. The first task to take the semaphore 
+has access; additional tasks that need access will then block until 
+the first holder calls ``sem_post()`` to relinquish access:
+
++---------------------+--------------------+
+|     **TASK A**      |     **TASK B**     |
++=====================+====================+
+| `have access`       |                    |
++---------------------+--------------------+
+| `priority boost`    | **sem_wait(sem);** |
++---------------------+--------------------+
+| `priority restored` | `have access`      |
++---------------------+--------------------+
+| **sem_post(sem);**  |                    |
++---------------------+--------------------+
+| **sem_wait(sem);**  |                    |
++---------------------+--------------------+
+|                     | `blocked`          |
++---------------------+--------------------+
+
+The important thing to note is that ``sem_wait()`` and ``sem_post()`` both 
+called on the same thread, TASK A. When ``sem_wait()`` succeeds, TASK 
+A becomes the holder of the semaphore and, while it is the holder 
+of the semaphore (1) other threads, such as TASK B, cannot access 
+the protected resource and (2) the priority of TASK A may be modified 
+by the priority inheritance logic. TASK A remains the holder until 
+is calls ``sem_post()`` on the `same thread`. At that time, (1) its 
+priority may be restored and (2) TASK B has access to the resource.
+
+Signaling Semaphores
+--------------------
+But a very different usage model for semaphores is for signaling 
+events. In this case, the semaphore count is initialized to 
+zero and the receiving task calls ``sem_wait()`` to wait for the 
+next event of interest to occur. When an event of interest is 
+detected by another task (or even an interrupt handler), 
+``sem_post()`` is called which increments the count to 1 and 
+wakes up the receiving task.
+
+Signaling Semaphores and Priority Inheritance details
+=====================================================
+
+Example
+-------
+For example, in the following TASK A waits on a semaphore 
+for events and TASK B (or perhaps an interrupt handler) 
+signals task A of the occurrence of the events by posting 
+to that semaphore:
+
++--------------------------+--------------------+
+|        **TASK A**        |     **TASK B**     |
++==========================+====================+
+| **sem_init(sem, 0, 0);** |                    |
++--------------------------+--------------------+
+| **sem_wait(sem);**       |                    |
++--------------------------+--------------------+
+| `blocked`                |                    |
++--------------------------+--------------------+
+|                          | **sem_post(sem);** |
++--------------------------+--------------------+
+| `Awakens as holder`      |                    |
++--------------------------+--------------------+
+
+Notice that unlike the mutual exclusion case above, 
+``sem_wait()`` and ``sem_post()`` are called on `different` 
+threads.
+
+Usage in Drivers
+----------------
+
+This usage case is used often within drivers, for example, 
+when the user calls the ``read()`` method and there is no data 
+available. ``sem_wait()`` is called to wait for new data to be 
+received; ``sem_post()`` is called when the new data arrives 
+and the user task is re-awakened.
+
+Priority Inheritance Fails
+--------------------------
+
+These two usage models, the locking modeling and the 
+signaling model, are really very different and priority 
+inheritance simply does not apply when the semaphore is 
+used for signalling rather than locking. In this signaling 
+case priority inheritance can interfere with the operation 
+of the semaphore. The problem is that when TASK A is 
+awakened it is a holder of the semaphore. Normally, a 
+task is removed from the holder list when it finally 
+releases the semaphore via ``sem_post()``.
+
+In this case, TASK B calls ``sem_post(sem)`` but TASK B is 
+not the holder of the semaphore. Since TASK A never 
+calls ``sem_post(sem)`` it becomes a permanently a holder 
+of the semaphore and may have its priority boosted at 
+any time when any other task tries to acquire the 
+semaphore.
+
+Who's to Blame
+--------------
+
+In the POSIX case, priority inheritance is specified only 
+in the pthread mutex layer. In NuttX, on the other hand, 
+pthread mutexes are simply built on top of binary locking 
+semaphores. Hence, in NuttX, priority inheritance is 
+implemented in the semaphore layer.
+
+In the case of a mutex this could be simply resolved since 
+there is only one holder but for the case of counting 
+semaphores, there may be many holders and if the holder 
+is not the thread that calls ``sem_post()``, then it is not 
+possible to know which thread/holder should be released.
+
+Selecting the Semaphore Protocol
+================================
+
+``sem_setprotocol()``
+---------------------
+
+The fix is to call non-standard NuttX function 
+``sem_setprotocol(SEM_PRIO_NONE)`` immediately after the 
+``sem_init()``. The effect of this function call is to 
+disable priority inheritance for that specific 
+semaphore. There should then be no priority inheritance 
+operations on this semaphore that is used for signalling.
+
+.. code-block:: C
+
+    sem_t sem
+    // ...
+    sem_init(&sem, 0, 0);
+    sem_setprotocol(&sem, SEM_PRIO_NONE);
+
+Here is the rule: If you have priority inheritance 
+enabled and you use semaphores for signaling events, 
+then you `must` call ``sem_setprotocol(SEM_PRIO_NONE)`` 
+immediately after initializing the semaphore.
+
+
+Why Another Non-Standard OS Interface?
+--------------------------------------
+
+The non-standard ``sem_setprotocol()`` is the `moral` 
+`equivalent` of the POSIX ``pthread_mutexattr_setprotocol()`` 
+and its naming reflects that relationship. In most 
+implementations, priority inheritance is implemented 
+only in the pthread mutex layer. In NuttX, on the 
+other hand, pthread mutexes are simply built on top 
+of binary locking semaphores. Hence, in NuttX, 
+priority inheritance is implemented in the semaphore 
+layer. This architecture then requires an interface 
+like ``sem_setprotocol()`` in order to manage the protocol 
+of the underlying semaphore.
+
+
+``pthread_mutexattr_setprotocol()``
+-----------------------------------
+
+Since NuttX implements pthread mutexes on top of 
+binary semaphores, the above recommendation also 
+applies when pthread mutexes are used for inter-thread 
+signaling. That is, a mutex that is used for 
+signaling should be initialize like this (simplified, 
+no error checking here):
+
+.. code-block:: c
+
+    pthread_mutexattr_t attr;
+    pthread_mutex_t mutex;
+    // ...
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_PRIO_NONE);
+    pthread_mutex_init(&mutex, &attr);
+
+Is this Always a Problem?
+=========================
+
+Ideally ``sem_setprotocol(SEM_PRIO_NONE)`` should be 
+called for all signaling semaphores. But, no, 
+often the use of a signaling semaphore with priority 
+inversion is not a problem. It is not a problem 
+if the signaling semaphore is always taken on 
+the same thread. For example:
+
+* If the driver is used by only a single task, or
+* If the semaphore is only taken on the worker thread.
+
+But this can be a serious problem if multiple tasks 
+ever wait on the signaling semaphore. Drivers like 
+the serial driver, for example, have many user 
+threads that may call into the driver.
\ No newline at end of file

Reply via email to