1.. _mailboxes_v2: 2 3Mailboxes 4######### 5 6A :dfn:`mailbox` is a kernel object that provides enhanced message queue 7capabilities that go beyond the capabilities of a message queue object. 8A mailbox allows threads to send and receive messages of any size 9synchronously or asynchronously. 10 11.. contents:: 12 :local: 13 :depth: 2 14 15Concepts 16******** 17 18Any number of mailboxes can be defined (limited only by available RAM). Each 19mailbox is referenced by its memory address. 20 21A mailbox has the following key properties: 22 23* A **send queue** of messages that have been sent but not yet received. 24 25* A **receive queue** of threads that are waiting to receive a message. 26 27A mailbox must be initialized before it can be used. This sets both of its 28queues to empty. 29 30A mailbox allows threads, but not ISRs, to exchange messages. 31A thread that sends a message is known as the **sending thread**, 32while a thread that receives the message is known as the **receiving thread**. 33Each message may be received by only one thread (i.e. point-to-multipoint and 34broadcast messaging is not supported). 35 36Messages exchanged using a mailbox are handled non-anonymously, 37allowing both threads participating in an exchange to know 38(and even specify) the identity of the other thread. 39 40Message Format 41============== 42 43A **message descriptor** is a data structure that specifies where a message's 44data is located, and how the message is to be handled by the mailbox. 45Both the sending thread and the receiving thread supply a message descriptor 46when accessing a mailbox. The mailbox uses the message descriptors to perform 47a message exchange between compatible sending and receiving threads. 48The mailbox also updates certain message descriptor fields during the exchange, 49allowing both threads to know what has occurred. 50 51A mailbox message contains zero or more bytes of **message data**. 52The size and format of the message data is application-defined, and can vary 53from one message to the next. 54 55A **message buffer** is an area of memory provided by the thread that sends or 56receives the message data. An array or structure variable can often be used for 57this purpose. 58 59A message that has neither form of message data is called an **empty message**. 60 61.. note:: 62 A message whose message buffer exists, but contains zero bytes of actual 63 data, is *not* an empty message. 64 65Message Lifecycle 66================= 67 68The life cycle of a message is straightforward. A message is created when 69it is given to a mailbox by the sending thread. The message is then owned 70by the mailbox until it is given to a receiving thread. The receiving thread 71may retrieve the message data when it receives the message from the mailbox, 72or it may perform data retrieval during a second, subsequent mailbox operation. 73Only when data retrieval has occurred is the message deleted by the mailbox. 74 75Thread Compatibility 76==================== 77 78A sending thread can specify the address of the thread to which the message 79is sent, or send it to any thread by specifying :c:macro:`K_ANY`. 80Likewise, a receiving thread can specify the address of the thread from which 81it wishes to receive a message, or it can receive a message from any thread 82by specifying :c:macro:`K_ANY`. 83A message is exchanged only when the requirements of both the sending thread 84and receiving thread are satisfied; such threads are said to be **compatible**. 85 86For example, if thread A sends a message to thread B (and only thread B) 87it will be received by thread B if thread B tries to receive a message 88from thread A or if thread B tries to receive from any thread. 89The exchange will not occur if thread B tries to receive a message 90from thread C. The message can never be received by thread C, 91even if it tries to receive a message from thread A (or from any thread). 92 93Message Flow Control 94==================== 95 96Mailbox messages can be exchanged **synchronously** or **asynchronously**. 97In a synchronous exchange, the sending thread blocks until the message 98has been fully processed by the receiving thread. In an asynchronous exchange, 99the sending thread does not wait until the message has been received 100by another thread before continuing; this allows the sending thread to do 101other work (such as gather data that will be used in the next message) 102*before* the message is given to a receiving thread and fully processed. 103The technique used for a given message exchange is determined 104by the sending thread. 105 106The synchronous exchange technique provides an implicit form of flow control, 107preventing a sending thread from generating messages faster than they can be 108consumed by receiving threads. The asynchronous exchange technique provides an 109explicit form of flow control, which allows a sending thread to determine 110if a previously sent message still exists before sending a subsequent message. 111 112Implementation 113************** 114 115Defining a Mailbox 116================== 117 118A mailbox is defined using a variable of type :c:struct:`k_mbox`. 119It must then be initialized by calling :c:func:`k_mbox_init`. 120 121The following code defines and initializes an empty mailbox. 122 123.. code-block:: c 124 125 struct k_mbox my_mailbox; 126 127 k_mbox_init(&my_mailbox); 128 129Alternatively, a mailbox can be defined and initialized at compile time 130by calling :c:macro:`K_MBOX_DEFINE`. 131 132The following code has the same effect as the code segment above. 133 134.. code-block:: c 135 136 K_MBOX_DEFINE(my_mailbox); 137 138Message Descriptors 139=================== 140 141A message descriptor is a structure of type :c:struct:`k_mbox_msg`. 142Only the fields listed below should be used; any other fields are for 143internal mailbox use only. 144 145*info* 146 A 32-bit value that is exchanged by the message sender and receiver, 147 and whose meaning is defined by the application. This exchange is 148 bi-directional, allowing the sender to pass a value to the receiver 149 during any message exchange, and allowing the receiver to pass a value 150 to the sender during a synchronous message exchange. 151 152*size* 153 The message data size, in bytes. Set it to zero when sending an empty 154 message, or when sending a message buffer with no actual data. When 155 receiving a message, set it to the maximum amount of data desired, or to 156 zero if the message data is not wanted. The mailbox updates this field with 157 the actual number of data bytes exchanged once the message is received. 158 159*tx_data* 160 A pointer to the sending thread's message buffer. Set it to ``NULL`` 161 when sending an empty message. Leave this field uninitialized when 162 receiving a message. 163 164*tx_target_thread* 165 The address of the desired receiving thread. Set it to :c:macro:`K_ANY` 166 to allow any thread to receive the message. Leave this field uninitialized 167 when receiving a message. The mailbox updates this field with 168 the actual receiver's address once the message is received. 169 170*rx_source_thread* 171 The address of the desired sending thread. Set it to :c:macro:`K_ANY` 172 to receive a message sent by any thread. Leave this field uninitialized 173 when sending a message. The mailbox updates this field 174 with the actual sender's address when the message is put into 175 the mailbox. 176 177Sending a Message 178================= 179 180A thread sends a message by first creating its message data, if any. 181 182Next, the sending thread creates a message descriptor that characterizes 183the message to be sent, as described in the previous section. 184 185Finally, the sending thread calls a mailbox send API to initiate the 186message exchange. The message is immediately given to a compatible receiving 187thread, if one is currently waiting. Otherwise, the message is added 188to the mailbox's send queue. 189 190Any number of messages may exist simultaneously on a send queue. 191The messages in the send queue are sorted according to the priority 192of the sending thread. Messages of equal priority are sorted so that 193the oldest message can be received first. 194 195For a synchronous send operation, the operation normally completes when a 196receiving thread has both received the message and retrieved the message data. 197If the message is not received before the waiting period specified by the 198sending thread is reached, the message is removed from the mailbox's send queue 199and the send operation fails. When a send operation completes successfully 200the sending thread can examine the message descriptor to determine 201which thread received the message, how much data was exchanged, 202and the application-defined info value supplied by the receiving thread. 203 204.. note:: 205 A synchronous send operation may block the sending thread indefinitely, 206 even when the thread specifies a maximum waiting period. 207 The waiting period only limits how long the mailbox waits 208 before the message is received by another thread. Once a message is received 209 there is *no* limit to the time the receiving thread may take to retrieve 210 the message data and unblock the sending thread. 211 212For an asynchronous send operation, the operation always completes immediately. 213This allows the sending thread to continue processing regardless of whether the 214message is given to a receiving thread immediately or added to the send queue. 215The sending thread may optionally specify a semaphore that the mailbox gives 216when the message is deleted by the mailbox, for example, when the message 217has been received and its data retrieved by a receiving thread. 218The use of a semaphore allows the sending thread to easily implement 219a flow control mechanism that ensures that the mailbox holds no more than 220an application-specified number of messages from a sending thread 221(or set of sending threads) at any point in time. 222 223.. note:: 224 A thread that sends a message asynchronously has no way to determine 225 which thread received the message, how much data was exchanged, or the 226 application-defined info value supplied by the receiving thread. 227 228Sending an Empty Message 229------------------------ 230 231This code uses a mailbox to synchronously pass 4 byte random values 232to any consuming thread that wants one. The message "info" field is 233large enough to carry the information being exchanged, so the data 234portion of the message isn't used. 235 236.. code-block:: c 237 238 void producer_thread(void) 239 { 240 struct k_mbox_msg send_msg; 241 242 while (1) { 243 244 /* generate random value to send */ 245 uint32_t random_value = sys_rand32_get(); 246 247 /* prepare to send empty message */ 248 send_msg.info = random_value; 249 send_msg.size = 0; 250 send_msg.tx_data = NULL; 251 send_msg.tx_target_thread = K_ANY; 252 253 /* send message and wait until a consumer receives it */ 254 k_mbox_put(&my_mailbox, &send_msg, K_FOREVER); 255 } 256 } 257 258Sending Data Using a Message Buffer 259----------------------------------- 260 261This code uses a mailbox to synchronously pass variable-sized requests 262from a producing thread to any consuming thread that wants it. 263The message "info" field is used to exchange information about 264the maximum size message buffer that each thread can handle. 265 266.. code-block:: c 267 268 void producer_thread(void) 269 { 270 char buffer[100]; 271 int buffer_bytes_used; 272 273 struct k_mbox_msg send_msg; 274 275 while (1) { 276 277 /* generate data to send */ 278 ... 279 buffer_bytes_used = ... ; 280 memcpy(buffer, source, buffer_bytes_used); 281 282 /* prepare to send message */ 283 send_msg.info = buffer_bytes_used; 284 send_msg.size = buffer_bytes_used; 285 send_msg.tx_data = buffer; 286 send_msg.tx_target_thread = K_ANY; 287 288 /* send message and wait until a consumer receives it */ 289 k_mbox_put(&my_mailbox, &send_msg, K_FOREVER); 290 291 /* info, size, and tx_target_thread fields have been updated */ 292 293 /* verify that message data was fully received */ 294 if (send_msg.size < buffer_bytes_used) { 295 printf("some message data dropped during transfer!"); 296 printf("receiver only had room for %d bytes", send_msg.info); 297 } 298 } 299 } 300 301Receiving a Message 302=================== 303 304A thread receives a message by first creating a message descriptor that 305characterizes the message it wants to receive. It then calls one of the 306mailbox receive APIs. The mailbox searches its send queue and takes the message 307from the first compatible thread it finds. If no compatible thread exists, 308the receiving thread may choose to wait for one. If no compatible thread 309appears before the waiting period specified by the receiving thread is reached, 310the receive operation fails. 311Once a receive operation completes successfully the receiving thread 312can examine the message descriptor to determine which thread sent the message, 313how much data was exchanged, 314and the application-defined info value supplied by the sending thread. 315 316Any number of receiving threads may wait simultaneously on a mailboxes' 317receive queue. The threads are sorted according to their priority; 318threads of equal priority are sorted so that the one that started waiting 319first can receive a message first. 320 321.. note:: 322 Receiving threads do not always receive messages in a first in, first out 323 (FIFO) order, due to the thread compatibility constraints specified by the 324 message descriptors. For example, if thread A waits to receive a message 325 only from thread X and then thread B waits to receive a message from 326 thread Y, an incoming message from thread Y to any thread will be given 327 to thread B and thread A will continue to wait. 328 329The receiving thread controls both the quantity of data it retrieves from an 330incoming message and where the data ends up. The thread may choose to take 331all of the data in the message, to take only the initial part of the data, 332or to take no data at all. Similarly, the thread may choose to have the data 333copied into a message buffer of its choice. 334 335The following sections outline various approaches a receiving thread may use 336when retrieving message data. 337 338Retrieving Data at Receive Time 339------------------------------- 340 341The most straightforward way for a thread to retrieve message data is to 342specify a message buffer when the message is received. The thread indicates 343both the location of the message buffer (which must not be ``NULL``) 344and its size. 345 346The mailbox copies the message's data to the message buffer as part of the 347receive operation. If the message buffer is not big enough to contain all of the 348message's data, any uncopied data is lost. If the message is not big enough 349to fill all of the buffer with data, the unused portion of the message buffer is 350left unchanged. In all cases the mailbox updates the receiving thread's 351message descriptor to indicate how many data bytes were copied (if any). 352 353The immediate data retrieval technique is best suited for small messages 354where the maximum size of a message is known in advance. 355 356The following code uses a mailbox to process variable-sized requests from any 357producing thread, using the immediate data retrieval technique. The message 358"info" field is used to exchange information about the maximum size 359message buffer that each thread can handle. 360 361.. code-block:: c 362 363 void consumer_thread(void) 364 { 365 struct k_mbox_msg recv_msg; 366 char buffer[100]; 367 368 int i; 369 int total; 370 371 while (1) { 372 /* prepare to receive message */ 373 recv_msg.info = 100; 374 recv_msg.size = 100; 375 recv_msg.rx_source_thread = K_ANY; 376 377 /* get a data item, waiting as long as needed */ 378 k_mbox_get(&my_mailbox, &recv_msg, buffer, K_FOREVER); 379 380 /* info, size, and rx_source_thread fields have been updated */ 381 382 /* verify that message data was fully received */ 383 if (recv_msg.info != recv_msg.size) { 384 printf("some message data dropped during transfer!"); 385 printf("sender tried to send %d bytes", recv_msg.info); 386 } 387 388 /* compute sum of all message bytes (from 0 to 100 of them) */ 389 total = 0; 390 for (i = 0; i < recv_msg.size; i++) { 391 total += buffer[i]; 392 } 393 } 394 } 395 396Retrieving Data Later Using a Message Buffer 397-------------------------------------------- 398 399A receiving thread may choose to defer message data retrieval at the time 400the message is received, so that it can retrieve the data into a message buffer 401at a later time. 402The thread does this by specifying a message buffer location of ``NULL`` 403and a size indicating the maximum amount of data it is willing to retrieve 404later. 405 406The mailbox does not copy any message data as part of the receive operation. 407However, the mailbox still updates the receiving thread's message descriptor 408to indicate how many data bytes are available for retrieval. 409 410The receiving thread must then respond as follows: 411 412* If the message descriptor size is zero, then either the sender's message 413 contained no data or the receiving thread did not want to receive any data. 414 The receiving thread does not need to take any further action, since 415 the mailbox has already completed data retrieval and deleted the message. 416 417* If the message descriptor size is non-zero and the receiving thread still 418 wants to retrieve the data, the thread must call :c:func:`k_mbox_data_get` 419 and supply a message buffer large enough to hold the data. The mailbox copies 420 the data into the message buffer and deletes the message. 421 422* If the message descriptor size is non-zero and the receiving thread does *not* 423 want to retrieve the data, the thread must call :c:func:`k_mbox_data_get`. 424 and specify a message buffer of ``NULL``. The mailbox deletes 425 the message without copying the data. 426 427The subsequent data retrieval technique is suitable for applications where 428immediate retrieval of message data is undesirable. For example, it can be 429used when memory limitations make it impractical for the receiving thread to 430always supply a message buffer capable of holding the largest possible 431incoming message. 432 433The following code uses a mailbox's deferred data retrieval mechanism 434to get message data from a producing thread only if the message meets 435certain criteria, thereby eliminating unneeded data copying. The message 436"info" field supplied by the sender is used to classify the message. 437 438.. code-block:: c 439 440 void consumer_thread(void) 441 { 442 struct k_mbox_msg recv_msg; 443 char buffer[10000]; 444 445 while (1) { 446 /* prepare to receive message */ 447 recv_msg.size = 10000; 448 recv_msg.rx_source_thread = K_ANY; 449 450 /* get message, but not its data */ 451 k_mbox_get(&my_mailbox, &recv_msg, NULL, K_FOREVER); 452 453 /* get message data for only certain types of messages */ 454 if (is_message_type_ok(recv_msg.info)) { 455 /* retrieve message data and delete the message */ 456 k_mbox_data_get(&recv_msg, buffer); 457 458 /* process data in "buffer" */ 459 ... 460 } else { 461 /* ignore message data and delete the message */ 462 k_mbox_data_get(&recv_msg, NULL); 463 } 464 } 465 } 466 467Suggested Uses 468************** 469 470Use a mailbox to transfer data items between threads whenever the capabilities 471of a message queue are insufficient. 472 473Configuration Options 474********************* 475 476Related configuration options: 477 478* :kconfig:option:`CONFIG_NUM_MBOX_ASYNC_MSGS` 479 480API Reference 481************* 482 483.. doxygengroup:: mailbox_apis 484