OSLIB Mailboxes

A Mailbox object is a circular queue of messages that can be posted and fetched from both thread and ISR contexts.

Global Settings

CH_CFG_USE_MAILBOXES This switch enables the mailboxes API in the kernel. Mailboxes also require CH_CFG_USE_SEMAPHORES.

Description

Unlike synchronous messages, mailboxes are asynchronous in nature and mono-directional, messages flow in one direction in FIFO order. Mailboxes are a very flexible communication features usable in a lot of situations:

  • Thread/ISR to Thread/ISR communication, multiple senders and multiple receivers are supported.
  • As a pool of pre-initialized objects.

State Diagram

Mailboxes are regulated by the following state diagram:

 Mailbox State Diagram

Messages

Mailboxes use the same msg_t type already seen in the synchronous messages chapter but without the limitation regarding the reserved 0, -1 and -2 constants. The numeric range is fully available.

Buffer

Mailbox objects have an associated circular buffer of messages, usually an array of msg_t, the mailbox can be filled up to the buffer capacity, further attempt at posting new data will result in the thread waiting for a message slot to become free.

 Mailbox Buffer

Internally, two thread queues are used for threads waiting to write or read the mailbox.

The Reset State

After resetting a mailbox using chMBReset() the mailbox enters a permanent reset state, any attempt to access the mailbox will result in a MSG_RESET return code. The reset state can be exited by explicitly calling chMBResumeX() on the mailbox object.

Mailboxes API

mailbox_t Type of a mailbox object.
MAILBOX_DECL() Mailboxes static initializer.
chMBObjectInit() Initializes a mailbox object of type mailbox_t.
chMBReset() A mailbox is instantly emptied, all waiting threads are notified with a MSG_RESET message then the mailbox is put in reset state.
chMBResetI() A mailbox is instantly emptied, all waiting threads are notified with a MSG_RESET message then the mailbox is put in reset state (I-Class variant).
chMBResumeX() Returns a mailbox in reset state to the active state.
chMBPost() A message is posted in the mailbox.
chMBPostTimeout() A message is posted in the mailbox with timeout.
chMBPostS() A message is posted in the mailbox (S-Class variant).
chMBPostTimeoutsS() A message is posted in the mailbox with timeout (S-Class variant).
chMBPostI() A message is posted in the mailbox (I-Class variant).
chMBPostAhead() A message is posted in the mailbox ahead of other messages.
chMBPostAheadTimeout() A message is posted in the mailbox ahead of other messages with timeout.
chMBPostAheadS() A message is posted in the mailbox ahead of other messages (S-Class variant).
chMBPostAheadTimeoutS() A message is posted in the mailbox ahead of other messages with timeout (S-Class variant).
chMBPostAheadI() A message is posted in the mailbox ahead of other messages (I-Class variant).
chMBFetch() Fetches a message from a mailbox.
chMBFetchTimeout() Fetches a message from a mailbox with timeout.
chMBFetchS() Fetches a message from a mailbox (S-Class variant).
chMBFetchTimeoutS() Fetches a message from a mailbox with timeout (S-Class variant).
chMBFetchI() Fetches a message from a mailbox (I-Class variant).
chMBGetSizeI() Returns the mailbox buffer size (I-Class variant).
chMBGetFreeCountI() Returns the number of free message slots into a mailbox (I-Class variant).
chMBGetUsedCountI() Returns the number of used message slots into a mailbox (I-Class variant).
chMBPeekI() Returns the next message in the queue without removing it (I-Class variant).

Examples

Large Messages

Imagine to want to exchange large buffers between a producer ISR and one or more consumer threads, the buffers represent incoming network frames.

This example uses two mailboxes:

  • An object pool of free buffers.
  • A messages queue of filled buffers sent by the ISR to the consumer threads.

There are several constraints:

  • Once retrieved by a consumer a buffer is “in use” and must not be touched until the consumer returns it to the pool.
  • Buffers must be available when the ISR requires one or a frame is lost. This means that there must be enough consumer threads and CPU bandwidth to process them quickly enough.
  • Network failures must be notified to the consumer threads.
#define NUM_BUFFERS 16
#define BUFFERS_SIZE 256
 
static char buffers[NUM_BUFFERS][BUFFERS_SIZE];
 
