Message queues provide buffers between tasks and interrupt service routines.

A message queue is a structure that enables tasks to post and receive messages. A message is a generic (void) pointer and, as such, can be used to send data that will fit into a pointer type (two or four bytes, depending upon the processor's word size) or to pass a pointer to a block of memory. The message queue uses a buffer to enable a number of posts to be completed without receives occurring. The buffer keeps the posted messages in FIFO order, so the oldest message is received first. When the buffer isn't full, a post will put the message at the back of the queue and the calling task continues execution. When the buffer is full, a post will block the calling task until there is room for the message. When the buffer isn't empty, a receive will return the message from the front of the queue and continue execution of the calling task. When the buffer is empty, a receive will block the calling task until a message is posted.

Initializing a message queue

You allocate a message queue by declaring it as a C variable:

CTL_MESSAGE_QUEUE_t m1;

A message queue is initialized using ctl_message_queue_init:

void *queue[20];
⁞
ctl_message_queue_init(&m1, queue, 20);

This example uses a 20-element array for the message queue. The array is a void * so pointers to memory or (cast) integers can be communicated via a message queue.

Posting to a message queue

You can post a message to a message queue with an optional timeout by using the ctl_message_queue_post function.

ctl_message_queue_post(&m1, (void *)45, CTL_TIMEOUT_NONE, 0);

This example posts the integer 45 to the message queue.

You can post multiple messages to a message queue with an optional timeout using ctl_message_queue_post_multi:

if (ctl_message_queue_post_multi(&m1, 4, messages, CTL_TIMEOUT_ABSOLUTE, ctl_get_current_time()+1000) != 4)
  {
    // timeout occurred
  }

This example tests the return result to see if the timeout occurred.

If you want to post a message and you cannot afford to block (e.g. inside an interrupt service routine), you can use ctl_message_queue_post_nb (or ctl_message_queue_post_multi_nb if you want to post multiple messages):

if (ctl_message_queue_post_nb(&m1, (void *)45) == 0)
  {
    // queue is full
  }

This example tests the return result to see if the post failed.

Receiving from a message queue

You can use ctl_message_queue_receive to receive a message with an optional timeout:

void *msg;
ctl_message_queue_receive(&m1, &msg, CTL_TIMEOUT_NONE, 0);

This example receives the oldest message in the message queue.

Use ctl_message_queue_receive_multi to receive multiple messages from a message queue with an optional timeout:

if (ctl_message_queue_multi_receive(&m1, 4, msgs, CTL_TIMEOUT_DELAY, 1000) != 4)
  {
    // timeout occurred
  }

This example tests the return result to see if the timeout occurred.

If you want to receive a message and you don't want to block (e.g., when executing interrupt service routine), you can use ctl_message_queue_receive_nb (or ctl_message_queue_receive_multi_nb to receive multiple messages).

if (ctl_message_queue_receive_nb(&m1, &msg) == 0)
  {
    // queue is empty
  }

Producer-consumer example

The following example uses a message queue to implement the producer-consumer problem.

CTL_MESSAGE_QUEUE_t m1;
void *queue[20];

void task1(void)
{
  ⁞
  ctl_message_queue_post(&m1, (void *)i, CTL_TIMEOUT_NONE, 0);
  ⁞
}

void task2(void)
{
  void *msg;
  ⁞
  ctl_message_queue_receive(&m1, &msg, CTL_TIMEOUT_NONE, 0);
  ⁞
}

int main(void)
{
  ⁞
  ctl_message_queue_init(&m1, queue, 20);
  ⁞
}

Advanced use

You can associate event flags with a message queue that are set (and similarly cleared) when the message queue is not full and not empty using the function ctl_message_queue_setup_events.

For example, you can use this to wait for messages to arrive from multiple message (or byte) queues:

CTL_MESSAGE_QUEUE_t m1, m2;
CTL_EVENT_SET_t e;
ctl_message_queue_setup_events(&m1, &e, 1<<0, 1<<1));
ctl_message_queue_setup_events(&m2, &e, 1<<2, 1<<3));
⁞
switch (ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS,
                        &e, (1<<0) | (1<<2),
                        CTL_TIMEOUT_NONE, 0))
  {
  case 1<<0:
    ctl_message_queue_receive(&m1, …
    break;
  case 1<<2:
    ctl_message_queue_receive(&m2, …
    break;
  }

This example sets up and waits for the not-empty event of message queue m1 and the not-empty event of message queue m2. When the wait completes, it reads from the appropriate message queue. Note that you should not use a ‘with auto clear’ event wait type when waiting for events associated with a message queue.

You can use ctl_message_queue_num_used to test how many messages are in a message queue and ctl_message_queue_num_free to learn how many free messages are in a message queue. With these functions you can poll the message queue:

while (ctl_message_queue_num_free(&m1) < 10)
  ctl_task_timeout_wait(ctl_get_current_time() + 1000);
ctl_message_queue_post_multi(&m1, 10, …

This example waits for 10 elements to be free before it posts 10 elements.