1 /****************************************************************************
3 ** Program: pipe-handler - an AmigaDOS handler for named pipes
5 ** Author: Ed Puckett qix@mit-oz
7 ** Copyright 1987 by EpAc Software. All Rights Reserved.
9 ** History: 05-Jan-87 Original Version (1.0)
10 ** 07-Feb-87 Added "lockct" check in CheckWaiting().
13 #include <libraries/dos.h>
14 #include <libraries/dosextens.h>
15 #include <libraries/filehandler.h>
16 #include <exec/exec.h>
18 #include "pipelists.h"
21 #include "pipecreate.h"
22 #include "pipesched.h"
23 #include "pipe-handler.h"
30 # include "pipedebug.h"
35 /*---------------------------------------------------------------------------
38 ** This module handles pipe I/O scheduling.
42 ** void StartPipeIO (pkt, iotype)
43 ** void CheckWaiting (pipe)
44 ** struct DosPacket *AllocPacket (ReplyPort)
45 ** void FreePacket (pkt)
46 ** void StartTapIO (pkt, Type, Arg1, Arg2, Arg3, Handler)
47 ** void HandleTapReply (pkt)
49 ** Macros (in pipesched.h)
50 ** -----------------------
55 ** void EndPipeIO (pipe, wd)
58 static void EndPipeIO (PIPEDATA
*pipe
, WAITINGDATA
*wd
);
62 /*---------------------------------------------------------------------------
63 ** A pipe I/O request is begun. A WAITINGDATA structure is allocated and
64 ** the request is stored in it. It is then stored in the appropriate list
65 ** (readerlist or writerlist) of the pipe. Finally, CheckWaiting is called
66 ** to service requests for that pipe.
67 ** Notice that CheckWaiting() is only called when a new I/O request
68 ** comes in, or when the pipe is closed. At no other time will the state of
69 ** the pipe change in such a way that more requests for it can be honored.
72 void StartPipeIO (pkt
, iotype
)
74 struct DosPacket
*pkt
;
75 IOTYPE iotype
; /* assumed only PIPEREAD or PIPEWRITE */
82 if ((iotype
!= PIPEREAD
) && (iotype
!= PIPEWRITE
))
83 { pkt
->dp_Res2
= ERROR_ACTION_NOT_KNOWN
;
91 pipekey
= (PIPEKEY
*) pkt
->dp_Arg1
;
94 if ((wd
= (WAITINGDATA
*) AllocMem (sizeof (WAITINGDATA
), ALLOCMEM_FLAGS
)) == NULL
)
95 { pkt
->dp_Res2
= ERROR_NO_FREE_STORE
;
100 pkt
->dp_Res2
= ERROR_INVALID_LOCK
; /* in case not open for iotype */
102 if (iotype
== PIPEREAD
)
103 { if ((pipekey
->iotype
!= PIPEREAD
) && (pipekey
->iotype
!= PIPERW
))
106 InsertTail (&pipe
->readerlist
, wd
);
109 { if ((pipekey
->iotype
!= PIPEWRITE
) && (pipekey
->iotype
!= PIPERW
))
112 InsertTail (&pipe
->writerlist
, wd
);
117 wd
->pktinfo
.pipewait
.reqtype
= iotype
;
118 wd
->pktinfo
.pipewait
.buf
= (BYTE
*) pkt
->dp_Arg2
; /* buffer */
119 wd
->pktinfo
.pipewait
.len
= (ULONG
) pkt
->dp_Arg3
; /* length */
126 /*---------------------------------------------------------------------------
127 ** Read requests for the pipe are satisfied until the pipe is empty or no
128 ** more requests are left. Then, write requests are satisifed until the pipe
129 ** is full or no more requests are left. This alternating process is
130 ** repeated until no further changes are possible.
131 ** Finished requests are sent to EndPipeIO() so that replies may be sent
132 ** to their owners. Aftereward, if the pipe is empty and is not open for
133 ** either read or write, then it is discarded. If it is open for read, but
134 ** is empty and has no write requests and is not open for write, then all
135 ** remaining read requests are returned in their current state. (This
136 ** implements EOF.) A pipe with a positive "lockct" will not be discarded.
137 ** UnLock() is expected to call here so that a previously locked, empty pipe
138 ** will be discarded.
141 void CheckWaiting (pipe
)
155 for (change
= TRUE
; change
; )
158 while ( (! (PipebufEmpty (pipe
->buf
))) &&
159 ((wd
= (WAITINGDATA
*) FirstItem (&pipe
->readerlist
)) != NULL
) )
160 { amt
= MoveFromPipebuf (pipe
->buf
, wd
->pktinfo
.pipewait
.buf
, wd
->pktinfo
.pipewait
.len
);
163 { wd
->pktinfo
.pipewait
.buf
+= amt
;
164 wd
->pktinfo
.pipewait
.len
-= amt
;
168 if (wd
->pktinfo
.pipewait
.len
== 0L) /* then finished with request */
169 EndPipeIO (pipe
, wd
);
170 } /* end of readerlist loop */
173 while ( (! (PipebufFull (pipe
->buf
))) &&
174 ((wd
= (WAITINGDATA
*) FirstItem (&pipe
->writerlist
)) != NULL
) )
175 { amt
= MoveToPipebuf (pipe
->buf
, wd
->pktinfo
.pipewait
.buf
, wd
->pktinfo
.pipewait
.len
);
178 { wd
->pktinfo
.pipewait
.buf
+= amt
;
179 wd
->pktinfo
.pipewait
.len
-= amt
;
183 if (wd
->pktinfo
.pipewait
.len
== 0L) /* then finished with request */
184 EndPipeIO (pipe
, wd
);
185 } /* end of writerlist loop */
189 if ( PipebufEmpty (pipe
->buf
) &&
190 (! (pipe
->flags
& OPEN_FOR_WRITE
)) &&
191 (FirstItem (&pipe
->writerlist
) == NULL
) ) /* then EOF */
192 { while ((wd
= (WAITINGDATA
*) FirstItem (&pipe
->readerlist
)) != NULL
)
193 EndPipeIO (pipe
, wd
);
195 if (! (pipe
->flags
& OPEN_FOR_READ
)) /* readerlist is now empty */
197 if (pipe
->lockct
== 0)
205 /*---------------------------------------------------------------------------
206 ** This routine returns a finished pipe I/O request. If it is a write
207 ** request to a pipe with a tap, then the same write request is sent to the
208 ** tap, and the reply is deferred until HandleTapReply() gets the tap request
209 ** reply. (This lets the user stop a pipe by typing a character into a
213 static void EndPipeIO (pipe
, wd
)
218 { struct DosPacket
*pkt
, *tappkt
;
219 struct FileHandle
*taphandle
;
224 pkt
->dp_Res1
= pkt
->dp_Arg3
- wd
->pktinfo
.pipewait
.len
;
227 if (wd
->pktinfo
.pipewait
.reqtype
== PIPEREAD
)
228 { Delete (&pipe
->readerlist
, wd
);
231 FreeMem (wd
, sizeof (WAITINGDATA
));
233 else /* must be PIPEWRITE -- reqtype is new PIPERW */
234 { Delete (&pipe
->writerlist
, wd
);
236 if (pipe
->tapfh
!= 0) /* then write to the pipe tap */
237 { if ((tappkt
= AllocPacket (TapReplyPort
)) == NULL
)
238 { QuickReplyPkt (pkt
);
239 FreeMem (wd
, sizeof (WAITINGDATA
));
241 OS ("!!! ERROR - Could not allocate packet for tap write\n");
245 { wd
->pkt
= tappkt
; /* reuse wd for tap write request */
246 wd
->pktinfo
.tapwait
.clientpkt
= pkt
;
247 /* don't need ...tapwait.handle */
249 taphandle
= (struct FileHandle
*) BPTRtoCptr (pipe
->tapfh
);
251 StartTapIO ( tappkt
, ACTION_WRITE
,
252 taphandle
->fh_Arg1
, pkt
->dp_Arg2
, pkt
->dp_Arg3
,
253 taphandle
->fh_Type
);
255 InsertHead (&tapwaitlist
, wd
); /* for HandleTapReply() */
258 else /* otherwise, return finished packet */
259 { QuickReplyPkt (pkt
);
260 FreeMem (wd
, sizeof (WAITINGDATA
));
267 /*---------------------------------------------------------------------------
268 ** An exec Message and a DosPacket are allocated, and they are initialized.
269 ** A pointer to the packet is returned, or NULL if it could not be allocated.
272 struct DosPacket
*AllocPacket (ReplyPort
)
274 struct MsgPort
*ReplyPort
;
276 { struct Message
*msg
;
277 struct DosPacket
*pkt
;
280 if ((msg
= (struct Message
*) AllocMem (sizeof (struct Message
), (ALLOCMEM_FLAGS
| MEMF_CLEAR
))) == NULL
)
283 if ((pkt
= (struct DosPacket
*) AllocMem (sizeof (struct DosPacket
), (ALLOCMEM_FLAGS
| MEMF_CLEAR
))) == NULL
)
284 { FreeMem (msg
, sizeof (struct Message
));
288 msg
->mn_Node
.ln_Type
= NT_MESSAGE
;
289 msg
->mn_Node
.ln_Name
= (char *) pkt
;
291 msg
->mn_ReplyPort
= ReplyPort
;
294 pkt
->dp_Port
= ReplyPort
;
301 /*---------------------------------------------------------------------------
302 ** A DosPacket/exec Message pair is freed.
305 void FreePacket (pkt
)
307 struct DosPacket
*pkt
;
310 { if (pkt
->dp_Link
!= NULL
)
311 FreeMem (pkt
->dp_Link
, sizeof (struct Message
));
313 FreeMem (pkt
, sizeof (struct DosPacket
));
319 /*---------------------------------------------------------------------------
320 ** The indicated fields are filled into the packet and it is sent.
323 void StartTapIO (pkt
, Type
, Arg1
, Arg2
, Arg3
, Handler
)
325 struct DosPacket
*pkt
;
330 struct MsgPort
*Handler
;
332 { pkt
->dp_Type
= Type
;
337 PutMsg (Handler
, pkt
->dp_Link
);
342 /*---------------------------------------------------------------------------
343 ** Handle replies from tap I/O requests. These were initiated by OpenTap(),
344 ** CloseTap() and EndPipeIO().
347 void HandleTapReply (pkt
)
349 struct DosPacket
*pkt
;
354 for (wd
= (WAITINGDATA
*) FirstItem (&tapwaitlist
); wd
!= NULL
; wd
= (WAITINGDATA
*) NextItem (wd
))
356 { Delete (&tapwaitlist
, wd
);
363 OS ("!!! ERROR - WAITINGDATA not found in HandleTapReply()\n");
366 return; /* not found - this should never happen */
369 switch (pkt
->dp_Type
)
370 { case MODE_READWRITE
:
372 case MODE_NEWFILE
: /* for a tap open request */
373 if (pkt
->dp_Res1
) /* then successful */
374 OpenPipe (wd
->pktinfo
.tapwait
.clientpkt
, pkt
->dp_Arg1
);
375 else /* couldn't open tap */
376 { FreeMem (wd
->pktinfo
.tapwait
.handle
, sizeof (struct FileHandle
));
378 pkt
->dp_Res2
= ERROR_INVALID_COMPONENT_NAME
;
379 QuickReplyPkt (wd
->pktinfo
.tapwait
.clientpkt
);
382 FreeMem (BPTRtoCptr (pkt
->dp_Arg3
), OPENTAP_STRSIZE
);
385 case ACTION_END
: /* for a tap close request */
386 FreeMem (wd
->pktinfo
.tapwait
.handle
, sizeof (struct FileHandle
));
389 case ACTION_WRITE
: /* for a tap write request */
390 QuickReplyPkt (wd
->pktinfo
.tapwait
.clientpkt
); /* return to client */
394 default: /* should never happen */
395 OS ("!!! ERROR - bad packet type in HandleTapReply(), type ="); OL (pkt
->dp_Type
); NL
;
400 FreeMem (wd
, sizeof (WAITINGDATA
));