Message passing in many systems is much more than simply passing messages from one process to another. Many systems incorporate process synchronization into message passing. If you recall from Pthreads, there were three message passing primitives: Send(), Receive(), and Reply().
The message passing part of these primitives is fairly straight forward: a message is sent from process A to B using Send(), process B retrieves the message using Receive(), and process B can send a message in reply to A's message using Reply().
The synchronization part is a little more subtle. Here, a process is blocked when is issues a receive(). It remains blocked until a message is available for it to receive (because some other process did a send). Notice that if there is already a message waiting for the receiver, receive() does not block at all, but immediately returns the message.
Send() also blocks the calling process. In this case the sender is blocked until a reply() is made.
This form of synchronization is a wonderful tool when implementing the client/server or administrator model of IPC. Using these primitives, the client gives the server to ability to synchronize its actions. The server is free to keep the client blocked, or ready the client at any time by deciding when it issues its reply. Replies need not be made right away, nor need they be made in the same order that the related message (sends) were received. You explored some of the implications of this in 315.