|A fundamental requirement of a competent multitasking system is a flexible means to detect occurrence of an event and then to synchronize a task with that event (external or internal; synchronous or asynchronous). The requirement is usually met by either semaphores or event flags. Whichever object type is used (and some RTOSes support both) their purposes are identical: synchronize a task as near as possible to the actual time of the event’s occurrence.
Semaphores and event flags must have the inherent capability to capture and retain information about an event’s occurrence since the system may be otherwise occupied when the event happened. This is known as event memory. The methodology for this event memory constitutes the primary difference between semaphores and event flags.
The term “semaphore” is derived from the nautical use of signaling flags to spell out messages in which the message’s characters are based on the position of the two flags, the semaphores. Except for the concept of signaling, the analogy ends there. In the world of computing, there are many variations to how semaphores are designed and operate, especially in conjunction with real-time operating systems.
The most commonly used semaphore model is one that has a counter and a set of tasks that are waiting on the associated event. This is the counting semaphore model and it is found in most commercial RTOS products today. The count element of the semaphore serves as the event memory. In its basic implementation, the semaphore’s count increments each time its associated event occurs and decrements it each time a task tests it when its count is greater than zero. A count greater than 0, means events have happened for which tasks have not yet tested. A count of 0 means that no events have occurred since the last time the semaphore was tested.
The set of waiter tasks is a record of those tasks that are waiting for the event to occur before proceeding further. Of course, it is not necessary to wait on an event if it has not occurred at the time of the test. It is certainly possible for a task to poll a semaphore and to continue processing depending on whether the associated event has or has not happened. That’s not usually what is considered good design, so let’s put it aside and focus on how the semaphore would normally be used.
When the counting semaphore is initialized, its count element and the set of waiting tasks are cleared to null states. If a task tests the semaphore associated with an event and it has not yet occurred, its count isn’t changed but the task becomes blocked and is added to the set of waiters. When the event eventually occurs, the semaphore’s count is incremented by 1. And if it has waiters, one or more of them can be resumed, depending on the design of the system. But regardless of the manner in which the system releases waiter tasks, the semaphore’s count would be reduced by 1. In the RTXC Quadros kernel, the application developer may choose to have waiting tasks released from the semaphore’s set of waiting tasks in priority order or all at once.
If, on the other hand, the event occurs before a task has come around to testing for it, the count element is incremented by one, setting the semaphore’s event memory. When the next test of the semaphore occurs, the operating system reduces the semaphore’s count by 1 and allows the testing task to continue without loss of CPU control.
A semaphore may be organized in various ways and takes various amounts of RAM, depending on that organization. Some counting semaphores can be organized as a single byte, others can require many more. Regardless of the organization, the counting semaphores’ can accurately maintain the count of multiple occurrences of the associated event without losing even one. This is one of the reasons the RTXC Quadros kernel uses counting semaphores as the primary mechanism for synchronizing tasks with those events.
Variations on this basic semaphore permit multiple semaphores to be signaled when one event occurs. Likewise, one task can synchronize with any one of a set of events to occur by waiting on their respective semaphores, producing a logical OR condition. There are other types of semaphore testing options. One is to have the testing task blocked for a certain number of ticks (instead of an indeterminate wait). In RTXC Quadros kernel, it is permissible to set a semaphore’s count to a negative value in order to have tasks wait for multiple occurrences of the associated event before being triggered.
The RTXC Quadros RTOS also allows semaphores to be associated with events in other classes. For instance, a semaphore can be associated with an alarm’s expiration or an empty queue receiving an entry. This provides additional synchronizing flexibility that allows disparate objects to be logically combined so that the developer can trigger a task to start at some determined period when any one of them occurs.