All,
In his Confluence paper on "Signaling Semaphores and Priority Inheritance”,
Brennan Ashton’s analysis is both thorough and accurate; and his conclusion that
There should then be no priority inheritance operations on this semaphore that
is used for signalling.
is unassailable.
It’s important to understand a few points here:
In any RTOS, priority inversion happens, and it’s OK, and a well-designed RTOS
can be configured to meet its timing constraints given CPU performance is
adequate for the application. Point: priority inversion is not intrinsically a
“bad thing,” it’s going to happen.
Point: The “bad thing” about priority inversion happens when it becomes
unbounded. There are plenty of papers on this, but summarily, unbounded
priority inversion happens when a thread blocks on a resource held by another
thread of a lower priority (so far this is normal priority inversion); and then
a third, unrelated thread, with an intermediate priority becomes ready (imagine
it decides to calculate pi). Now we have unbounded priority inversion (UPI)
and we no longer have a deterministic realtime system.
There are a number of solutions to the problem of UPI. In the case of NuttX, a
fixed-priority preemptive RTOS, one solution that both solves the problem and
minimizes performance penalty is priority inheritance. Point: priority
inheritance is a solution to the problem of unbounded priority inversion.
In the use case described by Brennan as “Signaling Semaphores”, the semaphore
is used as a signal to indicate some event has just occurred, i.e a serial
driver signaling an incoming character or keystroke. Since it’s not used to
protect a shared resource, there is no contention for a shared resource and
“priority inversion” is meaningless in this case (how would you boost the
priority of the device or person typing on the far end of a serial cable?).
Point: in the use case of a signaling semaphore, unbounded priority inversion
doesn’t happen because there’s no priority inversion to begin with.
Final Point: priority inheritance, in the case of Signaling Semaphores, is the
application of a solution to a problem that doesn’t exist; and in fact becomes
the problem.
My thoughts:
It seems to me that “Signaling Semaphores” are only used within the NuttX
kernel (is this correct?). For intertask communication POSIX pthread
mechanisms could and should be used.
The solution Brennan suggests is to initialize semaphores used as signaling
events as follows:
sem_init(&sem, 0, 0);
sem_setprotocol(&sem, SEM_PRIO_NONE);
this is, of course, correct, but retains the dual purpose of sem_wait() and
sem_post(). I believe this can be confusing and will continue to be a source
of subtle errors. I suggest going a step further and isolate the two use
cases. Let the current sem_init, sem_wait, sem_post be used only for the
resource locking use case.
For the signaling use case, create a new API for event signaling within the
kernel: nxev_init, nxev_wait, nxev_post where: nxev_init is simply:
sem_init(&nxev, 0, 0);
sem_setprotocol(&nxev, SEM_PRIO_NONE);
and:
#define nxev_wait sem_wait
#define nxev_post sem_post
In the case were PRIORITY_INHERITANCE is not configured, sem_setprotocol() does
nothing and the nxev_*() API is still used for event notification.
This may seem a trivial change, but having specific API function names for the
two specific use cases would, I believe, all but eliminate future confusion;
especially given that most people look to existing drivers to use as a
template. Finally, enabling or disabling PRIORITY_INHERITANCE would not
introduce the subtle error Brennan documented.
Cheers,
-david
> On Mar 30, 2023, at 6:49 AM, Gregory Nutt <[email protected]> wrote:
>
>
>>> 3. Move priority inheritance from nxsem to nxmutex, and optimize the
>>> performance of priority inheritance;
>>
>> That will break every a lot of people's code. There is probably a lot of
>> application logic that depends on priority inheritance working on counting
>> semaphores. Removing that will problems for a lot of people.
>>
>> This is, however, consistent with how Linux works.
>
> AFAIK there is nothing that prohibits semaphores from supporting priority
> inheritance -- other than the fact that there is no "owner" of a semaphore as
> there is for a mutex. Priority inheritance is very important for the correct
> behavior of some real time systems so we were able to use priority
> inheritance with counting semaphores by adding some non-standard interfaces
> to control whether a semaphore supports priority inheritance or not by its
> usage:
> https://cwiki.apache.org/confluence/display/NUTTX/Signaling+Semaphores+and+Priority+Inheritance
>
> I have mixed feelings myself and hope that we get some consensus through
> dialog. One one hand, it is important to stay faithful to documented
> standard and undocumented conventions for the use the a POSIX/Unix systems.
> But on the other hand, unlike other OSs that strive toward standard
> conformance, we are an RTOS and must satisfy certain requirements for
> deterministic, real time behavior.
>
> What do you all think?
>