static msg_t free_buffers_queue[NUM_BUFFERS];
static mailbox_t free_buffers;
 
static msg_t filled_buffers_queue[NUM_BUFFERS];
static mailbox_t filled_buffers;
 
/*
 * ISR serving the network interrupt.
 */
CH_IRQ_HANDLER(RX_FRAME_IRQ) {
 
  CH_IRQ_PROLOGUE();
 
  /* If the network is down all the waiting threads are notified.*/
  if ((NET->SR & NET_SR_ERROR) != 0 {
    /* Entering I-Locked state and waking up all threads with a
       mailbox reset notification.*/
    chSysLockFromISR();
    chMBResetI(&filled_buffers);
    chSysUnlockFromISR();
 
    /* Resetting interrupt source.*/
    NET->SR &= ~NET_SR_ERROR;
  }
 
  /* If there is a frame available in the network interface.*/
  if ((NET->SR & NET_SR_FRAME_AVAILABLE) != 0) {
    void *pbuf;
 
    /* Entering I-Locked state and waking up one thread, if available,
       the frame is lost if there are no available threads.*/
    chSysLockFromISR();
 
    /* if a buffer is available then fill and post it to the
       processor threads, we know that the post operation will
       not fail because the two mailboxes have the same size.*/
    if (chMBFetchI(&free_buffers, (msg_t *)&pbuf) == MSG_OK) {
      fill_buffer(pbuf);
      (void)chMBPostI(&filled_buffers, (msg_t)pbuf);
    }
 
    chSysUnlockFromISR();
 
    /* Resetting interrupt source.*/
    NET->SR &= ~NET_SR_FRAME_AVAILABLE;
  }
 
  CH_IRQ_EPILOGUE();
}
 
static THD_WORKING_AREA(waProcessFrameThread1, 128);
static THD_WORKING_AREA(waProcessFrameThread2, 128);
static THD_WORKING_AREA(waProcessFrameThread3, 128);
static THD_WORKING_AREA(waProcessFrameThread4, 128);
 
static THD_FUNCTION(ProcessFrameThread, arg) {
 
  while (true) {
    void *pbuf;
 
    /* Waiting for a filled buffer.*/
    msg_t msg = chMBFetch(&filled_buffers, (msg_t *)&pbuf);
 
    /* Processing the event.*/
    if (msg == MSG_RESET) {
      /* The mailbox has been reset, this means network failure.*/
      process_failure();
    }
    else {
      /* Processing incoming frame.*/
      process_frame(pbuf);
 
      /* Returning the buffer to the free buffers pool.*/
      (void)chMBPost(&free_buffers, (msg_t)pbuf);
    }
  }
}
 
/*
 * Initialization.
 */
void main(void) {
  unsigned i;
 
  /*
   * System initializations.
   * - Kernel initialization, the main() function becomes a thread and the
   *   RTOS is active.
   */
  chSysInit();
 
  /* Creating the mailboxes.*/
  chMBObjectInit(&filled_buffers, filled_buffers_queue, NUM_BUFFERS);
  chMBObjectInit(&free_buffers, free_buffers_queue, NUM_BUFFERS);
 
  /* Pre-filling the free buffers pool with the available buffers, the post
     will not stop because the mailbox is large enough.*/
  for (i = 0; i < NUM_BUFFERS; i++)
    (void)chMBPost(&free_buffers, (msg_t)&buffers[i]);
 
  /* Starting the frames processor threads.*/
  chThdCreateStatic(waProcessFrameThread1, sizeof(waProcessFrameThread1),
                    NORMALPRIO + 1, ProcessFrameThread, NULL);
  chThdCreateStatic(waProcessFrameThread2, sizeof(waProcessFrameThread2),
                    NORMALPRIO + 1, ProcessFrameThread, NULL);
  chThdCreateStatic(waProcessFrameThread3, sizeof(waProcessFrameThread3),
                    NORMALPRIO + 1, ProcessFrameThread, NULL);
  chThdCreateStatic(waProcessFrameThread4, sizeof(waProcessFrameThread4),
                    NORMALPRIO + 1, ProcessFrameThread, NULL);
 
  /* Networking subsystem initialization, processing starts.*/
  net_init();
 
  /* Continuing.*/
  ...;
}