CS452 - Real-Time Programming - Fall 2011

Lecture 9 - Send/Receive/Reply

Public Service Annoucements

  1. Reference handin for a0.

Inter-task Communication

Overview

Message passing combines synchronization and communication in one set of primitives.

int Send( Tid tid, char *message, int mslen, char *reply, int rplen )

int Receive( Tid *tid, char *message, int mslen )

int Reply( Tid tid, char *reply, int rplen )


How are They Used?

The Producer/Consumer Problem

+--------------+          +--------------+
|              |          |              |
|   Producer   |   ===>   |   Consumer   |
|              |          |              |
+--------------+          +--------------+

Producer Sends

  1. producer sends and blocks (I have some XXX for you)
  2. consumer receives (Give me some XXX)
  3. consumer accepts XXX
  4. consumer replies (I got the XXX)
  5. producer & consumer are simultaneously READY

Note. 1 & 2 could be run in the opposite order

Consumer Sends

  1. Consumer sends and blocks (`I am ready for some XXX.')
  2. Producer receives (`I have some XXX.')
  3. Producer replies (`Here is the XXX')
  4. Consumer accepts XXX
  5. Producer and consumer are simultaneously READY

Note. 1 & 2 could be run in the opposite order

Multiple Producers

Producers send; consumer receives in the order that producers send.

Note. Critical races can occur, which the application programmer must resolve.

Multiple Consumers

Consumers send; producer receives in the order that consumers send.

Note. Critical races can occur, which the application programmer must resolve.

Multiple Consumers AND Multiple Producers

Consumers send and producers send: who receives?

+---------------+          +------------+          +---------------+
|               |          |            |          |               |
|   Producers   |   ===>   |   Buffer   |   ===>   |   Consumers   |
|               |          |            |          |               |
+---------------+          +------------+          +---------------+

Buffer receives two types of request

  1. Here is some XXX
  2. I want some XXX

Only a receiver can accept two types of requests at once.


Sequence of States

Sender

  1. Active -> Receive_Blocked
  2. Receive_Blocked -> Reply_Blocked
  3. Reply_Blocked -> Ready

Receiver

  1. Active -> Send_Blocked
  2. Send_Blocked -> Ready
  3. Ready -> Active
  4. ...
  5. Active -> Ready

There are two cases

Send before Receive

Send
           ...
           Receive
           ...
           Reply
...        ...

Message copying occurs inside Receive and Reply.

Receive needs to have a list of current senders, the ReceiveQ

Receive before Send

            Receive
...
Send
            ...
            Reply
...         ...

Message copying occurs inside Send and Reply.


Practical Details

Note. You may find the descriptions below overly elaborate, but they catch the obvious corner cases.


int Send( Tid tid, char *message, int mslen, char *reply, int rplen )

These are pretty self explanatory, except

  1. The return value is the number of characters actually placed in the reply-buffer
  2. If something goes wrong, the return value is negative, coded to indicate what went wrong

    What can go wrong

    1. Illegal tid
    2. tid not an existing task

    It's up to Send to check that the reply-buffer was big enough by looking at its return value

    It's not an error if the task to which we Send never Receives

  3. Parsing argument and reply-buffer is potentially costly and error-prone

Implementing Send

What's in user space is just stubs.

What the kernel must do

  1. Check arguments
  2. Change state of sender to RECEIVE_BLOCKED
  3. Put sender on the end of the receiver's sendQ
  4. If receiver is SEND_BLOCKED, do #5 in Implementing Receive, below.

int Receive( Tid *tid, char *message, int msglen )

These are pretty self explanatory, except

  1. How is the task id copied form kernel to receiver?
  2. What if the buffer wasn't big enough?
  3. If several tasks have done Send, which one gets Received first?
  4. return value is number of bytes in message, including terminal character (\000) if the message is really a string..
  5. If something goes wrong, the return value is negative, coded to indicate what went wrong

    What can go wrong?

    1. Only part of the message was copied

    It's up to Receive to check that the message-buffer was big enough by looking at its return value

Implementing Receive

What the kernel must do

  1. Check arguments
  2. Change receiver's state to SEND_BLOCKED
  3. Check the sendQ
  4. If SENDQ_EMPTY
    1. Exit from kernel after scheduling
  5. else
    1. extract head of the send queue, the sender
    2. copy message from sender to receiver, after checking buffer sizes
    3. change sender's state to REPLY_BLOCKED
    4. change receiver's state to READY
    5. put sender's tid into receiver's argument
    6. put receiver on its readyQ
    7. set up receiver's return value

int Reply( Tid tid, char *reply, int rplen )

These are pretty self explanatory, except

  1. The Replyer need not be the Receiver, but must be in contact with the Receiver
  2. When all goes well Reply leaves two tasks READY when it completes

Implementing Reply

  1. Check arguments
  2. Copy message from replier to sender, checking buffer sizes
  3. Put sender on readyQ
  4. Set up sender's return value
  5. Change sender's state to READY
  6. Put replier on readyQ
  7. Set up replier's return value
  8. Change replier's state to READY.

Return to: