Some systems such as CSP regard the information in a message to be data subject to interpretation by the recipient. In these systems, the send operation is of the form:
send (<port name>, <outdata>)
while the receive is of the form:
receive (<port name>, <var>)The completion of the receive causes the outdata to be assigned to var (if the ports named are the same). Thus, a matching send and receive essentially perform a remote assignment:
<var> := <outdata>var must then be an assignment L-Value while outdata an R-value. Typically, var and outdata are untyped though with language support typed data may be transmitted in messages. For example, a port could be declared as follows:
<type name> port <port name>thereby typing the values that can be remotely assigned through by sending and receiving on it.
Other systems, such as Ada regard the information in a message to be a request for service. The service is named by the port, and message contains parameters. In these systems, the send operation is of the form:
<result> := send <port or service name> (<parameters>)and the receive operation is of the form:
receive <port or service name> (<formal parameters>): <result type> begin service request reply (answer) end(Here, we are assuming language support for specifying these operations)
The send operation is similar to a procedure call, and the receive operation is similar to a procedure declaration. When the receive succeeds, the parameters of the incoming message are assigned to the formal parameters of the receive statement. The receiving process then executes code that services the request, and then sends any results in a reply message. The following are examples of matching send and receive:
/* client */ foo := send add (3, 5); /* server */ receive add (p1, p2: int): int begin reply (p1 + p2) end
The main difference between the send and a procedure call is that the former can result in code being executed in a process on a remote machine. Thus the parameters are marshalled into a message from which they are unmarshalled into the receiver's address space. Hence, such a send is called a remote procedure call. The main difference between the receive and a procedure declaration is that the code associated with the receive is not executed till the process executes the receive operation. At this point, in the case of synchronous RPC, the sender and the receiver are said to be in a rendezvous. Later, we shall see other differences between local and remote procedure calls when we study distributed remote procedure calls.
The second form of message-passing can be simulated by the first. Thus the following communication according to the first form simulates the `add' example:
/*client*/ send (addPort, 3) /* addPort is an input port */ send (addPort, 5) send (addPort, replyPort); /* reply port is a bound port */ receive (replyPort, result); /*server*/ receive (addPort, p1); receive (addPort, p2); receive (addPort, replyPort); /* assuming a port can handle multiple types */ send (replyPort, p1 + p2);Thus the second form of communication is higher-level compared to the first and requires fewer kernel calls. However, it is also less flexible, since it requires exactly one reply to a message from the receiver of the message, does not allow incremental transmission of the parameters or the results, and also requires that all parameters of a request come from a single source. Sometimes it is useful if the receiver of a message forwards the request to another server. In such a situation the answer could come from a process other than the one to which the request was made. Also, in dataflow computations, often each operand of an operation comes from a different process. When all operands of an operation arrive at a server process, the operation is said to be triggered. Finally, a ``more'' process receiving a file from an ``ls' process or a video receiver receiving a video stream from a video sender would wish to receive and display the received information incrementally. RPC does not directly support the above scenarios.