dillon@CORY.BERKELEY.EDU (Matt Dillon) (12/25/87)
Never Wait() on the signal mask for the reply port and assume the
request has been returned when the Wait() returns.
The proper way to Wait() for a request using Wait() rather than
WaitIO() is:
long sigmask = 1L << req->io_Message.mn_ReplyPort->mp_SigBit;
while (!CheckIO(req))
Wait(sigmask);
Though usually one simply does a WaitIO(req)
Equivalent operations: (pseudo code)
CheckIO(req)
{
if (req->io_Flags & IOF_QUICK)
return(req)
if (req->io_Message.mn_Node.ln_Type == NT_REPLYMSG)
return(req)
return(NULL)
}
WaitIO(req)
{
long sigmask = 1L << req->io_Message.mn_ReplyPort->mp_SigBit;
while (!CheckIO(req))
Wait(sigmask)
if (!(req->io_Flags & IOF_QUICK))
Unlink(req);
return(req);
}
WaitIO(req)
{
long sigmask = 1L << req->io_Message.mn_ReplyPort->mp_SigBit;
if (req->io_Flags & IOF_QUICK)
return(req);
while (req->io_Message.mn_Node.ln_Type != NT_REPLYMSG)
Wait(sigmask)
UnLink(req);
return(req);
}
/*
* This works if the reply port is used for only one request, but
* is in general BAD CODE and should not be used.
*/
WaitIO(req)
{
WaitPort(req->io_Message.mn_ReplyPort); /* wait on port */
GetMsg(req->io_Message.mn_ReplyPort); /* unlink request */
}
/*
* HOWEVER, if you are waiting for N requests to complete, and they
* all get returned to the same port, you can do this:
*
* Note that variations of this method are used to Wait for the first
* of N requests to come back and act on it with very little overhead!
* In such cases it should be noted that the request is returned by
* GetMsg(). EXAMPLE: You want to read a packet from the serial
* device but also want to abort after 3 seconds if it doesn't all come
* in. Use the same reply port, then WaitPort()/GetMsg() to get the
* first request that completed (timer request or serial request), then
* AbortIO()/WaitIO() the other request.
*/
WaitNComplete(port, n)
{
while (n) {
WaitPort(port);
while (n && GetMsg(port))
--n;
}
}
/*
* This call assumes the device driver will make the request asyncronous
* if it cannot immediately fullfill it.
*/
DoIOWithTimeout(req, timerreq, replyport)
{
REQ *retreq;
req->io_Message.mn_ReplyPort = replyport; /* DoIO */
req->io_Flags = IOF_QUICK;
BeginIO(req);
if (req->io_Flags & IOF_QUICK) /* syncronous! */
return(1);
/* asyncronous, the request will be replied */
timerreq->io_Message.mn_ReplyPort = replyport;
SendIO(timereq);
WaitPort(replyport); /* ASSUME PORT USED ONLY FOR THESE TWO REQS */
retreq = GetMsg(replyport); /* guarenteed to return something */
if (retreq == req) { /* request finished first */
AbortIO(timerreq);
WaitIO(timerreq);
return(1);
} else { /* timeout occured first */
AbortIO(req);
WaitIO(req);
return(0);
}
}
DoIO(req)
{
req->io_Flags = IOF_QUICK;
BeginIO(req);
if (!(req->io_Flags & IOF_QUICK))
WaitIO(req);
return(req->io_Error);
}
void
SendIO(req)
{
req->io_Flags = 0;
BeginIO(req);
}
/*
* This is what ReplyMsg does. Note how the Type field is modified for
* both ReplyMsg() and PutMsg()
*/
void
ReplyMsg(msg)
{
if (msg->mn_Node.ln_ReplyPort == NULL) { /* no reply port */
msg->mn_Node.ln_Type = NT_FREEMSG;
return;
}
msg->mn_Node.ln_Type = NT_REPLYMSG;
PutMsgSkip(msg->mn_Node.ln_ReplyPort, msg);
Where PutMsgSkip() is PutMsg() skipping the part where the type is
set to NT_MESSAGE. That is, the type stays at NT_REPLYMSG.
}
/*
* This is what PutMsg does. Note that you can SAFELY PutMsg()/ReplyMsg()
* from an interrupt service routine!
*/
PutMsg(port,msg)
{
msg->mn_Node.ln_Type = NT_MESSAGE;
/* then Disable(), place message in reply port, Enable(), do whatever
* signalling is required, and return. Poof
*/
}dillon@CORY.BERKELEY.EDU (Matt Dillon) (12/25/87)
:PutMsg(port,msg)
:{
: msg->mn_Node.ln_Type = NT_MESSAGE;
:
: /* then Disable(), place message in reply port, Enable(), do whatever
: * signalling is required, and return. Poof
: */
:}
I meant "place message in port", not "reply port"..
-MAtt