Synchronous Message Passing Example

Mailboxes are anonymous and asynchronous. Any task can post and fetch messages to and from a mailbox, and both posting and retrieving mailbox messages do not cause a task to wait unless the mailbox is full (when posting) or empty (when retrieving) respectively. However, there are times when you may want to pass messages between tasks, and also want to synchronize their actions. For this and other uses, REXIS provides another set of message-passing APIs that are synchronous.

 

For example, consider the case of a writing a resource server, e.g. a task that returns a block of data from a block device such as an SD card. There are multiple ways to do this, of course, but synchronous message passing provides an elegant solution:

 

void SD_ServerTask(unsigned arg0)

      {

      unsigned char recbuf[2];

 

      while (REXIS_MessageReceive(&sender_pid, recbuf, 2, 0))

            {

            // reads a block (of BUFLEN) and return to sender

            if (recbuf[0] == CMD_READ)

                  {

                  int block_no = recbuf[1];

                 

                  … // open the SD device

                  SD_DeviceRead(block_no, buffer, BUFLEN);

                  REXIS_MessageReply(sender_pid, buffer, BUFLEN);

                  }

            … // other commands

            }

      }

 

void task1(unsigned arg0)

      {

      int cmdbuf[2];

 

      while (1)

            {

            unsigned char reqbuf[BUFLEN];

 

            cmdbuf[0] = CMD_READ;

            cmdbuf[1] = 0;          // read from block 0

            REXIS_MessageSend(SD_ServerTask_pid, cmdbuf, 2, reqbuf, BUFLEN, 0);

            // now reqbuf contains the SD data

            // process the data…

            }

      }

 

As with the mailbox API, REXIS does not restrict what kind of data may be passed as “messages”. The Send, Receive, and Reply functions all take buffers with the respective lengths as arguments. The details of these APIs are described later.

 

In this example, the SD_ServerTask performs various functions on the SD Card. One of them is CMD_READ, a command that reads a block of data from the card. The command enumerations, meanings, and formats are all defined by the tasks. The server task calls REXIS_MessageReceive to receive a message. When a message is received, the server task checks to see whether it is the CMD_READ command. If it is, the example code fragment reads the data from the SD card (these details are left as an exercise and are not relevant to the example here) and sends the data back to the sender task as a reply message via the REXIS_MessageReply API.

 

Meanwhile, the sender task “task1” sends a message to the server task via REXIS_MessageSend. The first argument is the receiver task process ID, which can be obtained in several ways. The second and third arguments are the message buffer and its length. The message in this case contains the CMD_READ command and the block number of the SD card to be read from. The fourth and fifth arguments are the buffer to receive the reply message and its length. The REXIS kernel blocks the sender task when it makes the REXIS_MessageSend call, and only unblocks the sender task when the receiver task replies to the sender, as described in the previous paragraph.

 

The order in which the tasks invoke REXIS_MessageSend and REXIS_MessageReceive does not matter. A message send will always block the sender task. If the receiver task is not waiting for a message at that moment, the message is queued at the receiver task until the receiver performs a message-receive call. Or if the receiver task is already waiting for a message, the sent message is delivered to the receiver task immediately. Therefore, the receiver task may or may not block when making a REXIS_MessageReceive call, depending on whether or not there is a pending message that was sent to it earlier.

 

These synchronous message passing functions are incredibly powerful. If an RTOS provides mailboxes as its only IPC mechanism, then the above functionality must be implemented using multiple mailboxes, or with the addition of a MUTEX or semaphores to synchronize data availability. Either approach will make the program logic more complicated than using synchronous message passing.