Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_SRC_1_19910112
-
6-1-monitor/pipe.mac
There are 4 other files named pipe.mac in the archive. Click here to see a list.
;<6-1-MONITOR>PIPE.MAC.10, 26-Mar-85 11:09:11, Edit by LOUGHEED
; Prevent spurious PIPRLX buginfs due to GTJFN% parsing failures
;<6-MONITOR>PIPE.MAC.9, 25-Jun-84 16:55:05, Edit by LOUGHEED
; Fix version lookup under Release 6
;<6-MONITOR>PIPE.MAC.8, 21-Jun-84 02:06:23, Edit by LOUGHEED
;<6-MONITOR>PIPE.MAC.7, 19-Jun-84 20:26:23, Edit by LOUGHEED
; Conditional assembly for Release 6
;<5-3-MONITOR>PIPE.MAC.6, 1-Jan-84 22:03:41, Edit by LOUGHEED
;<5-3-MONITOR>PIPE.MAC.5, 1-Jan-84 19:25:45, Edit by LOUGHEED
; If both ends of a pipe are opened in mode 1 (.GSSMB) no buffering
; is done. Makes interactive applications more viable.
;<5-3-MONITOR>PIPE.MAC.4, 26-Nov-83 23:31:58, Edit by LOUGHEED
; Release pipe data structures if GTJFN% version lookup fails
;<5-3-MONITOR>PIPE.MAC.3, 29-Oct-83 02:21:22, Edit by LOUGHEED
; PIPCLZ doesn't try to flush a non-existent queued buffer
;<5-3-MONITOR>PIPE.MAC.2, 29-Oct-83 01:57:59, Edit by LOUGHEED
; Make PIPNAM and PIPEXT a bit smarter about rejecting alphabetic strings
;<5-3-MONITOR>PIPE.MAC.1, 29-Oct-83 00:04:51, Edit by LOUGHEED
; Initial production version
SEARCH PROLOG
TTITLE(PIPE,PIPE,< -- I/O PIPES>)
SUBTTL Kirk Lougheed / Stanford University / 13-October-1983
; Copyright (c) 1983, 1984, 1985 by
; The Board of Trustees, Leland Stanford Junior University, Stanford, CA 94305
; All Rights Reserved.
; The information in this software is subject to change without notice
; and should not be construed as a commitment by Stanford University.
; Stanford assumes no responsibility for the use or reliability of
; this software.
SUBTTL Definitions and Storage
IFNDEF REL6,<REL6==1> ;Default to assembling for Release 6.0
;AC redefinitions
DEFAC (PIP,Q1) ;Index into pipe device data structures
DEFAC (IOS,Q2) ;I/O status bits
DEFAC (STS,P1) ;File status flags
DEFAC (JFN,P2) ;The current JFN
DEFAC (DEV,P4) ;Device bits,,DTB address
DEFAC (F1,P5) ;GTJFN% flags
DEFSTR FILPIP,FILOFN,35,18 ;Pipe device number (index into PIPSTS)
EXTN <NPIPES,PIPSTS,PIPST1,PIPBUF,PIPBF1> ;JSB storage defined in STG
;NPIPES==^D10 ;Maximum number of pipe devices per job
DEFWSZ==^D100 ;Default word size of a pipe buffer
MAXWSZ==777 ;Maximum word size of a pipe buffer
;JS PIPSTS,NPIPES
PIASGF==1B0 ;Assigned by GTJFN%
PIREDF==1B1 ;Open for read
PIWRTF==1B2 ;Open for write
PIQEDF==1B3 ;Queued buffer contains valid data
PIWCLF==1B4 ;EOF (write side was open, is now closed)
PIRCLF==1B5 ;EOF (read side was open, is now closed)
PILCKF==1B6 ;Pipe is locked
PIINTF==1B7 ;Interactive pipe (.GSSMB open mode)
DEFSTR PIPBSZ,PIPSTS,23,6 ;Byte size of pipe
DEFSTR PIPWSZ,PIPSTS,35,12 ;Number of words in buffer
;JS PIPST1,NPIPES
DEFSTR PIPBCT,PIPST1,17,18 ;Bytes/buffer for this byte size
DEFSTR PIPWTB,PIPST1,26,9 ;Wait bit for PIQEDF flag
DEFSTR PIPCNT,PIPST1,35,9 ;JFN share count for this pipe
;JS PIPBUF,NPIPES
DEFSTR PIPINP,PIPBUF,35,18 ;Input buffer pointer
DEFSTR PIPOUT,PIPBUF,17,18 ;Output buffer pointer
;JS PIPBF1,NPIPES
DEFSTR PIPQED,PIPBF1,35,18 ;Queued buffer pointer
DEFSTR PIPQCT,PIPBF1,17,18 ;Byte count of queued data
;This storage used by all jobs.
NUMWTW==^D10 ;Number of words of wait bits
RS PIPBTB,NUMWTW ;Table of wait bits (must be resident)
RS PIPBFF,NUMWTW ;Table of assigned wait bits (must be resident)
NR PIPWTF,1 ;-1 if wait bit pool initialized
SUBTTL Pipe Device Dispatch Table
SWAPCD
PIPDTB::
IFN REL6,<PIPDTL> ;Length of dispatch table
DTBDSP PIPSET ;*Directory setup routine
DTBDSP PIPNAM ;*Name lookup
DTBDSP PIPEXT ;*Extension lookup
DTBDSP PIPVER ;*Version lookup
DTBBAD (DESX9) ; Protection insertion
DTBBAD (DESX9) ; Account insertion
DTBBAD (DESX9) ; Status insertion
DTBDSP PIPOPN ;*Open file
DTBDSP PIPSQI ;*Byte input
DTBDSP PIPSQO ;*Byte output
DTBDSP PIPCLZ ;*Close file
DTBBAD (DESX9) ; Rename
DTBBAD (DESX9) ; Delete file
DTBBAD (DESX9) ; Dump mode input
DTBBAD (DESX9) ; Dump mode output
DTBBAD (DESX9) ; Mount device
DTBBAD (DESX9) ; Dismount device
DTBBAD (DESX9) ; Initialize directory
DTBBAD (DESX9) ; Do mtape operation
DTBDSP PIPGTD ;*Get device status (GDSTS%)
DTBBAD (DESX9) ; Set device status
DTBDSP PIPREC ;*Force record out (SOUTR%)
DTBDSP RFTADN ; Read file time and date
DTBDSP SFTADN ; Set file time and date
DTBDSP BIOINP ; Set JFN for input
DTBDSP BIOOUT ; Set JFN for output
DTBDSP PIPATR ;*Check attribute
DTBDSP PIPRJF ;*Release JFN
PIPDTL==.-PIPDTB
SUBTTL GTJFN% Parsing
COMMENT |
To create an instance of a pipe device, the user should give a string such as
"PIP:.;RECORD-SIZE:500" to GTJFN%. The record size attribute is optional and
controls the number of words in the pipe's buffers. The important thing is
that both the file name and file extension for the first GTJFN% string are
null strings. GTJFN% will create the pipe and use the pipe index as the file
name for that JFN. The user is then expected to use JFNS% to create a second
string of the form "PIP:#.#" where "#" is a small number, the file name
created by the first GTJFN%. It doesn't matter which JFN is used for read or
write. You may have multiple JFN's on a pipe, but only two may be opened, one
for read and one for write. When the last JFN on the pipe is released, the
pipe is destroyed. The buffer size attribute need only be specified once, but
if it is specified twice, it must be the same as the first time. The default
size is 100 (decimal) words.
|
;PIPSET - Set up directory field
PIPSET: TQNE <STEPF> ;Want to step?
RETBAD(GJFX17) ;Yes, error
NOINT ;Match the OKINT at return
IFN REL6,<RETSKP> ;Success return>
IFE REL6,<JRST SK2RET ;Success return>
;PIPVER - version lookup
;If we fail here we must decrement the share count *now* since RELJFX doesn't
;invoke the device dependent release JFN routine.
PIPVER:
IFE REL6,<JUMPE T1,GJSRET> ;Success if no version information.
IFN REL6,<JUMPE T1,GJ2RET> ;Success if no version information.
LOAD T1,FILPIP,(JFN) ;Don't want versions, get our pipe number
LOAD T2,PIPCNT,(T1) ;Get pipe share count
SUBI T2,2 ;Decrement it twice
STOR T2,PIPCNT,(T1) ;Store back updated count
MOVX T1,GJFX20 ;"No such generation number"
JRST GJERRX ;Take failure return
;PIPNAM - name lookup routine
;If the name string is non-null and numeric, we verify that the resulting
;number corresponds to a valid, assigned pipe device. If the name string is
;null, we need to create an instance of a pipe. This involves setting up the
;pipe data structure and creating a numeric name string for the JFN. Using
;the name string, the user can then get a second JFN on the pipe. The name
;string parsing is quite stupid, e.g. "2FOO" is treated the same as "2".
PIPNAM: SKIPN T1 ;Wildcard name?
RETERR (GJFX18) ;Yes, return an error
LDB T3,[POINT 7,1(T1),6] ;Get first character of string
JUMPE T3,PIPNA0 ;Null string means create a pipe
HRLI T1,(POINT 7,,35) ;Make string pointer
MOVEI T3,^D8 ;Pipes are octal
NIN% ;Read a number
RETERR(GJFX4) ;"Invalid character in filename"
SKIPLE T2 ;Zero or negative pipe number not allowed
CAIL T2,NPIPES ;Range check the pipe number
RETERR(ARGX19) ;"Invalid unit number"
MOVE T1,PIPSTS(T2) ;Get pipe status word
TXNN T1,PIASGF ;Already assigned?
RETERR(DIAGX2) ;"Device is not assigned"
STOR T2,FILPIP,(JFN) ;Remember our pipe number
JRST GJ2RET ;Take success return
;This code handles the case of creating a new pipe device.
PIPNA0: NOSKED ;No races
CALL GETPIP ;Get a new pipe device
RETERR(MONX01,<OKSKED>) ;"Insufficient system resources"
MOVX T1,PIASGF ;Get assignment flag
IORM T1,PIPSTS(T2) ;Make sure pipe is flagged as owned
OKSKED ;Reallow scheduling
STOR T2,FILPIP,(JFN) ;Remember our pipe number
HRRZ T1,FILTMP(JFN) ;Get address of temporary storage string
SETZM 1(T1) ;Clear first word (avoid storage header word)
MOVEI T2,2(T1) ;Form blt pointer in T2
HRLI T2,1(T1) ; ...
BLT T2,MAXLW(T1) ;Zero the temporary string, just in case
HRRZ T1,FILTMP(JFN) ;Address of temporary name string buffer
HRLI T1,(POINT 7,,35) ;Set up byte pointer
LOAD T2,FILPIP,(JFN) ;Get pipe number
MOVEI T3,^D8 ;Octal radix
NOUT% ;Replace null name with pipe name
RETERR(MONX03) ;"Monitor internal error"
MOVEM T1,FILOPT(JFN) ;Remember pointer to last byte
JRST GJ2RET ;Take success return
;PIPEXT - extension lookup
;Here we ensure that we have JFN's of the form "PIP:#." or "PIP:#.#" pointing
;to pipe number "#". If the tests are passed, the pipe share count is
;incremented.
PIPEXT: SKIPN T1 ;Wildcard name?
RETERR (GJFX18) ;Yes, return an error
LOAD T4,FILPIP,(JFN) ;Get pipe number we think we should have
LDB T3,[POINT 7,1(T1),6] ;Get first character of string
JUMPE T3,PIPEX0 ;Null string is okay
HRLI T1,(POINT 7,,35) ;Make string pointer
MOVEI T3,^D8 ;Pipes are octal
NIN% ;Read a number
RETERR(GJFX4) ;Invalid character in filename
CAME T4,T2 ;Extension matches name?
RETERR(GJFX16) ;No, "No such device"
PIPEX0: INCR PIPCNT,(T4) ;Increment share count
JRST GJ2RET ;Take success return
;PIPATR - check pipe attribute
;We use the "RECORD-LENGTH" attribute for magtapes to specify the number of
; words in a pipe buffer. This is currently the only attribute of a pipe
; that a user can specify.
;Takes T1/ address of attribute value string
; T2/ attribute code
;Returns +1 failure, T1/ error code
; +2 success
PIPATR: CALL SAVPIP ;Preserve our special AC's
CAIE T2,.PFRLN ;Is this the record length attribute?
RETBAD(GJFX49) ;"Invalid attribute for this device"
SKIPN T1 ;Pointer exist?
RETBAD(GJFX46) ;"Attribute value is required"
HRLI T1,010700 ;Fix up byte pointer
MOVEI T3,^D10 ;Want a decimal number
NIN% ;Read it
SETZ T2, ;Failed, pretend an illegal value was read
SKIPLE T2 ;Range check the value
CAILE T2,MAXWSZ ; ...
RETBAD(GJFX50) ;"Invalid argument for attribute"
LOAD PIP,FILPIP,(JFN) ;Get pipe index
CALL LCKPIP ;Lock the pipe
LOAD T1,PIPWSZ,(PIP) ;Get present word size
SKIPN T1 ;Skip if set
STOR T2,PIPWSZ,(PIP) ;Else save the new word size
CAME T1,T2 ;Sizes had better match
JUMPN T1,[RETBAD(GJFX50,<CALL ULKPIP>)] ;They don't, return error
IORB IOS,PIPSTS(PIP) ;Make sure IOS and PIPSTS(PIP) agree
CALL ULKPIP ;Unlock the pipe
RETSKP ;Skip return to caller
;GETPIP - find a free pipe device index
;Returns +1 failure, no free pipes
; +2 success, T2/ pipe index
;Note that zero is always an invalid pipe index
;Caller must be NOSKED to avoid races
;Clobbers T1-T2
GETPIP: MOVE T2,[XWD -NPIPES+1,1] ;Form aobjn pointer, avoid zero
GETPI0: MOVE T1,PIPSTS(T2) ;Get status bits
TXNE T1,PIASGF ;Assigned?
AOBJN T2,GETPI0 ;Yes, try next pipe
JUMPGE T2,R ;Maybe take failure return
HRRZS T2 ;Clear extraneous bits
RETSKP ;Return success, T2/ index
;Common GTJFN% returns
IFE REL6,<
GJ2RET: AOS 0(P) ;Double skip
GJSRET: AOS 0(P) ;Single skip
>;IFE REL6
IFN REL6,<
GJ2RET: AOS 0(P) ;Skip return
GJSRET: ;Single return
>;IFN REL6
TQNE <UNLKF> ;Should we unlock?
RET ;No
GJERRX: OKINT ;Yes
RET
SUBTTL OPENF% Routines
;PIPOPN - device dependent OPENF% code
;Returns +1 failure, T1/ error code
; +2 success, one side of pipe opened
PIPOPN: CALL SAVPIP ;Preserve PIP and IOS
TQNN <XCTF,RNDF> ;Execute and append are illegal
TQNN <READF,WRTF> ;Must be reading or writing
RETBAD(OPNX13) ;Illegal access
TQC <READF,WRTF> ;A pipe end is either read or write
TQCN <READF,WRTF> ; ....
RETBAD(OPNX13) ;Both read and write is illegal
LOAD PIP,FILPIP,(JFN) ;Get index into pipe data structures
CALL LCKPIP ;Lock pipe data structures
MOVX T1,PIREDF ;Assume trying for read access
TQNN <READF> ;True?
MOVX T1,PIWRTF ;No, write access
TDNE T1,IOS ;Don't open an end of a pipe twice
RETBAD(OPNX9,<CALL ULKPIP>) ;"Invalid simultaneous access"
;Determine data mode.
LDB T1,[POINT 4,STS,35] ;Get four bits of open mode
CAIE T1,.GSNRM ;Normal (mode 0)
CAIN T1,.GSSMB ;Small buffer (mode 1). Interactive use.
TRNA ;Good mode
RETBAD(OPNX14,<CALL ULKPIP>) ;"Invalid mode requested"
TXNN IOS,PIINTF ;Other side opened in interactive mode?
IFSKP.
CAIE T1,.GSSMB ;Yes, make sure we are doing same
RETBAD(OPNX14,<CALL ULKPIP>) ;"Invalid mode requested"
ENDIF.
CAIN T1,.GSSMB ;Opening in interactive mode?
TXO IOS,PIINTF ;Yes, make sure flag set
;Determine buffer and byte sizes.
LOAD T1,PIPWSZ,(PIP) ;Get word size of buffers
MOVEI T2,DEFWSZ ;Get default buffer size
SKIPN T1 ;If buffer size not specified
STOR T2,PIPWSZ,(PIP) ;Then use the default
LDB T1,PBYTSZ ;Get OPENF% byte size
LOAD T2,PIPBSZ,(PIP) ;Get pipe's byte size
SKIPN T2 ;Any byte size set yet?
STOR T1,PIPBSZ,(PIP) ;No, set the pipe's byte size
CAME T1,T2 ;OPENF% and pipe must match
JUMPN T2,[RETBAD(SFBSX1,<CALL ULKPIP>)] ;Return error otherwise
MOVEI T2,^D36 ;Bits per word
IDIVI T2,(T1) ;Divide by bits/byte to get bytes/word
LOAD T3,PIPWSZ,(PIP) ;Get words/buffer
IMULI T2,(T3) ;Multiply by words/buffer to get bytes/buffer
TXNE IOS,PIINTF ;Opened for interactive use?
SETZ T2, ;Yes, make FILCNT always be zero
STOR T2,PIPBCT,(PIP) ;Remember for later
IORB IOS,PIPSTS(PIP) ;Make sure byte info gets set in IOS as well
;Assign wait bit and buffers
LOAD T1,PIPWTB,(PIP) ;Do we have a wait bit yet?
IFE. T1
CALL ASNBIT ;Assign a wait bit
RETBAD(MONX01,<CALL ULKPIP>) ;"Insufficient system resources"
STOR T1,PIPWTB,(PIP) ;Store the wait bit
ENDIF.
XMOVEI T1,OPNRED ;Assume opening read side
TQNN <READF> ;Well?
XMOVEI T1,OPNWRT ;Opening write side
CALL (T1) ;Call appropriate routine
IFNSK.
IFXE. IOS,PIWRTF!PIREDF ;Failure, discard wait bit if no side is open
LOAD T1,PIPWTB,(PIP) ;Get our wait bit back
SETZRO PIPWTB,(PIP) ;Forget we ever had it
CALL RELBIT ;Release it
ENDIF.
RETBAD(MONX02,<CALL ULKPIP>) ;"Insufficient system resources"
ENDIF.
CALL ULKPIP ;Unlock pipe
RETSKP ;Success return
;OPNRED - open read side of pipe
;Returns +1 failure, T1/ error code
; +2 success
OPNRED: LOAD T2,PIPWSZ,(PIP) ;Get size of pipe buffer
ADDI T2,1 ;Count storage header
CALL ASGJFR ;Get some JSB free space
RET ;None, take failure return
STOR T1,PIPINP,(PIP) ;Save pointer
CALL SETBYT ;Set up FILBYT byte pointer
SETZM FILBYN(JFN) ;Current byte number is zero
SETZM FILCNT(JFN) ;No bytes in current buffer
TXO IOS,PIREDF ;Set "pipe open for read" flag
TXZ IOS,PIRCLF ;Clear "closed after open for read" flag
RETSKP ;Success return
;OPNWRT - open write side of pipe
;Returns +1 failure, T1/ error code
; +2 success
OPNWRT: LOAD T2,PIPWSZ,(PIP) ;Get size of pipe buffer
ADDI T2,1 ;Count storage header
CALL ASGJFR ;Get some JSB free space
RET ;None, take failure return
STOR T1,PIPQED,(PIP) ;Save pointer to queued buffer
LOAD T2,PIPWSZ,(PIP) ;Get size of pipe buffer
ADDI T2,1 ;Count storage header
CALL ASGJFR ;Get some JSB free space
IFNSK.
LOAD T2,PIPQED,(PIP) ;Can't. Get back pointer to queued buffer
MOVEI T1,JSBFRE ;Return it to JSB free pool
CALL RELFRE ; ...
SETZRO PIPQED,(PIP) ;Say we have no queued pipe
RET ;Take failure return
ENDIF.
STOR T1,PIPOUT,(PIP) ;Save pointer to output buffer
CALL SETBYT ;Set up FILBYT byte pointer
LOAD T1,PIPBCT,(PIP) ;Get bytes/buffer
MOVEM T1,FILCNT(JFN) ;Set buffer byte count
SETZM FILBYN(JFN) ;Current byte number is zero
TXO IOS,PIWRTF ;Set "open for write" flag
TXZ IOS,PIWCLF ;Clear "closed after open for write" flag
RETSKP ;Success return
Subttl CLOSF% and RLJFN% Routines
;PIPCLZ - device dependent CLOSF% code
;Returns +1 error, BLKF set if need to block, or T1/ error code
; +2 success, one side of pipe device closed
PIPCLZ: CALL SAVPIP ;Co-routine to preserve PIP and IOS
LOAD PIP,FILPIP,(JFN) ;Get pipe index
JUMPE PIP,RSKP ;Success if no pipe
CALL LCKPIP ;Lock out use of the pipe
UMOVE T1,1 ;Get flags from caller
TXNN T1,CZ%ABT ;Is this an abort?
TQNE <READF> ;Or closing the read side?
JRST PIPCL0 ;Yes to either, go flush this side of the pipe
TXNN IOS,PIREDF ;Is read end already closed?
JRST PIPCL0 ;Yes, don't bother forcing output
CALL DMPPIP ;Force output side of pipe
JRST PIPCL1 ;Must block until write side empties
PIPCL0: TQNN <READF> ;Open for read?
IFSKP.
CALL CLRQDF ;Invalidate any buffered data
LOAD T2,PIPINP,(PIP) ;Get pointer to input buffer
SETZRO PIPINP,(PIP) ;Zero it
TXZ IOS,PIREDF ;Clear "open for read"
TXO IOS,PIRCLF ;Set "closed after open for read"
ELSE.
LOAD T2,PIPOUT,(PIP) ;Writing, get pointer to output buffer
SETZRO PIPOUT,(PIP) ;Zero it
TXZ IOS,PIWRTF ;Clear "open for write"
TXO IOS,PIWCLF ;Set "closed after open for write"
ENDIF.
MOVEI T1,JSBFRE ;Address of free storage header
CALL RELFRE ;Release buffer
TXNE IOS,PIWRTF!PIREDF ;Open at any end?
IFSKP.
LOAD T2,PIPQED,(PIP) ;No, flushed queued buffer
SETZRO PIPQED,(PIP) ; ...
MOVEI T1,JSBFRE ;Belongs to JSB free space
SKIPE T2 ;Do nothing if never assigned that buffer
CALL RELFRE ;Release last buffer
LOAD T1,PIPWTB,(PIP) ;Get back our wait bit
CALL RELBIT ;Release it
SETZRO PIPWTB,(PIP) ;Say wait bit flushed
ENDIF.
CALL ULKPIP ;Release pipe lock
RETSKP ;Success return
;Here when we can't dump the write side of the pipe. We block until the
; PIQEDF flag is reset for this pipe.
PIPCL1: CALL ULKPIP ;Unlock the pipe
SETONE <BLKF> ;We need to block
LOAD T1,PIPWTB,(PIP) ;Get our wait bit
HRLI T1,WTBBZT ;Block until PIQEDF is reset
MOVSS T1 ;Canonicalize scheduler test
RET ;Take failure return
;PIPRJF - device dependent RLJFN% code
;Returns +1 error
; +2 success, pipe assignment released
PIPRJF: CALL SAVPIP ;Preserve our special AC's
LOAD PIP,FILPIP,(JFN) ;Get pipe number
JUMPE PIP,RSKP ;No pipe, do nothing
CALL LCKPIP ;Lock the pipe
LOAD T1,PIPCNT,(PIP) ;Get our share count
SUBI T1,2 ;GTJFN% normally double increments the count
SKIPGE T1 ;Count is good if non-negative
SETZ T1, ;Negative count means GTJFN% failure
STOR T1,PIPCNT,(PIP) ;Update pipe data structure
IFN. T1
CALL ULKPIP ;Still owners. Unlock pipe
RETSKP ;Good return
ENDIF.
LOAD T1,PIPINP,(PIP) ;Get pointer to input buffer
JUMPN T1,PIPRJ0
LOAD T1,PIPOUT,(PIP) ;Get pointer to output buffer
JUMPN T1,PIPRJ0
LOAD T1,PIPQED,(PIP) ;Get pointer to queued buffer
JUMPN T1,PIPRJ0
TXNE IOS,PIREDF!PIWRTF!PIQEDF ;Any read/write bits lit?
JRST PIPRJ0
LOAD T1,PIPWTB,(PIP) ;Get wait bit
JUMPN T1,PIPRJ0
SETZM PIPSTS(PIP) ;Flush pipe assignment (don't call ULKPIP)
RETSKP ;Return to caller
PIPRJ0: BUG.(INF,PIPRLX,PIPE,SOFT,<Pipe not properly closed>,<<PIP,INDEX>>)
CALLRET ULKPIP ;Unlock pipe and take error return
SUBTTL Sequential I/O
COMMENT |
The sequential I/O code uses three buffers to pass data from the write end of
a pipe to the read end. The buffers are allocated at OPENF% time; one is
associated with each end and the third is referred to in the code as the
"queued" buffer. The third buffer is present to avoid complicated, if not
impossible, messing around with the data structures of the two pipe JFN's.
If both ends of the pipe are opened in mode 1 (.GSSMB), no data buffering is
done, i.e., the output buffer takes only one byte. This mode allows for
interactive applications.
When the write side fills a buffer, it checks if the queued buffer is
available, i.e., if the PIQEDF flag is reset, indicating that that buffer
contains no data for the read side. If the queued buffer is not available and
the read side is open, the caller blocks waiting for PIQEDF to be reset.
Otherwise, we swap the output and queued buffers, fix up flags and counts, and
continue to do output. If the queued buffer is unavailable and the read side
is closed after being open, we assume the pipe is broken and generate an
error. If the read side has never been open, we block instead, hoping it will
eventually be opened.
When the read side needs input, it checks if PIQEDF is set, indicating that
the queued buffer contains valid data. If valid data is present, the input
and queued buffers are swapped and various flags and counts are fixed up. If
there is no further data and the write end has been closed normally after
being open, we set the EOF flag. If the write side has never been opened, we
block waiting for it to open.
|
;PIPSQO - pipe sequential output
;Returns +1 always
PIPSQO: TXNN STS,.GSSMB ;Interactive pipe?
IFSKP.
IDPB T1,FILBYT(JFN) ;Yes, deposit byte
AOS FILBYN(JFN) ;Count the byte
CALL PIPREC ;Force pipe now
RET ;Need to block or error, return to caller
RET ;Data transfered, return to caller
ENDIF.
SOSL FILCNT(JFN) ;Buffered pipe, decrement and test byte count
IFSKP.
PUSH P,T1 ;Buffer full, save byte on stack
SETZM FILCNT(JFN) ;Fixup value of FILCNT
CALL PIPREC ;Give buffer to read side (SOUTR%)
IFNSK.
ADJSP P,-1 ;No room or error, trim stack
RET ;Return to caller to block or err
ENDIF.
POP P,T1 ;Get back byte
JRST PIPSQO ;Try again
ENDIF.
IDPB T1,FILBYT(JFN) ;Store byte
AOS FILBYN(JFN) ;Advance byte number
RET ;Return
;PIPREC - force a buffer - SOUTR% support
;Returns +1 failure, T1/ scheduler test or ERRF set
; +2 success, write side of pipe emptied
PIPREC: CALL SAVPIP ;Preserve our favorite AC's
LOAD PIP,FILPIP,(JFN) ;Get pipe index
CALL LCKPIP ;Lock the pipe
CALL DMPPIP ;Force output now
IFNSK.
TXNN IOS,PIRCLF ;No go, is read side closed after being open?
IFSKP.
SETONE <ERRF> ;Yes, say error encountered
ELSE.
SETONE <BLKF> ;We need to block
LOAD T1,PIPWTB,(PIP) ;Get our wait bit
HRLI T1,WTBBZT ;Block until PIQEDF is reset
MOVSS T1 ;Canonicalize scheduler test
ENDIF.
JRST ULKPIP ;Unlock pipe and take single return to caller
ENDIF.
CALL ULKPIP ;Write side empty, unlock the pipe
RETSKP ;Skip return to caller
;DMPPIP - dump the write side of a pipe
;Takes PIP/ pipe index
; IOS/ pipe status bits
;Returns +1 failure, unable to dump write side
; +2 success, write side of pipe is empty
DMPPIP: TXNE IOS,PIINTF ;Buffered mode?
IFSKP.
MOVE T1,FILCNT(JFN) ;Yes, get bytes remaining in buffer
LOAD T2,PIPBCT,(PIP) ;Get max bytes/buffer
SUBI T2,(T1) ;Calculate bytes in this buffer
JUMPE T2,RSKP ;Buffer is empty, return now
ELSE.
MOVEI T2,1 ;Always one byte if interactive
ENDIF.
TXNE IOS,PIQEDF ;Is queued buffer available?
RET ;No, caller must decide to block or err
STOR T2,PIPQCT,(PIP) ;Store count of bytes in queued buffer
LOAD T1,PIPQED,(PIP) ;Get queued pointer
LOAD T2,PIPOUT,(PIP) ;Get output pointer
STOR T2,PIPQED,(PIP) ;Swap buffers
STOR T1,PIPOUT,(PIP) ; ....
CALL SETBYT ;Set up new FILBYT(JFN)
LOAD T1,PIPBCT,(PIP) ;Get max bytes/buffer
MOVEM T1,FILCNT(JFN) ;Reset FILCNT(JFN)
CALL SETQDF ;Set queued buffer valid flag
RETSKP ;Return to caller, output side dumped
;PIPSQI - pipe sequential input
;Returns +1 always
PIPSQI: SOSL FILCNT(JFN) ;Decrement and test byte count
IFSKP.
CALL PIPINB ;Get some more input
RET ;Have to block, return now
JRST PIPSQI ;Try again
ENDIF.
ILDB T1,FILBYT(JFN) ;Bytes remain, load next into T1
AOS FILBYN(JFN) ;Advance byte number
RET ;Return
;PIPINB - work routine for PIPSQI
;Returns +1 no input, return to caller to block or err
; +2 more input available
PIPINB: CALL SAVPIP ;Preserve our AC's
LOAD PIP,FILPIP,(JFN) ;Get pipe index
CALL LCKPIP ;Lock pipe data structure
TXNE IOS,PIQEDF ;Queued input valid?
IFSKP.
TXNN IOS,PIWCLF ;No, was write side once open, now closed?
IFSKP.
SETONE <EOFF> ;Yes, we have reached EOF. Set flag.
ELSE.
SETONE <BLKF> ;We must block for input
LOAD T1,PIPWTB,(PIP) ;Get our wait bit
HRLI T1,WTBBOT ;Block until PIQEDF is set
MOVSS T1 ;Canonicalize scheduler test
ENDIF.
JRST ULKPIP ;Unlock pipe data structures and return
ENDIF.
LOAD T1,PIPQED,(PIP) ;Load pointer to queued input buffer
LOAD T2,PIPINP,(PIP) ;Load pointer to (empty) input buffer
STOR T1,PIPINP,(PIP) ;Swap buffer pointers
STOR T2,PIPQED,(PIP) ; ...
CALL SETBYT ;Set new FILBYT pointer
LOAD T1,PIPQCT,(PIP) ;Get byte count of queued buffer
MOVEM T1,FILCNT(JFN) ;Set it in JFN database
ADDM T1,FILLEN(JFN) ;Update file length
CALL CLRQDF ;Clear queued input valid flag
CALL ULKPIP ;Unlock pipe data structure
RETSKP ;Take success return
SUBTTL JSYS Support Routines
;PIPGTD - GDSTS% support. Returns PIPSTS word for pipe.
PIPGTD: LOAD T1,FILPIP,(JFN) ;Get pipe index
MOVE T1,PIPSTS(T1) ;Get our status bits
RET ;Return to caller
;SETBYT - Set up a byte pointer
;Takes PIP/ pipe index
; JFN/ JFN on one end of pipe
; T1/ address of buffer
;Returns +1 always, FILBYT(JFN) updated
SETBYT: LOAD T2,PIPBSZ,(PIP) ;Get our byte size
ROT T2,-^D12 ;Get S field into place (P field is zero)
HLL T1,T2 ;Create the byte pointer
MOVEM T1,FILBYT(JFN) ;Stash pointer
RET ;Return to calller
;SAVPIP - AC saving co-routine
;Supports +1 and +2 returns
SAVPIP: PUSH P,PIP ;We use PIP for the pipe index
PUSH P,IOS ;We use IOS for pipe status bits
CALL @-2(P) ;Call our co-routine
TRNA ;Single return
AOS -3(P) ;Fix up skip return
POP P,IOS ;Restore preserved AC
POP P,PIP ; ...
ADJSP P,-1 ;Return one deeper in stack
RET ;Return
;LCKPIP - get exclusive use lock on a pipe
;Takes PIP/ pipe index
;Returns +1 always, IOS/ pipe status word
LCKPIP: NOSKED ;Disallow scheduling
MOVE IOS,PIPSTS(PIP) ;Get status bits
TXON IOS,PILCKF ;Locked?
IFSKP.
OKSKED ;Yes, someone else has it. Reallow scheduling.
TRNA ;Skip into the short term block
JRST LCKPIP ;CBLK1 returns here
CBLK1 ;Block for a short time
ENDIF.
IORB IOS,PIPSTS(PIP) ;Now have lock, update in-core copy of status
OKSKED ;Reallow scheduling
RET ;Return to caller
;ULKPIP - release lock on pipe
;Takes PIP/ pipe index
;Returns +1 always, PIPSTS(PIP) updated
ULKPIP: TXZ IOS,PILCKF ;Clear lock bit
MOVEM IOS,PIPSTS(PIP) ;Store updated status bits
RET ;Return to caller
Subttl Wait Bits
COMMENT |
Because we often need to block based on the state of non-resident data
and locks, we use a resident pool of "wait bits" for our scheduler testing. A
process assigns a wait bit and then sets or clears that bit as appropriate to
signal other processes to unblock. The code for the wait bit management was
taken largely from the TOPS-20 TCP code. The code duplication was necessary
because 1.) we can't assume that everyone has TCP in their monitor, and 2.) it
is desirable to minimize the interaction of the TCP and pipe code.
|
;INIBIT - initialize pool of wait bits
;Returns +1 always
INIBIT: MOVSI T1,-NUMWTW ;Number of words in the pool
HRLOI T2,377777 ;First word, omitting index 0
MOVEM T2,PIPBFF(T1) ;Clear free flags
SETO T2, ;Use all bits in succeeding words
AOBJN T1,.-2 ;Loop over entire table
SETOM PIPWTF ;Say that pool has been initialized
RET ;Return to caller
;ASNBIT - assign a (cleared) wait bit index.
;Returns +1 failure, no available wait bits
; +2 success, T1/ wait bit index
ASNBIT: NOSKED ;Only one process at a time
SKIPN PIPWTF ;Tables initialized?
CALL INIBIT ;No, do so now
MOVSI T3,-NUMWTW ;Number of words in bit table
SKIPE T1,PIPBFF(T3) ;Any free bits in this word?
JFFO T1,ASNBT1 ;Yes. Get bit number
AOBJN T3,.-2 ;No. Try next
OKSKED ;No free buffer bits, reallow scheduling
RET ;Return to caller
ASNBT1: MOVE T1,BITS(T2) ;Get the corresponding bit mask
ANDCAM T1,PIPBFF(T3) ;Make it not free
ANDCAM T1,PIPBTB(T3) ;Zero corresponding wait bit
OKSKED ;Reallow scheduling
HRRZ T1,T3 ;Get word offset
IMULI T1,^D36 ;Convert to bits
ADD T1,T2 ;Add bit within last word
RETSKP ;Return to caller
;RELBIT - release a wait bit assignment.
;Takes T1/ wait bit index
;Returns +1 always
RELBIT: JUMPE T1,R ;Ignore attempts to release zero
IDIVI T1,^D36 ;Convert to word and bit
MOVE T2,BITS(T2) ;Get corresponding bit mask
TDNE T2,PIPBFF(T1) ;Better be in use right now.
BUG.(CHK,PIPWA0,PIPE,SOFT,<PIPE - bit table fouled up>)
IORM T2,PIPBFF(T1) ;Free it
RET
;SETBIT - set a wait bit to one state.
;Takes T1/ wait bit index
;Returns +1 always
SETBIT: SETZ T2, ;Make sure T2 is something harmless
JUMPE T1,SETBT0 ;Bug if trying to set bit zero
IDIVI T1,^D36 ;Compute offsets
MOVE T2,BITS(T2) ;Get bit mask
TDNE T2,PIPBFF(T1) ;Check that it is assigned
SETBT0: BUG.(CHK,PIPWA1,PIPE,SOFT,<PIPE - wait bit not assigned>)
IORM T2,PIPBTB(T1) ;Set the bit
RET ;Return to caller
;CLRBIT - clear a wait bit to zero state.
;Takes T1/ wait bit index
;Returns +1 always
CLRBIT: SETZ T2, ;Make sure T2 is harmless
JUMPE T1,CLRBT0 ;Bug if trying to clear bit zero
IDIVI T1,^D36 ;Compute offsets
MOVE T2,BITS(T2) ; ...
TDNE T2,PIPBFF(T1) ;Make sure bit assigned
CLRBT0: BUG.(CHK,PIPWA2,IPIPIP,SOFT,<PIPE - wait bit not assigned>)
ANDCAM T2,PIPBTB(T1) ;Clear the bit
RET ;Return to caller
;WTBBZT - scheduler test for a wait bit clear or unassigned
;Takes T1/ wait bit index
RESCD
WTBBZT: JUMPE T1,1(T4) ;Beware bit 0
IDIVI T1,^D36 ;Convert to word and bit numbers
MOVE T2,BITS(T2) ;Get bit mask
TDNN T2,PIPBFF(T1) ;Bit unassigned?
TDNN T2,PIPBTB(T1) ;Zero yet?
JRST 1(T4) ;Yes, time to unblock
JRST 0(T4) ;No
SWAPCD
;WTBBOT - scheduler test for a wait bit set or unassigned
;Takes T1/ wait bit index
RESCD
WTBBOT: JUMPE T1,1(T4) ;Ignore bit zero
IDIVI T1,^D36 ;Get word and bit numbers
MOVE T2,BITS(T2) ;Get bit mask
TDNN T2,PIPBFF(T1) ;Bit unassigned?
TDNE T2,PIPBTB(T1) ;Is bit set yet?
JRST 1(T4) ;Yes, wakeup
JRST 0(T4) ;No, keep on blocking
SWAPCD
;SETQDF - set queued buffer flag and wait bit
;Takes PIP/ pipe index
; IOS/ pipe status word
;Returns +1 always
SETQDF: TXO IOS,PIQEDF ;Set queued buffer valid flag
LOAD T1,PIPWTB,(PIP) ;Get our wait bit
CALLRET SETBIT ;Set it and return
;CLRQDF - clear queued buffer flag and wait bit
;Takes PIP/ pipe index
; IOS/ pipe status word
;Returns +1 always
CLRQDF: TXZ IOS,PIQEDF ;Clear queued buffer valid flag
LOAD T1,PIPWTB,(PIP) ;Get our wait bit
CALLRET CLRBIT ;Clear it and return
TNXEND
END