Signal Sets
Within every embedded software application, certain events occur within the system that requires the application to respond. In a
traditional non-os application, this would be achieved by a super forever loop that continually searches (polling) for events
that have occurred. Although this approach is simple to code, it quickly becomes inefficient and difficult to follow and manage as an
application handles more and more events. Signals are the elegant and efficient way to handle internal and external system events.
Each signal set has the ability to receive up to 32 unique signals (SIGNAL_0 to SIGNAL_31). Any code, from any context,
including interrupts, can set signals within a signal set. A thread can control how the signals are received and what actions are taken when a
signal is received.
Waiting for a Signal
A powerful component of a signal is the way that a thread can block and wait for one or more signals to arrive. This functionality
gives the application a way of not wasting any CPU cycles until there is actual work for the thread to perform. Utilizing this ability
produces the most efficient and responsive applications possible.
A thread can block and wait for a single signal using SIGNAL_WaitOne() or it can wait for multiple signals using
SIGNAL_Wait(). Both calls allow the thread to specify a timeout interval so that it can continue on in the event the signal(s) do not
arrive. The thread can also specify a zero timeout interval to receive the signals without actually blocking or waiting.
Sending and Consuming Signals
When a signal set is initialized, each of it's 32 possible signals are initialized to a cleared state. At anytime, an application can send one or
more signals to a set using
SIGNAL_Set(). When a signal is set, a thread
may or may not be waiting for the signal. If a thread happens to be waiting for the signal, the signal will immediately be consumed (reset to cleared state)
and the waiting thread will be released. If no thread happens to be waiting for the signal, the signal will go into the pending state. If
a thread were to enter a wait upon a pending signal, the wait call will return without blocking and the signal will then be
consumed (reset to the cleared state). Since the signals support going into a pending state, it does not matter if the signal was sent just
before, or just after a wait call, thus removing any potential for race conditions.
A signal can be manually cleared using
SIGNAL_Clear(). This can be useful when it is necessary for
logic to ignore all signals that have been sent prior to a specific point in time. The signals can be queried, without changing their state, using
SIGNAL_Query().
Basic Usage
The code snippet below shows an event or ISR that signals a waiting thread. The waiting thread is blocked, not consuming any CPU
cycles, until the event or ISR occurs.
#include "Kernel/kernel_signal.h"
#include <assert.h>
static SIGNALSET sigset; /* Allocate a signal set */
/* An example thread function */
void APP_Thread(void* arg)
{
STATUS status;
SIGNAL_Init(&signals); /* Initialize the signal set */
for (;;) { /* Thread forever loop */
status = SIGNAL_WaitOne(&sigset, /* Wait for single signal */
SIGNAL_0, /* Wait for signal number 0 */
INFINITE); /* Wait indefinitely */
if (status == SUCCESS) { /* Was the signal received? */
/* The event or ISR has occurred */
}
}
}
/* An example event or ISR */
void APP_EventOrISR(void)
{
STATUS status;
status = SIGNAL_Set(&signals, SIGNAL_0); /* Set the signal for the waiting thread */
assert(status == SUCCESS);
}
The code snippet below shows a thread waiting until either signal number 0 or signal number 1 arrives. The snippet uses the returned signals
from the wait call to determine which of the signals has caused the wait call to exit.
/* An example thread function */
void APP_Thread(void* arg)
{
STATUS status;
UINT32 signals;
for (;;) {
status = SIGNAL_Wait(&sigset, /* Wait for multiple signals */
OPT_WAITALL, /* Wait until ALL of the signals arrive */
SIGNAL_0 | SIGNAL_1, /* Wait for both signal 0 and 1 */
&signals, /* A value to accept the received signals */
INFINITE); /* Wait indefinitely */
if (status == SUCCESS) { /* Did all of the signals arrive? */
/* Both signals have arrived */
}
}
}
Advanced Usage
The kernel allows for complex signaling scenarios. A code snippet below shows a more complex scenario where a thread is waiting
for several signals to arrive.
/* An example thread function */
void APP_Thread(void* arg)
{
STATUS status;
UINT32 signals;
for (;;) {
status = SIGNAL_Wait(&sigset, /* Wait for multiple signals */
OPT_WAITANY, /* Wait until any signals arrive (set) */
SIGNAL_0 | SIGNAL_1 | SIGNAL_2, /* Signal numbers 0, 1, and 2 */
&signals, /* Receive the state of the signals */
10); /* Wait for up to 10 ticks */
if (status == SUCCESS) { /* Was at least one signal received? */
if (signals & SIGNAL_0) {
/* Signal number 0 received */
}
if (signals & SIGNAL_1) {
/* Signal number 1 received */
}
if (signals & SIGNAL_2) {
/* Signal number 2 received */
}
}
}
}
A single thread can have multiple wait statements that work with independent signals without interfering with each other. This is
particularly useful since it allows the thread's wait statements to occur in a different order than which the signals are sent to the
thread.
The code snippet below shows a thread that calls upon a sub-function that is performing it's own logic with an independent signal.
/* An example thread function */
void APP_Thread(void* arg)
{
STATUS status;
UINT32 signals;
for (;;) { /* Thread forever loop */
APP_SubFunction(); /* Call on some sub-function */
/* The clear signal call is used to reset the signal
in the event that the signal was sent while running
the sub function above. This is not mandatory, but
gives the ability to create a window in time of which
the signal must be received. */
status = SIGNAL_Clear(&sigset, SIGNAL_0); /* Discard any previously received #0 signals */
if (status == SUCCESS) {
status = SIGNAL_WaitOne(&sigset,
SIGNAL_0, /* Wait for signal number 0 */
10); /* Only wait for up to 10 ticks */
if (status == SUCCESS) { /* Was the signal received? */
/* Event 0 has occurred */
}
}
}
}
/* A sub-function that uses another signal */
void APP_SubFunction(void)
{
STATUS status;
status = SIGNAL_WaitOne(&sigset,
SIGNAL_1, /* Wait for signal number 1 */
20); /* Only wait for up to 20 ticks */
if (status == SUCCESS) { /* Was the signal received? */
/* Event 1 has occurred */
}
}
/* An example event handler for Event 0 */
void APP_OnEvent0(void)
{
SIGNAL_Set(&sigset, SIGNAL_0);
}
/* An example event handler for Event 1 */
void APP_OnEvent1(void)
{
SIGNAL_Set(&sigset, SIGNAL_1);
}
Limitations
Only one thread at a time can block and wait upon a signal set.
API Reference
STATUS SIGNAL_Init(SIGNALSET* set)
Initializes a signal set.
PARAMETERS
set | A pointer to the signal set to be initialized. |
SUCCESS | The given signal set has been initialized. |
ERR_NULLREFERENCE | The argument 'set' was found to be NULL. |
STATUS SIGNAL_Query(const SIGNALSET* set, UINT32* signals)
Returns the state of the signals for a signal set. The signals are returned as a bit-field of signals where each bit number
corresponds to the signal number. A bit value of '0' indicates the particular signal is cleared; while a bit value of '1' indicates
the signal is currently pending.
PARAMETERS
set | A pointer to the signal set that contains the signals to be queried. |
signals | A pointer to a caller allocated value to accept the returned state of the caller's signals. |
SUCCESS | The state of the signals have been returned. |
ERR_NULLREFERENCE | The argument 'set' or 'signals' was found to be NULL. |
STATUS SIGNAL_Clear(SIGNALSET* set, UINT32 signals)
Clears one or more signals within a signal set.
PARAMETERS
set | A pointer to the signal set that contains the signals to be cleared. |
signals | The signal(s) to be cleared. Use a bitwise OR to clear multiple signals. e.g. (SIGNAL_0 | SIGNAL_1) to clear
both signal number 0 and signal number 1. |
SUCCESS | The specified signals have been cleared. |
ERR_NULLREFERENCE | The argument 'set' was found to be a NULL reference. |
STATUS SIGNAL_Set(SIGNALSET* set, UINT32 signals)
Sets one or more signals within a signal set. If a thread happens to be waiting for the signal(s), the signal(s) will be consumed and
reset back to the cleared state and the waiting thread will be released. If no thread is waiting for the signal(s), the signal(s)
will go into the pending state.
PARAMETERS
set | A pointer to the signal set to receive the signal(s). |
signals | One or more signals to be sent to the signal set. Use a bitwise OR to send multiple signals, e.g.
(SIGNAL_0 | SIGNAL_1). |
SUCCESS | The signals were successfully set within the signal set. |
ERR_NULLREFERENCE | The argument 'set' was found to be NULL. |
STATUS SIGNAL_WaitOne(SIGNALSET* set, UINT32 signal, UINT32 timeout)
Waits for a single signal to be received by a signal set. If the specified signal is already in the pending state, this will
return immediately without blocking. If the signal is successfully received, the signal is consumed and reset back to the cleared
state.
PARAMETERS
set | A pointer to the signal set to wait upon to receive the signal. |
signal | The signal to wait on to be received. |
timeout | The maximum amount of time, in kernel ticks, to wait for the signal to be received. Use '0' to return
immediately without blocking and use INFINITE to wait indefinitely. |
SUCCESS | The specified signal has been received. |
ERR_NULLREFERENCE | The argument 'set' was found to be a NULL reference. |
ERR_INVALIDCONTEXT | The operation is not supported from the context of an interrupt service routine (ISR). |
ERR_INVALIDARGUMENT | An invalid argument was found. Only a single signal can be specified. |
ERR_TIMEOUT | The maximum allowable time has elapsed prior to the specified signal being received. |
STATUS SIGNAL_Wait(SIGNALSET* set, UINT32 opt, UINT32 mask, UINT32* signals, UINT32 timeout)
Waits for multiple signals to be received by a signal set. If the specified signal(s) are already in the pending state, this
will return immediately without blocking. If the signal(s) are successfully received, the signal(s) are consumed and reset back to
the cleared state.
PARAMETERS
set | A pointer to the signal set to wait upon to receive the signal. |
opt | An option for waiting.
OPT_WAITANY | Wait for ANY of the specified signals to arrive. |
OPT_WAITALL | Wait for ALL of the specified signals to arrive. |
|
mask | The signal(s) to wait on to be received. Use bitwise OR for multiple signals, e.g. (SIGNAL_0 | SIGNAL_1). |
signals | A pointer to a caller allocated value to receive the state of the signals when the wait operation completed. Can be NULL if don't care. |
timeout | The maximum amount of time, in kernel ticks, to wait for any of the specified signals to be received. Use '0'
to return immediately without blocking and use INFINITE to wait indefinitely. |
SUCCESS | If OPT_WAITANY, at least one, otherwise all signal(s) have been received. |
ERR_NULLREFERENCE | The argument 'set' was found to be a NULL reference. |
ERR_INVALIDCONTEXT | The operation is not supported from the context of an interrupt service routine (ISR). |
ERR_TIMEOUT | The maximum allowable time has elapsed prior to the specified signals being received. |