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