Queues in Nucleus SE: Introduction and Core Service Calls
View the RTOS Revealed series
Queues were introduced in an earlier article. They provide a more flexible means of passing simple messages between tasks than mailboxes.
Using Queues
In Nucleus SE, queues are configured at build time. A maximum of 16 queues may be defined for an application. If no queues are configured, the RTOS omits all queue‑related data structures and API code, keeping the binary lean.
A queue is simply an array of storage slots, each large enough to hold a single ADDR data item. Access is synchronised so that multiple tasks can safely enqueue and dequeue messages. Tasks can write to a queue until all slots are full, and read from a queue in a first‑in, first‑out (FIFO) order. Attempts to send to a full queue or receive from an empty one trigger an error or a task suspension, depending on the API options selected and the Nucleus SE configuration.
Queues vs. Pipes
Nucleus SE also supports pipes, which differ primarily in message size. While a queue transmits a single ADDR (typically a pointer), a pipe carries a variable‑length block of bytes; the block size is fixed at configuration time. Pipes are discussed in depth in a future article.
Configuring Queues
Number of Queues
Queue configuration is controlled by #define statements in nuse_config.h. The key setting is NUSE_QUEUE_NUMBER, which determines how many queues are enabled. The default is 0 (no queues). You may set it to any value from 0 to 16; an out‑of‑range value triggers a compile‑time #error produced by nuse_config_check.h.
Setting NUSE_QUEUE_NUMBER to a non‑zero value activates the queue subsystem, allocating the necessary data structures and enabling the associated API calls.
API Enables
Each queue API function has an enabling #define symbol in nuse_config.h:
NUSE_QUEUE_SEND
NUSE_QUEUE_RECEIVE
NUSE_QUEUE_JAM
NUSE_QUEUE_RESET
NUSE_QUEUE_INFORMATION
NUSE_QUEUE_COUNT
By default all are set to FALSE, disabling the corresponding service calls. To use a particular API, set its symbol to TRUE. Enabling an API while no queues are configured (except NUSE_Queue_Count(), which is always allowed) results in a compile‑time error. If an API is called in code that has not been enabled, a link‑time error occurs because the implementation is omitted.
#define NUSE_QUEUE_NUMBER 0 /* Number of queues in the system – 0‑16 */
/* Service call enablers */
#define NUSE_QUEUE_SEND FALSE
#define NUSE_QUEUE_RECEIVE FALSE
#define NUSE_QUEUE_JAM FALSE
#define NUSE_QUEUE_RESET FALSE
#define NUSE_QUEUE_INFORMATION FALSE
#define NUSE_QUEUE_COUNT FALSE
Queue Service Calls
Nucleus RTOS offers ten queue‑related service calls, of which six are implemented in Nucleus SE. They provide the following functionality:
- Send a message – NUSE_Queue_Send()
- Receive a message – NUSE_Queue_Receive()
- Jam a message to the front – NUSE_Queue_Jam()
- Reset a queue – NUSE_Queue_Reset()
- Query queue information – NUSE_Queue_Information()
- Get the number of configured queues – NUSE_Queue_Count()
- Remaining calls (create, delete, enumerate, broadcast) are not available in Nucleus SE.
The implementation of each API is discussed in detail in the following sections.
Writing and Reading Services
The core operations on a queue are writing (sending) and reading (receiving). Writing to the front of a queue is called jamming. Nucleus RTOS and Nucleus SE each expose three basic API calls for these actions.
Writing to a Queue
The RTOS API NU_Send_To_Queue() is highly flexible, allowing indefinite suspension or a timeout if the queue is full. Nucleus SE offers the same function but suspending is optional and timeouts are not supported. RTOS also supports a broadcast operation, which is unavailable in SE.
RTOS API: NU_Send_To_Queue()
Prototype:
STATUS NU_Send_To_Queue(NU_QUEUE *queue, VOID *message,
UNSIGNED size, UNSIGNED suspend);
Parameters:
- queue – pointer to the user‑supplied queue control block
- message – pointer to the data to send
- size – number of UNSIGNED elements; must match the queue’s message size
- suspend – NU_NO_SUSPEND, NU_SUSPEND, or a timeout value
Return codes include NU_SUCCESS, NU_QUEUE_FULL, NU_TIMEOUT, and various error codes for invalid pointers or queue state.
SE API: NUSE_Queue_Send()
Prototype:
STATUS NUSE_Queue_Send(NUSE_QUEUE queue, ADDR *message,
U8 suspend);
Parameters:
- queue – queue index (ID)
- message – pointer to the ADDR value to enqueue
- suspend – NUSE_NO_SUSPEND or NUSE_SUSPEND
Return values mirror the RTOS function but reflect SE’s limited blocking behaviour.
Implementation Overview
When blocking is disabled, the routine simply checks for space and either writes the value or returns NUSE_QUEUE_FULL. With blocking enabled, the function loops, suspending the task if the queue is full until space becomes available or a reset occurs. The implementation uses NUSE_Queue_Blocking_Count to track blocked tasks and NUSE_Task_Blocking_Return to resume them once a slot opens.
if (NUSE_Queue_Items[queue] == NUSE_Queue_Size[queue]) {
return_value = NUSE_QUEUE_FULL;
} else {
NUSE_Queue_Data[queue][NUSE_Queue_Head[queue]++] = *message;
if (NUSE_Queue_Head[queue] == NUSE_Queue_Size[queue])
NUSE_Queue_Head[queue] = 0;
NUSE_Queue_Items[queue]++;
return_value = NUSE_SUCCESS;
}
do {
if (NUSE_Queue_Items[queue] == NUSE_Queue_Size[queue]) {
if (suspend == NUSE_NO_SUSPEND) {
return_value = NUSE_QUEUE_FULL;
} else {
NUSE_Queue_Blocking_Count[queue]++;
NUSE_Suspend_Task(NUSE_Task_Active, (queue << 4) | NUSE_QUEUE_SUSPEND);
return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active];
if (return_value != NUSE_SUCCESS)
suspend = NUSE_NO_SUSPEND;
}
} else {
NUSE_Queue_Data[queue][NUSE_Queue_Head[queue]++] = *message;
if (NUSE_Queue_Head[queue] == NUSE_Queue_Size[queue])
NUSE_Queue_Head[queue] = 0;
NUSE_Queue_Items[queue]++;
if (NUSE_Queue_Blocking_Count[queue] != 0) {
U8 index;
NUSE_Queue_Blocking_Count[queue]--;
for (index = 0; index < NUSE_TASK_NUMBER; index++) {
if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND)
&& (HINIB(NUSE_Task_Status[index]) == queue)) {
NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS;
NUSE_Wake_Task(index);
break;
}
}
}
return_value = NUSE_SUCCESS;
suspend = NUSE_NO_SUSPEND;
}
} while (suspend == NUSE_SUSPEND);
The loop continues until a message is successfully enqueued or the task is unblocked by a reset. The implementation ensures thread‑safe access and efficient wake‑up of blocked consumers.
Embedded
- Stainless Steel Explained: Composition, Production, and Global Impact
- Diodes and Rectifiers: Fundamentals, Operation, and Key Parameters
- Understanding Conductors and Insulators: From Quantum Mechanics to Practical Applications
- C# Fundamentals: Input and Output Essentials
- Mailboxes in Nucleus SE: A Practical Guide to Configuration and Usage
- Semaphores in Nucleus RTOS: Utility Services & Data Structures
- Semaphores in Nucleus SE: Overview, Configuration, and API Essentials
- Event Flag Groups in Nucleus SE: An Introductory Guide to Configuration and API Usage
- Partition Memory in Nucleus RTOS/SE: Utility Services and Data Structures
- Partition Memory in Nucleus SE: Configuration, APIs, and Best Practices