Changes the mem_pool API so that the timeout parameter must be specified when invoking task_mem_pool_alloc() thereby obsoleting the following APIs: task_mem_pool_alloc_wait() task_mem_pool_alloc_wait_alloc() _task_mem_pool_alloc() Change-Id: Ifa88f13bca98ca3c7d0e1a3b64b40a00068619e0 Signed-off-by: Peter Mitsis <peter.mitsis@windriver.com>
645 lines
26 KiB
ReStructuredText
645 lines
26 KiB
ReStructuredText
.. _microkernel_mailboxes:
|
|
|
|
Mailboxes
|
|
#########
|
|
|
|
Concepts
|
|
********
|
|
|
|
The microkernel's mailbox object type is an implementation of a traditional
|
|
message queue.
|
|
|
|
A mailbox allows tasks to exchange messages. A task that sends a message
|
|
is known as the *sending task*, while a task that receives the message
|
|
is known as the *receiving task*. Messages may not be sent or received
|
|
by fibers or ISRs, nor may a given message be received by more than one task
|
|
(i.e. point-to-multipoint messaging is not supported).
|
|
|
|
A mailbox has a queue of messages that have been sent, but not yet received.
|
|
The messages in the queue are sorted by priority, allowing a higher priority
|
|
message to be received before a lower priority message that was sent earlier.
|
|
Messages of equal priority are handled in a first in, first out manner.
|
|
|
|
Any number of mailboxes can be defined in a microkernel system. Each mailbox
|
|
has a name that uniquely identifies it. A mailbox does not limit the number
|
|
of messages it can queue, nor does it place limits on the size of the messages
|
|
it handles.
|
|
|
|
The content of a message is stored in an array of bytes, called the
|
|
*message data*. The size and format of the message data is application-defined,
|
|
and can vary from one message to the next. Message data may be stored
|
|
in a buffer provided by the task that sends or receives the message,
|
|
or in a memory pool block. The message data portion of a message is optional;
|
|
a message without any message data is called an *empty message*.
|
|
|
|
The lifecycle of a message is fairly simple. A message is created when it
|
|
is given to a mailbox by the sending task. The message is then owned
|
|
by the mailbox until it is given to a receiving task. The receiving task may
|
|
retrieve the message data when it receives the message from the mailbox,
|
|
or it may perform data retrieval during a second, subsequent mailbox operation.
|
|
Only when data retrieval has been performed is the message deleted
|
|
by the mailbox.
|
|
|
|
Messages can be exchanged non-anonymously or anonymously. A sending task
|
|
can specify the name of the task to which the message is being sent,
|
|
or it can specify that any task may receive the message. Likewise, a receiving
|
|
task can specify the name of the task it wishes to receive a message from,
|
|
or it can specify that it is willing to receive a message from any task.
|
|
A message is exchanged only if the requirements of both the sending task and
|
|
receiving task can both be satisfied; such tasks are said to be *compatible*.
|
|
|
|
For example, if task A sends a message to task B it will be received by task B
|
|
if the latter tries to receive a message from task A (or from any task), but
|
|
not if task B tries to receive a message from task C. The message can never
|
|
be received by task C, even if it is trying to receive a message from task A
|
|
(or from any task).
|
|
|
|
Messages can be exchanged sychronously or asynchronously. In a synchronous
|
|
exchange the sending task blocks until the message has been fully processed
|
|
by the receiving task. In an asynchronous exchange the sending task does not
|
|
wait until the message has been received by another task before continuing,
|
|
thereby allowing the task to do other work (such as gathering data that will be
|
|
used in the next message) before the message is given to a receiving task and
|
|
fully processed. The technique used for a given message exchange is determined
|
|
by the sending task.
|
|
|
|
The synchronous exchange technique provides an inherent form of flow control,
|
|
preventing a sending task from generating messages faster than they can be
|
|
consumed by receiving tasks. The asynchronous exchange technique provides an
|
|
optional form of flow control, which allows a sending task to determine
|
|
if a previously sent message still exists before sending a subsequent message.
|
|
|
|
Message Descriptor
|
|
==================
|
|
|
|
A message descriptor is a data structure that specifies where a message's data
|
|
is located and how the message is to be handled by the mailbox. Both the
|
|
sending task and the receiving task pass a message descriptor to the mailbox
|
|
when accessing a mailbox. The mailbox uses both message descriptors to perform
|
|
a message exchange between compatible sending and receiving tasks. The mailbox
|
|
also updates some fields of the descriptors during the exchange to allow both
|
|
tasks to know what occurred.
|
|
|
|
A message descriptor is a structure of type :c:type:`struct k_msg`. The fields
|
|
listed below are available for application use; all other fields are for
|
|
kernel use only.
|
|
|
|
:c:option:`info`
|
|
A 32 bit value that is exchanged by the message sender and receiver,
|
|
and whose meaning is defined by the application. This exchange is
|
|
bi-directional, allowing the sender to pass a value to the receiver
|
|
during any message exchange, and allowing the receiver to pass a value
|
|
to the sender during a synchronous message exchange.
|
|
|
|
:c:option:`size`
|
|
The message data size, in bytes. Set it to zero when sending an empty
|
|
message, or when discarding the message data of a received message.
|
|
The mailbox updates this field with the actual number of data bytes
|
|
exchanged once the message is received.
|
|
|
|
:c:option:`tx_data`
|
|
A pointer to the sending task's message data buffer. Set it to
|
|
:c:macro:`NULL` when sending a memory pool block, or when sending
|
|
an empty message. (Not used when receiving a message.)
|
|
|
|
:c:option:`tx_block`
|
|
The descriptor for the memory pool block containing the sending task's
|
|
message data. (Not used when sending a message data buffer,
|
|
or when sending an empty message. Not used when receiving a message.)
|
|
|
|
:c:option:`rx_data`
|
|
A pointer to the receiving task's message data buffer. Set it to
|
|
:c:macro:`NULL` when the message's data is not wanted, or when it will be
|
|
retrieved by a subsequent mailbox operation. (Not used when sending
|
|
a message.)
|
|
|
|
:c:option:`tx_task`
|
|
The name of the sending task. Set it to :c:macro:`ANYTASK` to receive
|
|
a message sent by any task. The mailbox updates this field with the
|
|
actual sender's name once the message is received. (Not used when
|
|
sending a message.)
|
|
|
|
:c:option:`rx_task`
|
|
The name of the receiving task. Set it to :c:macro:`ANYTASK` to allow
|
|
any task to receive the message. The mailbox updates this field with
|
|
the actual receiver's name once the message is received, but only if
|
|
the message is sent synchronously. (Not used when receiving a message.)
|
|
|
|
Sending a Message
|
|
=================
|
|
|
|
A task sends a message by first creating the message data to be sent (if any).
|
|
The data may be placed in a message buffer---such as an array or structure
|
|
variable---whose contents are copied to an area supplied by the receiving task
|
|
during the message exchange. Alternatively, the data may be placed in a block
|
|
allocated from a memory pool, which is handed off to the receiving task
|
|
during the exchange. A message buffer is typically used when the amount of
|
|
data involved is small, and the cost of copying the data is less than the cost
|
|
of allocating and freeing a memory pool block. A memory pool block *must*
|
|
be used when a non-empty message is sent asynchronously.
|
|
|
|
Next, the task creates a message descriptor that characterizes the message
|
|
to be sent, as described in the previous section.
|
|
|
|
Finally, the task calls one of the mailbox send APIs to initiate the message
|
|
exchange. The message is immediately given to a compatible receiving task,
|
|
if one is currently waiting for a message. Otherwise, the message is added
|
|
to the mailbox's queue of messages, according to the priority specified by
|
|
the sending task. Typically, a sending task sets the message priority to
|
|
its own task priority level, allowing messages sent by higher priority tasks
|
|
to take precedence over those sent by lower priority tasks.
|
|
|
|
For a synchronous send operation the operation normally completes when a
|
|
receiving task has both received the message and retrieved the message data.
|
|
If the message is not received before the waiting period specified by the
|
|
sending task is reached, the message is removed from the mailbox's queue
|
|
and the sending task continues processing. When a send operation completes
|
|
successfully the sending task can examine the message descriptor to determine
|
|
which task received the message and how much data was exchanged, as well as
|
|
the application-defined info value supplied by the receiving task.
|
|
|
|
.. note::
|
|
A synchronous send operation may block the sending task indefinitely---even
|
|
when the task specifies a maximum waiting period---since the waiting period
|
|
only limits how long the mailbox waits before the message is received
|
|
by another task. Once a message is received there is no limit to the time
|
|
the receiving task may take to retrieve the message data and unblock
|
|
the sending task.
|
|
|
|
For an asynchronous send operation the operation always completes immediately.
|
|
This allows the sending task to continue processing regardless of whether the
|
|
message is immediately given to a receiving task or is queued by the mailbox.
|
|
The sending task may optionally specify a semaphore that the mailbox gives
|
|
when the message is deleted by the mailbox (i.e. when the message has been
|
|
received and its data retrieved by a receiving task). The use of a semaphore
|
|
allows the sending task to easily implement a flow control mechanism that
|
|
ensures that the mailbox holds no more than an application-specified number
|
|
of messages from a sending task (or set of sending tasks) at any point in time.
|
|
|
|
Receiving a Message
|
|
===================
|
|
|
|
A task receives a message by first creating a message descriptor that
|
|
characterizes the message it wants to receive. It then calls one of the
|
|
mailbox receive APIs. The mailbox searches its queue of messages
|
|
and takes the first one it finds that satisfies both the sending and
|
|
receiving tasks' message descriptor criteria. If no compatible message
|
|
exists, the receiving task may choose to wait for one to be sent. If no
|
|
compatible message appears before the waiting period specified
|
|
by the receiving task is reached, the receive operation fails and
|
|
the receiving task continues processing. Once a receive operation completes
|
|
successfully the receiving task can examine the message descriptor
|
|
to determine which task sent the message, how much data was exchanged,
|
|
and the application-defined info value supplied by the sending task.
|
|
|
|
The receiving task controls both the amount of data it retrieves from an
|
|
incoming message and where the data ends up. The task may choose to take
|
|
all of the data in the message, to take only the initial part of the data,
|
|
or to take no data at all. Similarly, the task may choose to have the data
|
|
copied into a buffer area of its choice or to have it placed in a memory
|
|
pool block. A message buffer is typically used when the amount of data
|
|
involved is small, and the cost of copying the data is less than the cost
|
|
of allocating and freeing a memory pool block.
|
|
|
|
The following sections outline various approaches a receiving task may use
|
|
when retrieving message data.
|
|
|
|
Retrieving Data Immediately into a Buffer
|
|
-----------------------------------------
|
|
|
|
The most straightforward way for a task to retrieve message data is to
|
|
specify a buffer when the message is received. The task indicates
|
|
both the location of the buffer (which must not be :c:macro:`NULL`)
|
|
and its size (which must be greater than zero).
|
|
|
|
The mailbox copies the message's data to the buffer as part of the
|
|
receive operation. If the buffer is not big enough to contain all of the
|
|
message's data, any uncopied data is lost. If the message is not big enough
|
|
to fill all of the buffer with data, the unused portion of the buffer is
|
|
left unchanged. In all cases the mailbox updates the receiving task's
|
|
message descriptor to indicate how many data bytes were copied (if any).
|
|
|
|
The immediate data retrieval technique is best suited for applications involving
|
|
small messages where the maximum size of a message is known in advance.
|
|
|
|
.. note::
|
|
This technique can be used when the message data is actually located
|
|
in a memory pool block supplied by the sending task. The mailbox copies
|
|
the data into the buffer specified by the receiving task, then automatically
|
|
frees the block back to its memory pool. This allows a receiving task
|
|
to retrieve message data without having to know whether the data
|
|
was sent using a buffer or a block.
|
|
|
|
Retrieving Data Subsequently into a Buffer
|
|
------------------------------------------
|
|
|
|
A receiving task may choose to retrieve no message data at the time the message
|
|
is received, so that it can retrieve the data into a buffer at a later time.
|
|
The task does this by specifying a buffer location of :c:macro:`NULL`
|
|
and a size indicating the maximum amount of data it is willing to retrieve
|
|
later (which must be greater than or equal to zero).
|
|
|
|
The mailbox does not copy any message data as part of the receive operation.
|
|
However, the mailbox still updates the receiving task's message descriptor
|
|
to indicate how many data bytes are available for retrieval.
|
|
|
|
The receiving task must then respond as follows:
|
|
|
|
* If the message descriptor size is zero, then either the received message is
|
|
an empty message or the receiving task did not want to receive any
|
|
message data. The receiving task does not need to take any further action
|
|
since the mailbox has already completed data retrieval and deleted the
|
|
message.
|
|
|
|
* If the message descriptor size is non-zero and the receiving task still
|
|
wants to retrieve the data, the task must supply a buffer large enough
|
|
to hold the data. The task first sets the message descriptor's
|
|
:c:option:`rx_data` field to the address of the buffer, then calls
|
|
:c:func:`task_mbox_data_get()`. This instructs the mailbox to copy the data
|
|
and delete the message.
|
|
|
|
* If the message descriptor size is non-zero and the receiving task does *not*
|
|
want to retrieve the data, the task sets the message descriptor's
|
|
:c:option:`size` field to zero and calls :c:func:`task_mbox_data_get()`.
|
|
This instructs the mailbox to delete the message without copying the data.
|
|
|
|
The subsequent data retrieval technique is suitable for applications where
|
|
immediate retrieval of message data is undesirable. For example, it can be
|
|
used when memory limitations make it impractical for the receiving task to
|
|
always supply a buffer capable of holding the largest possible incoming message.
|
|
|
|
.. note::
|
|
This technique can be used when the message data is actually located
|
|
in a memory pool block supplied by the sending task. The mailbox copies
|
|
the data into the buffer specified by the receiving task, then automatically
|
|
frees the block back to its memory pool. This allows a receiving task
|
|
to retrieve message data without having to know whether the data
|
|
was sent using a buffer or a block.
|
|
|
|
Retrieving Data Subsequently into a Block
|
|
-----------------------------------------
|
|
|
|
A receiving task may choose to retrieve message data into a memory pool block,
|
|
rather than a buffer area of its choice. This is done in much the same way
|
|
as retrieving data subsequently into a buffer---the receiving task first
|
|
receives the message without its data, then retrieves the data by calling
|
|
:c:func:`task_mbox_data_block_get()`. The latter call fills in the block
|
|
descriptor supplied by the receiving task, allowing the task to access the data.
|
|
This call also causes the mailbox to delete the received message, since
|
|
data retrieval has been completed. The receiving task is then responsible
|
|
for freeing the block back to the memory pool when the data is no longer needed.
|
|
|
|
This technique is best suited for applications where the message data has
|
|
been sent using a memory pool block, either because a large amount of data
|
|
is involved or because the message was sent asynchronously.
|
|
|
|
.. note::
|
|
This technique can be used when the message data is located in a buffer
|
|
supplied by the sending task. The mailbox automatically allocates a memory
|
|
pool block and copies the message data into it. However, this is much less
|
|
efficient than simply retrieving the data into a buffer supplied by the
|
|
receiving task. In addition, the receiving task must be designed to handle
|
|
cases where the data retrieval operation fails because the mailbox cannot
|
|
allocate a suitable block from the memory pool. If such cases are possible,
|
|
the receiving task can call :c:func:`task_mbox_data_block_get_wait()` or
|
|
:c:func:`task_mbox_data_block_get_wait_timeout()` to permit the task to wait
|
|
until a suitable block can be allocated. Alternatively, the task can use
|
|
:c:func:`task_mbox_data_get()` to inform the mailbox that it no longer wishes
|
|
to receive the data at all, allowing the mailbox to release the message.
|
|
|
|
Purpose
|
|
*******
|
|
|
|
Use a mailbox to transfer data items between tasks whenever the capabilities
|
|
of a FIFO are insufficient.
|
|
|
|
|
|
Usage
|
|
*****
|
|
|
|
Defining a Mailbox
|
|
==================
|
|
|
|
The following parameters must be defined:
|
|
|
|
*name*
|
|
This specifies a unique name for the mailbox.
|
|
|
|
Public Mailbox
|
|
--------------
|
|
|
|
Define the mailbox in the application's MDEF using the following syntax:
|
|
|
|
.. code-block:: console
|
|
|
|
MAILBOX name
|
|
|
|
For example, the file :file:`projName.mdef` defines a mailbox as follows:
|
|
|
|
.. code-block:: console
|
|
|
|
% MAILBOX NAME
|
|
% ==========================
|
|
MAILBOX REQUEST_BOX
|
|
|
|
A public mailbox can be referenced by name from any source file that
|
|
includes the file :file:`zephyr.h`.
|
|
|
|
Private Mailbox
|
|
---------------
|
|
|
|
Define the mailbox in a source file using the following syntax:
|
|
|
|
.. code-block:: c
|
|
|
|
DEFINE_MAILBOX(name);
|
|
|
|
For example, the following code defines a private mailbox named ``PRIV_MBX``.
|
|
|
|
.. code-block:: c
|
|
|
|
DEFINE_MAILBOX(PRIV_MBX);
|
|
|
|
The mailbox ``PRIV_MBX`` can be used in the same style as those
|
|
defined in the MDEF.
|
|
|
|
To utilize this mailbox from a different source file use the following syntax:
|
|
|
|
.. code-block:: c
|
|
|
|
extern const kmbox_t PRIV_MBX;
|
|
|
|
Example: Sending a Variable-Sized Mailbox Message
|
|
=================================================
|
|
|
|
This code uses a mailbox to synchronously pass variable-sized requests
|
|
from a producing task to any consuming task that wants it. The message
|
|
"info" field is used to exchange information about the maximum size buffer
|
|
that each task can handle.
|
|
|
|
.. code-block:: c
|
|
|
|
void producer_task(void)
|
|
{
|
|
char buffer[100];
|
|
int buffer_bytes_used;
|
|
|
|
struct k_msg send_msg;
|
|
k_priority_t send_priority = task_priority_get();
|
|
|
|
while (1) {
|
|
|
|
/* generate data to send */
|
|
...
|
|
buffer_bytes_used = ... ;
|
|
memcpy(buffer, source, buffer_bytes_used);
|
|
|
|
/* prepare to send message */
|
|
send_msg.info = buffer_bytes_used;
|
|
send_msg.size = buffer_bytes_used;
|
|
send_msg.tx_data = buffer;
|
|
send_msg.rx_task = ANYTASK;
|
|
|
|
/* send message and wait until a consumer receives it */
|
|
task_mbox_put(REQUEST_BOX, send_priority,
|
|
&send_msg,TICKS_UNLIMITED);
|
|
|
|
/* info, size, and rx_task fields have been updated */
|
|
|
|
/* verify that message data was fully received */
|
|
if (send_msg.size < buffer_bytes_used) {
|
|
printf("some message data dropped during transfer!");
|
|
printf("receiver only had room for %d bytes", send_msg.info);
|
|
}
|
|
}
|
|
}
|
|
|
|
Example: Receiving a Variable-Sized Mailbox Message
|
|
===================================================
|
|
|
|
This code uses a mailbox to process variable-sized requests from any
|
|
producing task, using the immediate data retrieval technique. The message
|
|
"info" field is used to exchange information about the maximum size buffer
|
|
that each task can handle.
|
|
|
|
.. code-block:: c
|
|
|
|
void consumer_task(void)
|
|
{
|
|
struct k_msg recv_msg;
|
|
char buffer[100];
|
|
|
|
int i;
|
|
int total;
|
|
|
|
while (1) {
|
|
/* prepare to receive message */
|
|
recv_msg.info = 100;
|
|
recv_msg.size = 100;
|
|
recv_msg.rx_data = buffer;
|
|
recv_msg.rx_task = ANYTASK;
|
|
|
|
/* get a data item, waiting as long as needed */
|
|
task_mbox_get(REQUEST_BOX, &recv_msg, TICKS_UNLIMITED);
|
|
|
|
/* info, size, and tx_task fields have been updated */
|
|
|
|
/* verify that message data was fully received */
|
|
if (recv_msg.info != recv_msg.size) {
|
|
printf("some message data dropped during transfer!");
|
|
printf("sender tried to send %d bytes", recv_msg.info);
|
|
}
|
|
|
|
/* compute sum of all message bytes (from 0 to 100 of them) */
|
|
total = 0;
|
|
for (i = 0; i < recv_msg.size; i++) {
|
|
total += buffer[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
Example: Sending an Empty Mailbox Message
|
|
=========================================
|
|
|
|
This code uses a mailbox to synchronously pass 4 byte random values
|
|
to any consuming task that wants one. The message "info" field is
|
|
large enough to carry the information being exchanged, so the data buffer
|
|
portion of the message isn't used.
|
|
|
|
.. code-block:: c
|
|
|
|
void producer_task(void)
|
|
{
|
|
struct k_msg send_msg;
|
|
k_priority_t send_priority = task_priority_get();
|
|
|
|
while (1) {
|
|
|
|
/* generate random value to send */
|
|
uint32_t random_value = sys_rand32_get();
|
|
|
|
/* prepare to send empty message */
|
|
send_msg.info = random_value;
|
|
send_msg.size = 0;
|
|
send_msg.tx_data = NULL;
|
|
send_msg.rx_task = ANYTASK;
|
|
|
|
/* send message and wait until a consumer receives it */
|
|
task_mbox_put(REQUEST_BOX, send_priority,
|
|
&send_msg, TICKS_UNLIMITED);
|
|
|
|
/* no need to examine the receiver's "info" value */
|
|
}
|
|
}
|
|
|
|
Example: Deferring the Retrieval of Message Data
|
|
================================================
|
|
|
|
This code uses a mailbox's subsequent data retrieval mechanism to get message
|
|
data from a producing task only if the message meets certain criteria,
|
|
thereby eliminating unneeded data copying. The message "info" field supplied
|
|
by the sender is used to classify the message.
|
|
|
|
.. code-block:: c
|
|
|
|
void consumer_task(void)
|
|
{
|
|
struct k_msg recv_msg;
|
|
char buffer[10000];
|
|
|
|
while (1) {
|
|
/* prepare to receive message */
|
|
recv_msg.size = 10000;
|
|
recv_msg.rx_data = NULL;
|
|
recv_msg.rx_task = ANYTASK;
|
|
|
|
/* get message, but not its data */
|
|
task_mbox_get(REQUEST_BOX, &recv_msg, TICKS_UNLIMITED);
|
|
|
|
/* get message data for only certain types of messages */
|
|
if (is_message_type_ok(recv_msg.info)) {
|
|
/* retrieve message data and delete the message */
|
|
recv_msg.rx_data = buffer;
|
|
task_mbox_data_get(&recv_msg);
|
|
|
|
/* process data in "buffer" */
|
|
...
|
|
} else {
|
|
/* ignore message data and delete the message */
|
|
recv_msg.size = 0;
|
|
task_mbox_data_get(&recv_msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
Example: Sending an Asynchronous Mailbox Message
|
|
================================================
|
|
|
|
This code uses a mailbox to send asynchronous messages using memory blocks
|
|
obtained from TXPOOL, thereby eliminating unneeded data copying when exchanging
|
|
large messages. The optional semaphore capability is used to hold off
|
|
the sending of a new message until the previous message has been consumed,
|
|
so that a backlog of messages doesn't build up if the consuming task is unable
|
|
to keep up.
|
|
|
|
.. code-block:: c
|
|
|
|
void producer_task(void)
|
|
{
|
|
struct k_msg send_msg;
|
|
kpriority_t send_priority = task_priority_get();
|
|
|
|
volatile char *hw_buffer;
|
|
|
|
/* indicate that all previous messages have been processed */
|
|
task_sem_give(MY_SEMA);
|
|
|
|
while (1) {
|
|
/* allocate memory block that will hold message data */
|
|
task_mem_pool_alloc(&send_msg.tx_block, TXPOOL,
|
|
4096, TICKS_UNLIMITED);
|
|
|
|
/* keep saving hardware-generated data in the memory block */
|
|
/* until the previous message has been received by the consumer */
|
|
do {
|
|
memcpy(send_msg.tx_block.pointer_to_data, hw_buffer, 4096);
|
|
} while (task_sem_take(MY_SEMA, TICKS_NONE) != RC_OK);
|
|
|
|
/* finish preparing to send message */
|
|
send_msg.size = 4096;
|
|
send_msg.rx_task = ANYTASK;
|
|
|
|
/* send message containing most current data and loop around */
|
|
task_mbox_block_put(REQUEST_BOX, send_priority, &send_msg, MY_SEMA);
|
|
}
|
|
}
|
|
|
|
Example: Receiving an Asynchronous Mailbox Message
|
|
==================================================
|
|
|
|
This code uses a mailbox to receive messages sent asynchronously using a
|
|
memory block, thereby eliminating unneeded data copying when processing
|
|
a large message.
|
|
|
|
.. code-block:: c
|
|
|
|
void consumer_task(void)
|
|
{
|
|
struct k_msg recv_msg;
|
|
struct k_block recv_block;
|
|
|
|
int total;
|
|
char *data_ptr;
|
|
int i;
|
|
|
|
while (1) {
|
|
/* prepare to receive message */
|
|
recv_msg.size = 10000;
|
|
recv_msg.rx_data = NULL;
|
|
recv_msg.rx_task = ANYTASK;
|
|
|
|
/* get message, but not its data */
|
|
task_mbox_get(REQUEST_BOX, &recv_msg, TICKS_UNLIMITED);
|
|
|
|
/* get message data as a memory block and discard message */
|
|
task_mbox_data_block_get(&recv_msg, &recv_block, RXPOOL,
|
|
TICKS_UNLIMITED);
|
|
|
|
/* compute sum of all message bytes in memory block */
|
|
total = 0;
|
|
data_ptr = (char *)(recv_block.pointer_to_data);
|
|
for (i = 0; i < recv_msg.size; i++) {
|
|
total += data_ptr++;
|
|
}
|
|
|
|
/* release memory block containing data */
|
|
task_mem_pool_free(&recv_block);
|
|
}
|
|
}
|
|
|
|
.. note::
|
|
An incoming message that was sent synchronously is also processed correctly
|
|
by this algorithm, since the mailbox automatically creates a memory block
|
|
containing the message data using RXPOOL. However, the performance benefit
|
|
of using the asynchronous approach is lost.
|
|
|
|
APIs
|
|
****
|
|
|
|
The following APIs for mailbox operations are provided by the kernel:
|
|
|
|
:c:func:`task_mbox_put()`
|
|
Sends synchronous message to a receiving task, with time limited waiting.
|
|
|
|
:c:func:`task_mbox_block_put()`
|
|
Sends asynchrnonous message to a receiving task, or to a mailbox queue.
|
|
|
|
:c:func:`task_mbox_get()`
|
|
Gets message from a mailbox, with time limited waiting.
|
|
|
|
:c:func:`task_mbox_data_get()`
|
|
Retrieves message data into a buffer.
|
|
|
|
:c:func:`task_mbox_data_block_get()`
|
|
Retrieves message data into a block, with time limited waiting.
|