Signaling Semaphores and Priority Inheritance

来源:互联网 发布:网络交易的特点 编辑:程序博客网 时间:2024/05/16 05:38
origin: http://nuttx.org/doku.php?id=wiki:howtos:signalling-semaphores

Locking vsSignaling Semaphores

Locking Semaphores.POSIX counting semaphores have multiple uses. The typical usageis where the semaphoreis used as lock on one or more resources. In this typical case, priority inheritance works perfectly: The holder of asemaphore count must be remembered so that its priority can be boosted if a higher priority task requires a count from thesemaphore. It remains the holder until the same task callssem_post() to release the count on the semaphore.

Mutual Exclusion Example.This usage is very common for providing mutual exclusion. Thesemaphore is initialized to a value of one. The first task to take thesemaphore has access; additional tasks that need access will then block until the first holder callssem_post() to relinquish access:

TASK ATASK Bsem_wait(sem); have access priority boostsem_wait(sem); blockedsem_post(sem); priority restoredhave access

The important thing to note is that sem_wait() and sem_post() both called on the same thread, TASK A. Whensem_wait() succeeds, TASK A becomes the holder of the semaphore and, while it is the holder of thesemaphore (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 untilis 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 modelfor semaphores isfor signaling events. In this case, thesemaphore count is initialized to zero and the receiving task callssem_wait() to wait for the next event of interest to occur. When an event of interestis 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

Example.For example, in the following TASK A waits on asemaphore for events and TASK B (or perhaps an interrupt handler) signals task A of the occurence of the events by posting to thatsemaphore:

TASK ATASK Bsem_init(sem, 0, 0); sem_wait(sem); blocked  sem_post(sem);Awakens as holder  

Notice that unlike the mutual exclusion case above, sem_wait() andsem_post() are called on diferent threads.

Usage in Drivers.This usage case isused often within drivers, for example, when the user calls the read() method and there is no data available. sem_wait() is called to waitfor new data to be received; sem_post()is called when the new data arrives and the user taskis re-awakened.

Priority Inheritance Fails.These two usage models, the locking modeling and thesignaling model, are really very different and priority inheritance simply does not apply when thesemaphore is used for signalling rather than locking. In thissignaling case priority inheritance can interfere with the operation of thesemaphore. The problem is that when TASK Ais awakened it is a holder of thesemaphore. 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 callssem_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 thesemaphore.

Who's to BlameIn 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 inheritanceis 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 holderis not the thread that calls sem_post(), then itis not possible to know which thread/holder should be released.

Selecting theSemaphore Protocol

sem_setprotocol().The fix is to call non-standard NuttX functionsem_setprotocol(SEM_PRIO_NONE) immediately after the sem_init(). The effect of this function callis to disable priority inheritance for that specific semaphore. There should then be no priority inheritance operations on thissemaphore that isused for signalling.

sem_t semsem_init(&sem, 0, 0);sem_setprotocol(&sem, SEM_PRIO_NONE);

Here is the rule: If you have priority inheritance enabled and you use semaphoresfor signaling events, then youmust call sem_setprotocol(SEM_PRIO_NONE) immediately after initializing thesemaphore.

Why Another Non-Standard OS Interface?The non-standard sem_setprotocol() is the moral equivalent of the POSIXpthread_mutexattr_setprotocol() and its naming reflects that relationship. In most implementations, priority inheritanceis 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 inheritanceis implemented in the semaphore layer. This architecture then requires an interface likesem_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 areused for inter-threadsignaling. That is, a mutex thatis used for signaling should be initialize like this (simplified, no error checking here):

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 asignaling semaphore with priority inversionis not a problem. It is not a problem if thesignaling semaphoreis always taken on the same thread. For example:

  • If the driver us used by only a single tasks, 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.


原创粉丝点击