Table of Contents
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
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.
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.*/ ...; }