/**************************************************************************** ** File: pipesched.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added "lockct" check in CheckWaiting(). */ #include #include #include #include #include "pipelists.h" #include "pipename.h" #include "pipebuf.h" #include "pipecreate.h" #include "pipesched.h" #include "pipe-handler.h" #if PIPEDIR # include "pipedir.h" #endif PIPEDIR #ifdef DEBUG # include "pipedebug.h" #endif DEBUG /*--------------------------------------------------------------------------- ** pipesched.c ** ----------- ** This module handles pipe I/O scheduling. ** ** Visible Functions ** ----------------- ** void StartPipeIO (pkt, iotype) ** void CheckWaiting (pipe) ** struct DosPacket *AllocPacket (ReplyPort) ** void FreePacket (pkt) ** void StartTapIO (pkt, Type, Arg1, Arg2, Arg3, Handler) ** void HandleTapReply (pkt) ** ** Macros (in pipesched.h) ** ----------------------- ** - none - ** ** Local Functions ** --------------- ** void EndPipeIO (pipe, wd) */ /*--------------------------------------------------------------------------- ** A pipe I/O request is begun. A WAITINGDATA structure is allocated and ** the request is stored in it. It is then stored in the appropriate list ** (readerlist or writerlist) of the pipe. Finally, CheckWaiting is called ** to service requests for that pipe. ** Notice that CheckWaiting() is only called when a new I/O request ** comes in, or when the pipe is closed. At no other time will the state of ** the pipe change in such a way that more requests for it can be honored. */ void StartPipeIO (pkt, iotype) struct DosPacket *pkt; IOTYPE iotype; /* assumed only PIPEREAD or PIPEWRITE */ { PIPEKEY *pipekey; PIPEDATA *pipe; WAITINGDATA *wd; if ((iotype != PIPEREAD) && (iotype != PIPEWRITE)) { pkt->dp_Res2= ERROR_ACTION_NOT_KNOWN; SPIOEXIT: pkt->dp_Res1= -1; ReplyPkt (pkt); return; } pipekey= (PIPEKEY *) pkt->dp_Arg1; pipe= pipekey->pipe; if ((wd= (WAITINGDATA *) AllocMem (sizeof (WAITINGDATA), ALLOCMEM_FLAGS)) == NULL) { pkt->dp_Res2= ERROR_NO_FREE_STORE; goto SPIOEXIT; } pkt->dp_Res2= ERROR_INVALID_LOCK; /* in case not open for iotype */ if (iotype == PIPEREAD) { if ((pipekey->iotype != PIPEREAD) && (pipekey->iotype != PIPERW)) goto SPIOEXIT; InsertTail (&pipe->readerlist, wd); } else /* PIPEWRITE */ { if ((pipekey->iotype != PIPEWRITE) && (pipekey->iotype != PIPERW)) goto SPIOEXIT; InsertTail (&pipe->writerlist, wd); } wd->pkt= pkt; wd->pktinfo.pipewait.reqtype= iotype; wd->pktinfo.pipewait.buf= (BYTE *) pkt->dp_Arg2; /* buffer */ wd->pktinfo.pipewait.len= (ULONG) pkt->dp_Arg3; /* length */ CheckWaiting (pipe); } /*--------------------------------------------------------------------------- ** Read requests for the pipe are satisfied until the pipe is empty or no ** more requests are left. Then, write requests are satisifed until the pipe ** is full or no more requests are left. This alternating process is ** repeated until no further changes are possible. ** Finished requests are sent to EndPipeIO() so that replies may be sent ** to their owners. Aftereward, if the pipe is empty and is not open for ** either read or write, then it is discarded. If it is open for read, but ** is empty and has no write requests and is not open for write, then all ** remaining read requests are returned in their current state. (This ** implements EOF.) A pipe with a positive "lockct" will not be discarded. ** UnLock() is expected to call here so that a previously locked, empty pipe ** will be discarded. */ void CheckWaiting (pipe) PIPEDATA *pipe; { BYTE change; WAITINGDATA *wd; ULONG amt; void EndPipeIO(); #if PIPEDIR SetPipeDate (pipe); #endif PIPEDIR for (change= TRUE; change; ) { change= FALSE; while ( (! (PipebufEmpty (pipe->buf))) && ((wd= (WAITINGDATA *) FirstItem (&pipe->readerlist)) != NULL) ) { amt= MoveFromPipebuf (pipe->buf, wd->pktinfo.pipewait.buf, wd->pktinfo.pipewait.len); if (amt) { wd->pktinfo.pipewait.buf += amt; wd->pktinfo.pipewait.len -= amt; change= TRUE; } if (wd->pktinfo.pipewait.len == 0L) /* then finished with request */ EndPipeIO (pipe, wd); } /* end of readerlist loop */ while ( (! (PipebufFull (pipe->buf))) && ((wd= (WAITINGDATA *) FirstItem (&pipe->writerlist)) != NULL) ) { amt= MoveToPipebuf (pipe->buf, wd->pktinfo.pipewait.buf, wd->pktinfo.pipewait.len); if (amt) { wd->pktinfo.pipewait.buf += amt; wd->pktinfo.pipewait.len -= amt; change= TRUE; } if (wd->pktinfo.pipewait.len == 0L) /* then finished with request */ EndPipeIO (pipe, wd); } /* end of writerlist loop */ } if ( PipebufEmpty (pipe->buf) && (! (pipe->flags & OPEN_FOR_WRITE)) && (FirstItem (&pipe->writerlist) == NULL) ) /* then EOF */ { while ((wd= (WAITINGDATA *) FirstItem (&pipe->readerlist)) != NULL) EndPipeIO (pipe, wd); if (! (pipe->flags & OPEN_FOR_READ)) /* readerlist is now empty */ #if PIPEDIR if (pipe->lockct == 0) #endif PIPEDIR DiscardPipe (pipe); } } /*--------------------------------------------------------------------------- ** This routine returns a finished pipe I/O request. If it is a write ** request to a pipe with a tap, then the same write request is sent to the ** tap, and the reply is deferred until HandleTapReply() gets the tap request ** reply. (This lets the user stop a pipe by typing a character into a ** tap window.) */ static void EndPipeIO (pipe, wd) PIPEDATA *pipe; WAITINGDATA *wd; { struct DosPacket *pkt, *tappkt; struct FileHandle *taphandle; pkt= wd->pkt; pkt->dp_Res1= pkt->dp_Arg3 - wd->pktinfo.pipewait.len; pkt->dp_Res2= 0; if (wd->pktinfo.pipewait.reqtype == PIPEREAD) { Delete (&pipe->readerlist, wd); ReplyPkt (pkt); FreeMem (wd, sizeof (WAITINGDATA)); } else /* must be PIPEWRITE -- reqtype is new PIPERW */ { Delete (&pipe->writerlist, wd); if (pipe->tapfh != 0) /* then write to the pipe tap */ { if ((tappkt= AllocPacket (TapReplyPort)) == NULL) { ReplyPkt (pkt); FreeMem (wd, sizeof (WAITINGDATA)); #ifdef DEBUG OS ("!!! ERROR - Could not allocate packet for tap write\n"); #endif DEBUG } else { wd->pkt= tappkt; /* reuse wd for tap write request */ wd->pktinfo.tapwait.clientpkt= pkt; /* don't need ...tapwait.handle */ taphandle= (struct FileHandle *) BPTRtoCptr (pipe->tapfh); StartTapIO ( tappkt, ACTION_WRITE, taphandle->fh_Arg1, pkt->dp_Arg2, pkt->dp_Arg3, taphandle->fh_Type ); InsertHead (&tapwaitlist, wd); /* for HandleTapReply() */ } } else /* otherwise, return finished packet */ { ReplyPkt (pkt); FreeMem (wd, sizeof (WAITINGDATA)); } } } /*--------------------------------------------------------------------------- ** An exec Message and a DosPacket are allocated, and they are initialized. ** A pointer to the packet is returned, or NULL if it could not be allocated. */ struct DosPacket *AllocPacket (ReplyPort) struct MsgPort *ReplyPort; { struct Message *msg; struct DosPacket *pkt; if ((msg = (struct Message *) AllocMem (sizeof (struct Message), (ALLOCMEM_FLAGS | MEMF_CLEAR))) == NULL) return NULL; if ((pkt = (struct DosPacket *) AllocMem (sizeof (struct DosPacket), (ALLOCMEM_FLAGS | MEMF_CLEAR))) == NULL) { FreeMem (msg, sizeof (struct Message)); return NULL; } msg->mn_Node.ln_Type= NT_MESSAGE; msg->mn_Node.ln_Name= (char *) pkt; msg->mn_ReplyPort= ReplyPort; pkt->dp_Link= msg; pkt->dp_Port= ReplyPort; return pkt; } /*--------------------------------------------------------------------------- ** A DosPacket/exec Message pair is freed. */ void FreePacket (pkt) struct DosPacket *pkt; { if (pkt != NULL) { if (pkt->dp_Link != NULL) FreeMem (pkt->dp_Link, sizeof (struct Message)); FreeMem (pkt, sizeof (struct DosPacket)); } } /*--------------------------------------------------------------------------- ** The indicated fields are filled into the packet and it is sent. */ void StartTapIO (pkt, Type, Arg1, Arg2, Arg3, Handler) struct DosPacket *pkt; LONG Type; LONG Arg1; LONG Arg2; LONG Arg3; struct MsgPort *Handler; { pkt->dp_Type= Type; pkt->dp_Arg1= Arg1; pkt->dp_Arg2= Arg2; pkt->dp_Arg3= Arg3; PutMsg (Handler, pkt->dp_Link); } /*--------------------------------------------------------------------------- ** Handle replies from tap I/O requests. These were initiated by OpenTap(), ** CloseTap() and EndPipeIO(). */ void HandleTapReply (pkt) struct DosPacket *pkt; { WAITINGDATA *wd; for (wd= (WAITINGDATA *) FirstItem (&tapwaitlist); wd != NULL; wd= (WAITINGDATA *) NextItem (wd)) if (wd->pkt == pkt) { Delete (&tapwaitlist, wd); break; } if (wd == NULL) { #ifdef DEBUG OS ("!!! ERROR - WAITINGDATA not found in HandleTapReply()\n"); #endif DEBUG FreePacket (pkt); return; /* not found - this should never happen */ } switch (pkt->dp_Type) { case MODE_READWRITE: case MODE_READONLY: case MODE_NEWFILE: /* for a tap open request */ if (pkt->dp_Res1) /* then successful */ OpenPipe (wd->pktinfo.tapwait.clientpkt, pkt->dp_Arg1); else /* couldn't open tap */ { FreeMem (wd->pktinfo.tapwait.handle, sizeof (struct FileHandle)); pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; ReplyPkt (wd->pktinfo.tapwait.clientpkt); } FreeMem (BPTRtoCptr (pkt->dp_Arg3), OPENTAP_STRSIZE); break; case ACTION_END: /* for a tap close request */ FreeMem (wd->pktinfo.tapwait.handle, sizeof (struct FileHandle)); break; case ACTION_WRITE: /* for a tap write request */ ReplyPkt (wd->pktinfo.tapwait.clientpkt); /* return to client */ break; #ifdef DEBUG default: /* should never happen */ OS ("!!! ERROR - bad packet type in HandleTapReply(), type ="); OL (pkt->dp_Type); NL; #endif DEBUG } FreePacket (pkt); FreeMem (wd, sizeof (WAITINGDATA)); }