Message Queues¶
A 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.
Concepts¶
Any number of message queues can be defined (limited only by available RAM). 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 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.
A thread can also peek at the message on the head of a message queue without removing it from the queue. 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.
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 k_msgq
.
It must then be initialized by calling 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.
struct data_item_type {
uint32_t field1;
uint32_t field2;
uint32_t field3;
};
char __aligned(4) my_msgq_buffer[10 * sizeof(struct data_item_type)];
struct k_msgq my_msgq;
k_msgq_init(&my_msgq, my_msgq_buffer, sizeof(struct data_item_type), 10);
Alternatively, a message queue can be defined and initialized at compile time
by calling 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.
K_MSGQ_DEFINE(my_msgq, sizeof(struct data_item_type), 10, 4);
The following code demonstrates an alignment implementation for the
structure defined in the previous example code. aligned
means each
data_item_type
will begin on the specified byte boundary.
aligned(4)
means that the structure is aligned to an address that
is divisible by 4.
typedef struct {
uint32_t field1;
uint32_t field2;
uint32_t field3;
}__attribute__((aligned(4))) data_item_type;
Writing to a Message Queue¶
A data item is added to a message queue by calling 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.
void producer_thread(void)
{
struct data_item_type 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 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. Note that
the return value of k_msgq_get()
should be tested as -ENOMSG
can be returned due to k_msgq_purge()
.
void consumer_thread(void)
{
struct data_item_type data;
while (1) {
/* get a data item */
k_msgq_get(&my_msgq, &data, K_FOREVER);
/* process data item */
...
}
}
Peeking into a Message Queue¶
A data item is read from a message queue by calling k_msgq_peek()
.
The following code peeks into the message queue to read the data item at the head of the queue that is generated by one or more producing threads.
void consumer_thread(void)
{
struct data_item_type data;
while (1) {
/* read a data item by peeking into the queue */
k_msgq_peek(&my_msgq, &data);
/* 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.
API Reference¶
-
group
msgq_apis
Defines
-
K_MSGQ_FLAG_ALLOC
¶
-
K_MSGQ_DEFINE
(q_name, q_msg_size, q_max_msgs, q_align)¶ Statically define and initialize a message queue.
The message queue’s ring buffer contains space for q_max_msgs messages, each of which is q_msg_size bytes long. The buffer is aligned to a q_align -byte boundary, which must be a power of 2. To ensure that each message is similarly aligned to this boundary, q_msg_size must also be a multiple of q_align.
The message queue can be accessed outside the module where it is defined using:
extern struct k_msgq <name>;
- Parameters
q_name – Name of the message queue.
q_msg_size – Message size (in bytes).
q_max_msgs – Maximum number of messages that can be queued.
q_align – Alignment of the message queue’s ring buffer.
Functions
-
void
k_msgq_init
(struct k_msgq *msgq, char *buffer, size_t msg_size, uint32_t max_msgs)¶ Initialize a message queue.
This routine initializes a message queue object, prior to its first use.
The message queue’s ring buffer must contain space for max_msgs messages, each of which is msg_size bytes long. The buffer must be aligned to an N-byte boundary, where N is a power of 2 (i.e. 1, 2, 4, …). To ensure that each message is similarly aligned to this boundary, q_msg_size must also be a multiple of N.
- Parameters
msgq – Address of the message queue.
buffer – Pointer to ring buffer that holds queued messages.
msg_size – Message size (in bytes).
max_msgs – Maximum number of messages that can be queued.
- Returns
N/A
-
int
k_msgq_alloc_init
(struct k_msgq *msgq, size_t msg_size, uint32_t max_msgs)¶ Initialize a message queue.
This routine initializes a message queue object, prior to its first use, allocating its internal ring buffer from the calling thread’s resource pool.
Memory allocated for the ring buffer can be released by calling k_msgq_cleanup(), or if userspace is enabled and the msgq object loses all of its references.
- Parameters
msgq – Address of the message queue.
msg_size – Message size (in bytes).
max_msgs – Maximum number of messages that can be queued.
- Returns
0 on success, -ENOMEM if there was insufficient memory in the thread’s resource pool, or -EINVAL if the size parameters cause an integer overflow.
-
int
k_msgq_cleanup
(struct k_msgq *msgq)¶ Release allocated buffer for a queue.
Releases memory allocated for the ring buffer.
- Parameters
msgq – message queue to cleanup
- Returns 0
on success
- Returns -EBUSY
Queue not empty
-
int
k_msgq_put
(struct k_msgq *msgq, const void *data, k_timeout_t timeout)¶ Send a message to a message queue.
This routine sends a message to message queue q.
- Function properties (list may not be complete)
Note
The message content is copied from data into msgq and the data pointer is not retained, so the message content will not be modified by this function.
- Parameters
msgq – Address of the message queue.
data – Pointer to the message.
timeout – Non-negative waiting period to add the message, or one of the special values K_NO_WAIT and K_FOREVER.
- Returns 0
Message sent.
- Returns -ENOMSG
Returned without waiting or queue purged.
- Returns -EAGAIN
Waiting period timed out.
-
int
k_msgq_get
(struct k_msgq *msgq, void *data, k_timeout_t timeout)¶ Receive a message from a message queue.
This routine receives a message from message queue q
in a “first in,
first out” manner.
- Function properties (list may not be complete)
Note
timeout must be set to K_NO_WAIT if called from ISR.
- Parameters
msgq – Address of the message queue.
data – Address of area to hold the received message.
timeout – Waiting period to receive the message, or one of the special values K_NO_WAIT and K_FOREVER.
- Returns 0
Message received.
- Returns -ENOMSG
Returned without waiting.
- Returns -EAGAIN
Waiting period timed out.
-
int
k_msgq_peek
(struct k_msgq *msgq, void *data)¶ Peek/read a message from a message queue.
This routine reads a message from message queue q
in a “first in,
first out” manner and leaves the message in the queue.
- Function properties (list may not be complete)
- Parameters
msgq – Address of the message queue.
data – Address of area to hold the message read from the queue.
- Returns 0
Message read.
- Returns -ENOMSG
Returned when the queue has no message.
-
void
k_msgq_purge
(struct k_msgq *msgq)¶ Purge a message queue.
This routine discards all unreceived messages in a message queue’s ring buffer. Any threads that are blocked waiting to send a message to the message queue are unblocked and see an -ENOMSG error code.
- Parameters
msgq – Address of the message queue.
- Returns
N/A
-
uint32_t
k_msgq_num_free_get
(struct k_msgq *msgq)¶ Get the amount of free space in a message queue.
This routine returns the number of unused entries in a message queue’s ring buffer.
- Parameters
msgq – Address of the message queue.
- Returns
Number of unused ring buffer entries.
-
void
k_msgq_get_attrs
(struct k_msgq *msgq, struct k_msgq_attrs *attrs)¶ Get basic attributes of a message queue.
This routine fetches basic attributes of message queue into attr argument.
- Parameters
msgq – Address of the message queue.
attrs – pointer to message queue attribute structure.
- Returns
N/A
-
struct
k_msgq
¶ - #include <kernel.h>
Message Queue Structure.
Public Members
-
_wait_q_t
wait_q
¶ Message queue wait queue
-
struct k_spinlock
lock
¶ Lock
-
size_t
msg_size
¶ Message size
-
uint32_t
max_msgs
¶ Maximal number of messages
-
char *
buffer_start
¶ Start of message buffer
-
char *
buffer_end
¶ End of message buffer
-
char *
read_ptr
¶ Read pointer
-
char *
write_ptr
¶ Write pointer
-
uint32_t
used_msgs
¶ Number of used messages
-
uint8_t
flags
¶ Message queue
-
_wait_q_t
-
struct
k_msgq_attrs
¶ - #include <kernel.h>
Message Queue Attributes.
-