The MsgDeliverEvent() function
As mentioned above in The send-hierarchy, there are cases when you need to break the natural flow of sends.
Such a case might occur if you had a client that sent a message to the server, the result might not be available for a while, and the client didn't want to block. Of course, you could also partly solve this with threads, by having the client simply use up a thread on the blocking server call, but this may not scale well for larger systems (where you'd be using up lots of threads to wait for many different servers). Let's say you didn't want to use a thread, but instead wanted the server to reply immediately to the client, I'll get around to your request shortly. At this point, since the server replied, the client is now free to continue processing. Once the server has completed whatever task the client gave it, the server now needs some way to tell the client, Hey, wake up, I'm done. Obviously, as we saw in the send-hierarchy discussion above, you can't have the server send a message to the client, because this might cause deadlock if the client sent a message to the server at that exact same instant. So, how does the server send a message to a client without violating the send hierarchy?
It's actually a multi-step operation. Here's how it works:
- The client creates a struct sigevent structure, and fills it in.
- The client sends a message to the server, effectively stating, Perform this specific task for me, reply right away, and by the way, here's a struct sigevent that you should use to notify me when the work is completed.
- The server receives the message (which includes the struct sigevent), stores the struct sigevent and the receive ID away, and replies immediately to the client.
- The client is now running, as is the server.
- When the server completes the work, the server uses MsgDeliverEvent() to inform the client that the work is now complete.
We'll take a look in detail at the struct sigevent in the Clocks, Timers, and Getting a Kick Every So Often chapter, under How to fill in the struct sigevent. For now, just think of the struct sigevent as a black box that somehow contains the event that the server uses to notify the client.
Since the server stored the struct sigevent and the receive ID from the client, the server can now call MsgDeliverEvent() to deliver the event, as selected by the client, to the client:
int MsgDeliverEvent (int rcvid, const struct sigevent *event);
Notice that the MsgDeliverEvent() function takes two parameters, the receive ID (in rcvid) and the event to deliver in event. The server does not modify or examine the event in any way! This point is important, because it allows the server to deliver whatever kind of event the client chose, without any specific processing on the server's part. (The server can, however, verify that the event is valid by using the MsgVerifyEvent() function.)
The rcvid is a receive ID that the server got from the client. Note that this is indeed a special case. Generally, after the server has replied to a client, the receive ID ceases to have any meaning (the reasoning being that the client is unblocked, and the server couldn't unblock it again, or read or write data from/to the client, etc.). But in this case, the receive ID contains just enough information for the kernel to be able to decide which client the event should be delivered to. When the server calls the MsgDeliverEvent() function, the server doesn't block — this is a non-blocking call for the server. The client has the event delivered to it (by the kernel), and may then perform whatever actions are appropriate.