| .. _message_queues_v2: |
| |
| Message Queues |
| ############## |
| |
| A :dfn:`message queue` is a kernel object that implements a simple |
| message queue, allowing threads and ISRs to asynchronously send and receive |
| fixed-size data items. |
| |
| .. contents:: |
| :local: |
| :depth: 2 |
| |
| Concepts |
| ******** |
| |
| Any number of message queues can be defined. Each message queue is referenced |
| by its memory address. |
| |
| A message queue has the following key properties: |
| |
| * A **ring buffer** of data items that have been sent but not yet received. |
| |
| * A **data item size**, measured in bytes. |
| |
| * A **maximum quantity** of data items that can be queued in the ring buffer. |
| |
| The message queue's ring buffer must be aligned to an N-byte boundary, where |
| N is a power of 2 (i.e. 1, 2, 4, 8, ...). To ensure that the messages stored in |
| the ring buffer are similarly aligned to this boundary, the data item size |
| must also be a multiple of N. |
| |
| A message queue must be initialized before it can be used. |
| This sets its ring buffer to empty. |
| |
| A data item can be **sent** to a message queue by a thread or an ISR. |
| The data item a pointed at by the sending thread is copied to a waiting thread, |
| if one exists; otherwise the item is copied to the message queue's ring buffer, |
| if space is available. In either case, the size of the data area being sent |
| *must* equal the message queue's data item size. |
| |
| If a thread attempts to send a data item when the ring buffer is full, |
| the sending thread may choose to wait for space to become available. |
| Any number of sending threads may wait simultaneously when the ring buffer |
| is full; when space becomes available |
| it is given to the highest priority sending thread that has waited the longest. |
| |
| A data item can be **received** from a message queue by a thread. |
| The data item is copied to the area specified by the receiving thread; |
| the size of the receiving area *must* equal the message queue's data item size. |
| |
| If a thread attempts to receive a data item when the ring buffer is empty, |
| the receiving thread may choose to wait for a data item to be sent. |
| Any number of receiving threads may wait simultaneously when the ring buffer |
| is empty; when a data item becomes available it is given to |
| the highest priority receiving thread that has waited the longest. |
| |
| .. note:: |
| The kernel does allow an ISR to receive an item from a message queue, |
| however the ISR must not attempt to wait if the message queue is empty. |
| |
| Implementation |
| ************** |
| |
| Defining a Message Queue |
| ======================== |
| |
| A message queue is defined using a variable of type :c:type:`struct k_msgq`. |
| It must then be initialized by calling :cpp:func:`k_msgq_init()`. |
| |
| The following code defines and initializes an empty message queue |
| that is capable of holding 10 items, each of which is 12 bytes long. |
| |
| .. code-block:: c |
| |
| struct data_item_type { |
| u32_t field1; |
| u32_t field2; |
| u32_t field3; |
| }; |
| |
| char __aligned(4) my_msgq_buffer[10 * sizeof(data_item_type)]; |
| struct k_msgq my_msgq; |
| |
| k_msgq_init(&my_msgq, my_msgq_buffer, sizeof(data_item_type), 10); |
| |
| Alternatively, a message queue can be defined and initialized at compile time |
| by calling :c:macro:`K_MSGQ_DEFINE`. |
| |
| The following code has the same effect as the code segment above. Observe |
| that the macro defines both the message queue and its buffer. |
| |
| .. code-block:: c |
| |
| K_MSGQ_DEFINE(my_msgq, sizeof(data_item_type), 10, 4); |
| |
| Writing to a Message Queue |
| ========================== |
| |
| A data item is added to a message queue by calling :cpp:func:`k_msgq_put()`. |
| |
| The following code builds on the example above, and uses the message queue |
| to pass data items from a producing thread to one or more consuming threads. |
| If the message queue fills up because the consumers can't keep up, the |
| producing thread throws away all existing data so the newer data can be saved. |
| |
| .. code-block:: c |
| |
| void producer_thread(void) |
| { |
| struct data_item_t data; |
| |
| while (1) { |
| /* create data item to send (e.g. measurement, timestamp, ...) */ |
| data = ... |
| |
| /* send data to consumers */ |
| while (k_msgq_put(&my_msgq, &data, K_NO_WAIT) != 0) { |
| /* message queue is full: purge old data & try again */ |
| k_msgq_purge(&my_msgq); |
| } |
| |
| /* data item was successfully added to message queue */ |
| } |
| } |
| |
| Reading from a Message Queue |
| ============================ |
| |
| A data item is taken from a message queue by calling :cpp:func:`k_msgq_get()`. |
| |
| The following code builds on the example above, and uses the message queue |
| to process data items generated by one or more producing threads. |
| |
| .. code-block:: c |
| |
| void consumer_thread(void) |
| { |
| struct data_item_t data; |
| |
| while (1) { |
| /* get a data item */ |
| k_msgq_get(&my_msgq, &data, K_FOREVER); |
| |
| /* process data item */ |
| ... |
| } |
| } |
| |
| Suggested Uses |
| ************** |
| |
| Use a message queue to transfer small data items between threads |
| in an asynchronous manner. |
| |
| .. note:: |
| A message queue can be used to transfer large data items, if desired. |
| However, this can increase interrupt latency as interrupts are locked |
| while a data item is written or read. It is usually preferable to transfer |
| large data items by exchanging a pointer to the data item, rather than the |
| data item itself. The kernel's memory map and memory pool object types |
| can be helpful for data transfers of this sort. |
| |
| A synchronous transfer can be achieved by using the kernel's mailbox |
| object type. |
| |
| Configuration Options |
| ********************* |
| |
| Related configuration options: |
| |
| * None. |
| |
| APIs |
| **** |
| |
| The following message queue APIs are provided by :file:`kernel.h`: |
| |
| * :c:macro:`K_MSGQ_DEFINE` |
| * :cpp:func:`k_msgq_init()` |
| * :cpp:func:`k_msgq_put()` |
| * :cpp:func:`k_msgq_get()` |
| * :cpp:func:`k_msgq_purge()` |
| * :cpp:func:`k_msgq_num_used_get()` |
| * :cpp:func:`k_msgq_num_free_get()` |