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 multistep operation. Here's how it works:
- The client creates a struct sigevent structure, and fills it in.
- The client calls MsgRegisterEvent() to register the sigevent as a secure event. The function converts the sigevent into a handle.
- 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 the handle for a struct sigevent that you should use to notify me when the work is completed.
- The server receives the message (which includes the handle for the struct sigevent), stores the handle and the receive ID, and replies immediately to the client.
- The client is now running, as is the server.
- When it completes the work, the server uses MsgDeliverEvent() to inform the client that the work is complete.
For now, think of this structure as an invisible box that contains the event that the server uses to notify 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. 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 MsgDeliverEvent(), the server doesn't block—this is a non-blocking call. The client has the event delivered to it (by the kernel) and may then perform whatever actions are appropriate.