186 lines
5.8 KiB
ReStructuredText
186 lines
5.8 KiB
ReStructuredText
.. _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()`
|