[comp.sys.amiga] Another don't for IO:

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