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 7. 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 7. Queue Messaging Process

Functional Example “”“”“”“”“”“”“”“”“” Figure 8. and Figure 9. 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 8. 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 8. 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 8. 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 9. Sequence diagram for dequeuing a message

Step 3 in Figure 9., 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 9., 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 9., the application can now free the memory allocated in Step 2.