Queues

The TI-RTOS Queue module provides a thread-safe unidirectional message passing module operating in a first in, first out (FIFO) basis. Queues are commonly used to allow high priority threads to pass messages to lower priority tasks for deferred processing; therefore allowing low priority tasks to block until necessary to run.

In Figure 18. a queue is configured for unidirectional communication from task A to task B. Task A “puts” messages into the queue and task B “gets” messages from the queue.

../_images/fig-queue-messaging-process.jpg

Figure 18. Queue Messaging Process

Functional Example “”“”“”“”“”“”“”“”“” Figure 19. and Figure 20. illustrate how a queue is used to enqueue a button press message from a Hwi to a Swi to be post-processed within a task context.

@startuml
hide footbox

box "Swi context"
    participant "Board Key module" as A
    participant Application as B
    database appMsgQueue as C
end box

-[#red]> A : Key press interrupt
<[#red]-- A

activate A

autonumber
A -> B : Application_keyChangeHandler();
activate B

note right: Add APP_KEY_CHANGE_EVT into the queue
B -> B : Application_enqueueMsg();
activate B
autonumber stop
B -> : malloc();
B -> C: Queue_put();
activate C
C --> B:
deactivate C
B -> : Event_post();
deactivate B
B --> A
deactivate B
deactivate A

@enduml

Figure 19. Sequence diagram for enqueuing a message

With interrupts enabled, a pin interrupt can occur asynchronously within a Hwi context. To keep interrupts as short as possible, the work associated to the interrupt is deferred to tasks for processing. In the some example applications, pin interrupts are abstracted via the Board Key module. This module notifies registered functions via a Swi callback. In this case, Application_keyChangeHandler is the registered callback function.

Step 1 in Figure 19. shows the callback to Application_keyChangeHandler when a key is pressed. This event is placed into the application’s queue for processing.

Listing 9. Defining Application_keyChangeHandler()
1
2
3
4
void Application_keyChangeHandler(uint8 keys)
{
  Application_enqueueMsg(APP_KEY_CHANGE_EVT, keys, NULL);
}

Step 2 in Figure 19. shows how this key press is enqueued for the application task. Here, memory is allocated so the message can be added to the queue.

Listing 10. Defining Application_enqueueMsg()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
static uint8_t Application_enqueueMsg(uint8_t event, uint8_t state, uint8_t *pData)
{
  qEvt_t *pMsg = malloc(sizeof(qEvt_t));

  // Create dynamic pointer to message.
  if (pMsg)
  {
    pMsg->hdr.event = event;
    pMsg->hdr.state = state;
    pMsg->pData = pData;

    // Enqueue the message.
    return Queue_put(appMsgQueue,pMsg->_elem);
  }

  return (false);
}

@startuml
hide footbox

box "Task context"
    participant Application as A
    database appMsgQueue as B
end box

activate A
A -> : Event_pend()
note right: Task called Event_pend() and gets blocked
deactivate A

...

-> A : Posted event
activate A
autonumber 3
A -> A : while (pMsg = Queue_get())
activate A
autonumber stop

autonumber resume
A -> : Application_processAppMsg(pMsg);
note right: Application_processAppMsg \n{\n\tcase (APP_KEY_CHANGE_EVT):\n\t\tApplication_handleKeys()\n};
autonumber resume
A -> : free(pMsg)

autonumber stop
note right: Repeat while there are more messages\nin the queue
deactivate A

@enduml

Figure 20. Sequence diagram for dequeuing a message

Step 3 in Figure 20., the application is unblocked by the posted event where it proceeds to check if messages have been placed in the queue for processing.

Listing 11. Processing application messages
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// If RTOS queue is not empty, process app message
if (events & APP_QUEUE_EVT)
{
    scEvt_t *pMsg;
    while (pMsg = (scEvt_t *)Queue_get(appMsgQueue))
    {
        // Process message
        Application_processAppMsg(pMsg);

        // Free the space from the message
        free(pMsg);
    }
}

Step 4 in Figure 20., the application takes the dequeued message and processes it.

Listing 12. Processing key interrupt message
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
static void Application_processAppMsg(sbcEvt_t *pMsg)
{
  switch (pMsg->hdr.event)
  {
    case APP_KEY_CHANGE_EVT:
      Application_handleKeys(pMsg->hdr.state);
      break;
    //...
  }
}

Step 5 in Figure 20., the application can now free the memory allocated in Step 2.