Receiving


Up: Design Next: Synchronous sends Previous: Sending

This is a little harder, because to start with we don't know even know the device that a message is ariving on. There are two obvious ways to approach this: either determine which device a message is available on and then receive that message, or for each device, try to receive a message (without blocking when no message is available). So we start could start with commands to check to see if a message is available, or to wait for one. But this leaves us with the problem of what to do with the message if checking for the message might receive the envelope. So, instead of checking for a whether a message is available, it is better to try and receive a message with


which has blocking and non-blocking versions for each device.

The multi-device version just calls the check_device for each of the devices. A wait may then turn into a poll. Note that the multi-device code could optimize for the case of a single active device, and use a wait when a blocking check-device is needed.

This requires each device to provide its own check_device; this really isn't so bad as the message-channel (eager/rendezvous) versions really need different code from the shared-memory (get) versions.

Note that this solves a number of problems:

    1. It doesn't matter whether to low-level code wants to receive a pointer to a control packet or the actual packet.


    2. The packets are likely different in detail, though we can probably restrict a few fields. Note that for shared memory, we usually want the address of the data, whereas for ``stream/channel'' interfaces, we receive the data into local data


    3. Heterogenity information will be needed by only some of the packets.


    4. Note that we may receive forwarding packets that are intended to send data to another node. This is just a data packet, so again the actual form is unimportant.

The check device code itself could use either a lookup table based on the packet type field (small integer) or a switch statement. In the lookup table case, this selects the routine that matches the kind of send that was used. The table is initialized when the device starts up. Using a switch is simpler but a little less flexible. Still, since the check_device code itself is accessed through a function pointer, we could eventually add a more complex version.

Now the situation is much like the send case. If the operation is not complete, we set up pointers to functions to


Note that the push operation can change the others as a way to implement a finite-state-machine approach to message delivery.

Now, for receives, there are two situations: the message is expected, and the message is unexpected. The above handles the case of an expected message. In the unexpected case, we also need another action:


This can start the process of receiving the data for an unexpected message that is now expected. One question is whether this should be a separate function or just another case of push. We have chosen to use the push function in order to reduce the number of function pointers in the request, and because it is a push operation. Note that the push for an unexpected message does not need to complete immediately; it can start the receive and return, setting the test, wait etc. functions appropriately.



Up: Design Next: Synchronous sends Previous: Sending