Download FreeRTOS

Quality RTOS & Embedded Software

Simplifying Authenticated Cloud Connectivity for Any Device.
Designing an energy efficient and cloud-connected IoT solution with CoAP.
Introducing FreeRTOS Kernel version 11.0.0:
FreeRTOS Roadmap and Code Contribution process.
OPC-UA over TSN with FreeRTOS.

Blocking on Multiple RTOS Objects

Introduction to Queue Sets

Queue sets are a FreeRTOS feature that enables an RTOS task to block (pend) when receiving from multiple queues and/or semaphores at the same time. Queues and semaphores are grouped into sets, then, instead of blocking on an individual queue or semaphore, a task instead blocks on the set.

Note: While it is sometimes necessary to block (pend) on more than one queue if you are integrating FreeRTOS with third party of legacy code, designs that are free from such restrictions can normally achieve the same functionality in a more efficient way using the alternative design pattern that is documented at the bottom of this page.

Using Queue Sets

Queue sets are used in a similar way to the select() API function, and related functions, that are part of the standard Berkeley sockets networking API.

Queue sets can contain queues and semaphores, which together are known as queue set members. API function parameters and return values that can take either a queue handle or a semaphore handle use the QueueSetMemberHandle_t type. Variables of type QueueHandle_t and SemaphoreHandle_t can normally be implicitly converted to an QueueSetMemberHandle_t parameter or return value without compiler warnings being generated (explicit casting to and from the QueueSetMemberHandle_t type is not normally required).

Creating a queue set Before a queue set can be used it must be created using the xQueueCreateSet() API function. Once created the queue set is referenced by a variable of type QueueSetHandle_t.

Adding a member to a queue set The xQueueAddToSet() API function is used to add a queue or semaphore to a queue set.

Blocking (pending) on a queue set The xQueueSelectFromSet() API function is used to test whether any of the set members are ready for reading - where reading means 'receiving' when the member is a queue, and 'taking' when the member is a semaphore.

Just like when using the xQueueReceive() and xSemaphoreTake() API functions, xQueueSelectFromSet() allows the calling task to optionally block until a member of the queue set is ready for reading.

NULL is returned if a call to xQueueSelectFromSet() times out. Otherwise xQueueSelectFromSet() returns the handle of the queue set member that is ready for reading, allowing the calling task to immediately call xQueueReceive() or xSemaphoreTake() (on a queue handle or semaphore handle respectively) with the guarantee that the operation will succeed.

Source Code Examples

The xQueueCreateSet() API function documentation page includes a source code example.

The standard demo/test file called QueueSet.c (located in the FreeRTOS/Demo/Common/Minimal/ directory of the main FreeRTOS zip file download) contains a comprehensive example.

Alternatives to Using Queue Sets

Unless there is a specific integration issue that necessitates blocking on multiple queues, the same functionality can normally be achieved with a lower code size, RAM size, and run time overhead using a single queue. The FreeRTOS-Plus-UDP implementation provides a convenient example of how this is done, and is described in the following sub-sections.

UDP/IP Stack: Problem Definition

The task that manages the FreeRTOS-Plus-UDP stack is event driven. There are multiple event sources. Some events do not have any data associated with them. Some events have a variable amount of data associated with them. Events include:
  • The Ethernet hardware receiving a frame. Frames contain large and variable amounts of data.

  • The Ethernet hardware completing the transmission of a frame, freeing network and DMA buffers.
  • An application task sending a packet. Packets can contain a large and variable amount of data.

  • Various software timers, including the ARP timer. Timer events are not associated with any data.

UDP/IP Stack: Solution

The UDP/IP stack could use a different queue for each event source, then use a queue set to block on all the queues at once. Instead, the UDP/IP stack:
  1. Defines a structure that contains a member to hold the event type, and another member to hold the data (or a pointer to the data) that is associated with the event.

  2. Uses a single queue that is created to hold the defined structure. Each event source posts to the same queue.

The structure definition is shown below.

typedef struct IP_TASK_COMMANDS
    eIPEvent_t eEventType; /* Tells the receiving task what the event is. */
    void *pvData; /* Holds or points to any data associated with the event. */

} xIPStackEvent_t;

Examples of how this structure is used:

  • When the ARP timer expires it sends an event to the queue with eEventType set to eARPTimerEvent (an enumerated type). ARP timer events are not associated with any data so pvData is not set.

  • When the Ethernet driver receives a frame it sends an event to the queue with eEventType set to eEthernetRxEvent, and pvData set to point to the frame buffer.

  • Etc.

The UDP/IP task processes events using a simple loop:

/* The variable used to receive from the queue. */
xIPStackEvent_t xReceivedEvent;

for( ;; )
    /* Wait until there is something to do. */
    xQueueReceive( xNetworkEventQueue, &xReceivedEvent, portMAX_DELAY );

    /* Perform a different action for each event type. */
    switch( xReceivedEvent.eEventType )
        case eNetworkDownEvent :

        case eEthernetRxEvent :
            prvProcessEthernetFrame( xReceivedEvent.pvData );

        case eARPTimerEvent :

        case eStackTxEvent :
            prvProcessGeneratedPacket( xReceivedEvent.pvData );

        case eDHCPEvent:

        default :
            /* Should not get here. */

Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.