-- File: BufferMgr.mesa -- Edit by: BLyon on: March 19, 1981 12:37 PM -- Edit by: HGM on: March 17, 1981 6:32 PM -- Edit by: Garlick on: January 26, 1981 1:52 PM -- Last Edited by: Levin, August 9, 1983 9:27 am DIRECTORY Basics USING [LongNumber], BufferDefs USING [ defaultDataWordsPerSystemBuffer, BufferAccessHandle, BufferAccessObject, BufferFunction, BufferType, Buffer, PupBuffer, OisBuffer, SppBuffer, BufferObject, Queue, QueueObject, wordsPerNonVarientBufferOverhead], CommFlags USING [doDebug, doStats], CommUtilDefs USING [ AllocateBuffers, FreeBuffers, AllocateIocb, FreeIocb, GetReturnFrame, MaybeShorten], DriverDefs USING [Glitch, GetGiantVector, Network], DriverTypes USING [bufferSeal, bufferPoolSeal, queueSeal], PrincOpsUtils USING [BITAND, LowHalf], OISCP USING [], OISCPTypes USING [BufferBody, wordPerPktHeader, wordsPerLevel2SppHeader], Process USING [InitializeCondition, MsecToTicks], PupDefs USING [], StatsDefs USING [StatBump, StatIncr], ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses]; BufferMgr: MONITOR IMPORTS CommUtilDefs, DriverDefs, PrincOpsUtils, Process, StatsDefs, ProcessorFace EXPORTS BufferDefs, DriverDefs, OISCP, PupDefs SHARES BufferDefs = BEGIN OPEN BufferDefs, DriverDefs; -- EXPORTed TYPEs Network: PUBLIC TYPE = DriverDefs.Network; -- monitor protected data accessHandleChainHead: BufferDefs.BufferAccessHandle ← NIL; systemAccessHandle: PUBLIC BufferDefs.BufferAccessHandle ← NIL; systemFreeQueueNotEmpty: CONDITION; systemBufferQueue: Queue; systemDataWordsPerBuffer: CARDINAL ← defaultDataWordsPerSystemBuffer; systemBufferSize: CARDINAL ← 0; -- the raw buffer data length that drivers need to know (includes encapsulation words) useCount: CARDINAL ← 0; wordsPerIocb: CARDINAL ← 0; totalSendAndReceiveBuffers: CARDINAL ← 0; totalSendAndReceiveBuffersInUse: CARDINAL ← 0; totalReserveBuffers: CARDINAL ← 0; inactivePools: CARDINAL ← 0; enqueue: PROCEDURE [Queue, Buffer] ← Enqueue; -- Buffers and IOCB locations must be rounded up for alignment constraints. -- Alto SLA Microcode needs IOCB/LCB to be EVEN. -- D0 Ethernet/Xwire Microcode needs IOCB to be Quad word aligned, -- and first data word to be (almost?) QUAD word aligned. -- size of a buffer without any data rawOverhead: CARDINAL = BufferDefs.wordsPerNonVarientBufferOverhead; oisOverhead: CARDINAL = rawOverhead + OISCPTypes.wordPerPktHeader; sppOisOverhead: CARDINAL = oisOverhead + OISCPTypes.wordsPerLevel2SppHeader; -- 1 extra for pup checksum pupOverhead: CARDINAL = SIZE[pupWords pup BufferDefs.BufferObject] + 1; overhead: CARDINAL = MAX[ pupOverhead, oisOverhead, sppOisOverhead]; -- for the Glitches QueueSealBroken: PUBLIC ERROR = CODE; PoolSealBroken: PUBLIC ERROR = CODE; BufferSealBroken: PUBLIC ERROR = CODE; FreeQueueNotInitialized: PUBLIC ERROR = CODE; BufferPoolNotInitialized: PUBLIC ERROR = CODE; QueueScrambled: PUBLIC ERROR = CODE; CantResetWhileActive: PUBLIC ERROR = CODE; SystemBufferSizeConfused: PUBLIC ERROR = CODE; DontKnowHowToAllocateBuffer: PUBLIC ERROR = CODE; -- Cold Procedures AdjustBufferSize: PUBLIC ENTRY PROCEDURE [bufferSize: CARDINAL] = BEGIN IF useCount # 0 THEN Glitch[CantResetWhileActive]; IF bufferSize # 0 THEN systemDataWordsPerBuffer ← bufferSize; systemBufferSize ← 0; END; GetBufferSize: PUBLIC PROCEDURE RETURNS [bufferSize: CARDINAL] = BEGIN RETURN[systemDataWordsPerBuffer]; END; GetWordsPerIocb: PUBLIC PROCEDURE RETURNS [CARDINAL] = BEGIN RETURN[wordsPerIocb]; END; SetWordsPerIocb: PUBLIC PROCEDURE [new: CARDINAL] = BEGIN wordsPerIocb ← new; END; -- NB: wordsPerBuffer and wordsPerIocb MUST be QuadWord multiples MakeBufferPool: PUBLIC ENTRY PROCEDURE [ total: CARDINAL, -- number of new buffer that will actually be created send: CARDINAL, -- number of send type buffer that can be allocated from this pool (could be greater than total) receive: CARDINAL, -- number of receive type buffer that can be allocated from this pool (could be greater than total) reserve: CARDINAL, -- number of send type buffer that can be allocated from this pool (could be greater than total) forSystemUse: BOOLEAN] -- as opposed to for socket use RETURNS [p: BufferAccessHandle] = BEGIN b: Buffer; x: --SHORT--POINTER TO BufferObject = NIL; i: CARDINAL; newSystemBufferSize: CARDINAL; IF NOT forSystemUse THEN BEGIN totalSendAndReceiveBuffers ← totalSendAndReceiveBuffers + send + receive; END; totalReserveBuffers ← totalReserveBuffers + reserve; useCount ← useCount + 1; p ← NEW[BufferAccessObject]; p.seal ← DriverTypes.bufferPoolSeal; p.active ← TRUE; p.madeForSystem ← forSystemUse; p.filler ← 0; p.total ← total; p.sendInUse ← p.receiveInUse ← p.recovered ← 0; p.send ← send; p.receive ← receive; p.reserve ← reserve; -- insert this accessObject into the linked list of known accessObjects p.next ← accessHandleChainHead; accessHandleChainHead ← p; IF CommFlags.doDebug THEN DriverDefs.GetGiantVector[].firstBufferAccessHandle ← accessHandleChainHead; Process.InitializeCondition[ CommUtilDefs.MaybeShorten[@p.bufferAvailable], Process.MsecToTicks[10000]]; IF p.total = 0 THEN BEGIN p.firstBuffer ← LOOPHOLE[p]; -- kludgy, but lets us do sanity check for nil values RETURN; END; -- 2 for checksum, 4 for end test, 3 for round down p.wordsPerBuffer ← systemDataWordsPerBuffer + overhead + 2 + 4 + 3; UNTIL PrincOpsUtils.BITAND[p.wordsPerBuffer, 3] = 0 DO p.wordsPerBuffer ← p.wordsPerBuffer + 1; ENDLOOP; -- systemBufferSize is a little bigger because of the quad word allignment newSystemBufferSize ← p.wordsPerBuffer - (@x.encapsulation - LOOPHOLE[x, POINTER]); IF systemBufferSize = 0 THEN systemBufferSize ← newSystemBufferSize ELSE IF newSystemBufferSize # systemBufferSize THEN Glitch[SystemBufferSizeConfused]; p.firstBuffer ← CommUtilDefs.AllocateBuffers[p.wordsPerBuffer*total + 3]; -- This determines the buffer alignment: see DriverTypes.Encapsulation UNTIL PrincOpsUtils.BITAND[PrincOpsUtils.LowHalf[@p.firstBuffer.encapsulation], 3] = 3 DO p.firstBuffer ← p.firstBuffer + 1; ENDLOOP; b ← p.firstBuffer; FOR i IN [0..total) DO b.iocbChain ← NIL; b.allNets ← b.bypassZeroNet ← FALSE; b.type ← raw; b.pupLength ← b.length ← 0; b.pupType ← last; b.queue ← NIL; b.pool ← p; b.next ← NIL; IF CommFlags.doDebug THEN b.seal ← DriverTypes.bufferSeal; b.requeueProcedure ← ReturnFreeBuffer; Enqueue[systemBufferQueue, b]; b ← b + p.wordsPerBuffer; ENDLOOP; IF wordsPerIocb # 0 THEN BEGIN b ← p.firstBuffer; FOR i IN [0..total) DO -- this does NOT align each individual iocb b.iocbChain ← CommUtilDefs.AllocateIocb[wordsPerIocb]; b ← b + p.wordsPerBuffer; ENDLOOP; END; END; DestroyBufferPoolLocked: PROCEDURE [p: BufferAccessHandle] = BEGIN IF CommFlags.doDebug THEN BEGIN b: Buffer ← p.firstBuffer; FOR i: CARDINAL IN [0..p.total) DO IF b.queue # NIL THEN Glitch[QueueScrambled]; b ← b + p.wordsPerBuffer; ENDLOOP; END; IF p.total # 0 THEN BEGIN b: Buffer ← p.firstBuffer; FOR i: CARDINAL IN [0..p.total) DO IF b.iocbChain # NIL THEN CommUtilDefs.FreeIocb[b.iocbChain]; b ← b + p.wordsPerBuffer; ENDLOOP; CommUtilDefs.FreeBuffers[p.firstBuffer, p.wordsPerBuffer*p.total + 3]; END; IF NOT p.madeForSystem THEN totalSendAndReceiveBuffers ← totalSendAndReceiveBuffers - (p.send + p.receive - p.total); p.firstBuffer ← NIL; -- remove this accessObject from the linked list of known accessObjects IF accessHandleChainHead=p THEN BEGIN accessHandleChainHead ← p.next; IF CommFlags.doDebug THEN DriverDefs.GetGiantVector[].firstBufferAccessHandle ← accessHandleChainHead; END ELSE BEGIN prev: BufferDefs.BufferAccessHandle ← accessHandleChainHead; WHILE (prev#NIL) AND (prev.next#p) DO prev ← prev.next; ENDLOOP; IF prev=NIL THEN ERROR; prev.next ← p.next; END; p.next ← NIL; -- Heap.FreeNode[p: p]; before conversion to REFs. (ADB). useCount ← useCount - 1; IF (inactivePools ← inactivePools - 1) = 0 AND enqueue = EnqueueActiveBuffersOnly THEN BEGIN IF CommFlags.doStats THEN StatsDefs.StatBump[statRecyclingZombieBuffersTime, LOOPHOLE[((ProcessorFace.GetClockPulses[]-startReCyclingTime+50)/100)*ProcessorFace.microsecondsPerHundredPulses, Basics.LongNumber].lowbits]; enqueue ← Enqueue; END; END; startReCyclingTime: LONG CARDINAL --Pulses--; FreeBufferPool: PUBLIC ENTRY PROCEDURE [p: BufferAccessHandle] = BEGIN b: Buffer ← p.firstBuffer; IF CommFlags.doDebug AND (useCount = 0 OR p = NIL) THEN Glitch[FreeQueueNotInitialized]; IF CommFlags.doDebug AND p.seal # DriverTypes.bufferPoolSeal THEN Glitch[PoolSealBroken]; IF CommFlags.doDebug AND p.firstBuffer = NIL THEN Glitch[BufferPoolNotInitialized]; -- since sends are asynchronous, don't free the pool until they are completed IF NOT p.madeForSystem THEN BEGIN WHILE p.sendInUse>0 DO WAIT systemFreeQueueNotEmpty; -- any short CV will do ENDLOOP; END; p.active ← FALSE; inactivePools ← inactivePools + 1; totalReserveBuffers ← totalReserveBuffers - p.reserve; p.recovered ← 0; FOR i: CARDINAL IN [0..p.total) DO IF b.queue = systemBufferQueue THEN BEGIN p.recovered ← p.recovered + 1; IF NOT p.madeForSystem THEN totalSendAndReceiveBuffers ← totalSendAndReceiveBuffers - 1; IF ExtractFromQueue[systemBufferQueue, b] # b THEN Glitch[QueueScrambled]; END; b ← b + p.wordsPerBuffer; ENDLOOP; IF p.recovered = p.total THEN DestroyBufferPoolLocked[p] -- all buffers recovered, so destroy pool. ELSE BEGIN IF CommFlags.doStats AND enqueue # EnqueueActiveBuffersOnly THEN startReCyclingTime ← ProcessorFace.GetClockPulses[]; enqueue ← EnqueueActiveBuffersOnly; -- continue trying to recover buffers END; END; EnqueueActiveBuffersOnly: PROCEDURE [q: Queue, b: Buffer] = BEGIN aH: BufferAccessHandle = b.pool; IF aH.active THEN Enqueue[q, b] ELSE BEGIN IF b = NIL OR b.queue # NIL THEN Glitch[QueueScrambled]; IF CommFlags.doDebug AND q.seal # DriverTypes.queueSeal THEN Glitch[QueueSealBroken]; IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN Glitch[BufferSealBroken]; b.next ← NIL; aH.recovered ← aH.recovered + 1; IF NOT aH.madeForSystem THEN totalSendAndReceiveBuffers ← totalSendAndReceiveBuffers - 1; IF aH.recovered = aH.total THEN DestroyBufferPoolLocked[aH]; END; END; -- Cool Procedures -- This is not an ENTRY procedure because we assume that systemBufferPool can -- have its parameters set only once. DataWordsPerPupBuffer: PUBLIC SAFE PROCEDURE RETURNS [CARDINAL] = CHECKED BEGIN RETURN[systemDataWordsPerBuffer + (overhead - pupOverhead)]; END; -- This is not an ENTRY procedure because we assume that systemBufferPool can -- have its parameters set only once. DataWordsPerRawBuffer: PUBLIC PROCEDURE RETURNS [CARDINAL] = BEGIN RETURN[systemDataWordsPerBuffer + (overhead - rawOverhead)]; END; -- This is not an ENTRY procedure because we assume that systemBufferPool can -- have its parameters set only once. DataWordsPerOisBuffer: PUBLIC PROCEDURE RETURNS [CARDINAL] = BEGIN RETURN[systemDataWordsPerBuffer + (overhead - oisOverhead)]; END; -- This is not an ENTRY procedure because we assume that systemBufferPool can -- have its parameters set only once. DataWordsPerSppBuffer: PUBLIC PROCEDURE RETURNS [CARDINAL] = BEGIN RETURN[systemDataWordsPerBuffer + (overhead - sppOisOverhead)]; END; -- Hot Procedures SendAndReceiveBuffersInFreeQueue: PROCEDURE RETURNS [CARDINAL] = INLINE BEGIN RETURN[ IF totalSendAndReceiveBuffersInUse >= totalSendAndReceiveBuffers THEN 0 ELSE totalSendAndReceiveBuffers - totalSendAndReceiveBuffersInUse]; END; -- This routine is only used by device drivers to get buffers to read things into. -- NB: b.length is setup for the size of the buffer, including ALL of the encapsulation. -- All device drivers except for the XWire must fudge things themsleves, since they do -- not use all of the encapsulation field. GetInputBuffer: PUBLIC ENTRY PROCEDURE [tryToWaitForBuffer: BOOLEAN] RETURNS [b: Buffer ← NIL] = BEGIN IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; FOR i: CARDINAL IN [0..2) DO -- since the drivers only use the buffers for a short time, no access control -- is used in their allocation; a filled buffer will be subject to usual access control -- when it is delivered; therefore if we have a buffer the drivers can have it. IF systemBufferQueue.length > 0 THEN BEGIN b ← Dequeue[systemBufferQueue]; b.currentOwner ← systemAccessHandle; b.bufFunc ← systemUse; b.length ← systemBufferSize; b.type ← raw; IF CommFlags.doDebug THEN b.debug ← CommUtilDefs.GetReturnFrame[].accesslink; RETURN; END; IF (~tryToWaitForBuffer) OR (i > 0) THEN RETURN; WAIT systemFreeQueueNotEmpty; ENDLOOP; END; FillInBufferTypeInformation: PROCEDURE [ b: Buffer, bufType: BufferType, bufFunc: BufferFunction] = INLINE BEGIN b.bufFunc ← bufFunc; SELECT bufType FROM oisSpp => BEGIN sppBuf: LONG POINTER TO spp OISCPTypes.BufferBody = LOOPHOLE[b+rawOverhead]; sppBuf.systemPacket ← sppBuf.sendAck ← sppBuf.attention ← sppBuf.endOfMessage ← FALSE; bufType ← ois; END; pup => b.pupType ← data; ENDCASE => NULL; b.type ← bufType; END; GetFreeBuffer: PUBLIC ENTRY PROCEDURE [ bufType: BufferType, aH: BufferAccessHandle, bufFunc: BufferFunction] RETURNS [b: Buffer] = BEGIN ENABLE UNWIND => NULL; IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; IF CommFlags.doDebug AND aH.seal # DriverTypes.bufferPoolSeal THEN Glitch[PoolSealBroken]; IF CommFlags.doDebug AND aH.firstBuffer = NIL THEN Glitch[BufferPoolNotInitialized]; IF aH = systemAccessHandle THEN BEGIN IF CommFlags.doDebug AND bufFunc # systemUse THEN Glitch[DontKnowHowToAllocateBuffer]; IF CommFlags.doStats AND ~systemBufferQueue.length > totalReserveBuffers + SendAndReceiveBuffersInFreeQueue[] THEN StatsDefs.StatIncr[statBufferWaits]; UNTIL systemBufferQueue.length > totalReserveBuffers + SendAndReceiveBuffersInFreeQueue[] DO WAIT systemFreeQueueNotEmpty; ENDLOOP; END ELSE BEGIN sendOrReceiveInUse: LONG POINTER TO CARDINAL; sendOrReceive: CARDINAL; SELECT bufFunc FROM send => BEGIN sendOrReceiveInUse ← @aH.sendInUse; sendOrReceive ← aH.send; END; receive => BEGIN sendOrReceiveInUse ← @aH.receiveInUse; sendOrReceive ← aH.receive; END; ENDCASE => Glitch[DontKnowHowToAllocateBuffer]; IF CommFlags.doStats AND (~sendOrReceiveInUse↑ < sendOrReceive OR systemBufferQueue.length = 0) THEN StatsDefs.StatIncr[statBufferWaits]; UNTIL (sendOrReceiveInUse↑ < sendOrReceive) DO WAIT aH.bufferAvailable; ENDLOOP; sendOrReceiveInUse↑ ← sendOrReceiveInUse↑ + 1; totalSendAndReceiveBuffersInUse ← totalSendAndReceiveBuffersInUse + 1; UNTIL (systemBufferQueue.length > 0) DO WAIT systemFreeQueueNotEmpty; ENDLOOP; END; b ← Dequeue[systemBufferQueue]; IF CommFlags.doDebug AND b = NIL THEN Glitch[QueueScrambled]; FillInBufferTypeInformation[b, bufType, bufFunc]; b.currentOwner ← aH; IF CommFlags.doDebug THEN b.debug ← CommUtilDefs.GetReturnFrame[].accesslink; END; -- Get free buffer, but don't wait if there is none -- NB: These will return the last buffer, use with caution MaybeGetFreeBuffer: PUBLIC ENTRY PROCEDURE [ bufType: BufferType, aH: BufferAccessHandle, bufFunc: BufferFunction] RETURNS [b: Buffer] = BEGIN IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; IF CommFlags.doDebug AND aH.seal # DriverTypes.bufferPoolSeal THEN Glitch[PoolSealBroken]; IF CommFlags.doDebug AND aH.firstBuffer = NIL THEN Glitch[BufferPoolNotInitialized]; IF aH = systemAccessHandle THEN BEGIN IF ~systemBufferQueue.length > SendAndReceiveBuffersInFreeQueue[] THEN BEGIN b ← NIL; IF CommFlags.doStats THEN StatsDefs.StatIncr[statBufferWaits]; RETURN; END; IF CommFlags.doDebug AND bufFunc # systemUse THEN Glitch[DontKnowHowToAllocateBuffer]; END ELSE BEGIN sendOrReceiveInUse: LONG POINTER TO CARDINAL; sendOrReceive: CARDINAL; SELECT bufFunc FROM send => BEGIN sendOrReceiveInUse ← @aH.sendInUse; sendOrReceive ← aH.send; END; receive => BEGIN sendOrReceiveInUse ← @aH.receiveInUse; sendOrReceive ← aH.receive; END; ENDCASE => Glitch[DontKnowHowToAllocateBuffer]; IF (~sendOrReceiveInUse↑ < sendOrReceive) OR (systemBufferQueue.length = 0) THEN BEGIN b ← NIL; IF CommFlags.doStats THEN StatsDefs.StatIncr[statBufferWaits]; RETURN; END; sendOrReceiveInUse↑ ← sendOrReceiveInUse↑ + 1; totalSendAndReceiveBuffersInUse ← totalSendAndReceiveBuffersInUse + 1; END; IF (b ← Dequeue[systemBufferQueue]) = NIL THEN Glitch[QueueScrambled]; FillInBufferTypeInformation[b, bufType, bufFunc]; b.currentOwner ← aH; IF CommFlags.doDebug THEN b.debug ← CommUtilDefs.GetReturnFrame[].accesslink; END; -- credits may be used to avoid copying buffers CreditReceiveOisBuffer: PUBLIC ENTRY PROCEDURE [ aH: BufferAccessHandle, b: OisBuffer] RETURNS [gotCreadit: BOOLEAN] = BEGIN IF CommFlags.doDebug AND (aH = NIL OR aH.firstBuffer = NIL) THEN Glitch[BufferPoolNotInitialized]; IF CommFlags.doDebug AND aH.seal # DriverTypes.bufferPoolSeal THEN Glitch[PoolSealBroken]; IF (gotCreadit ← aH.receiveInUse < aH.receive) THEN BEGIN aH.receiveInUse ← aH.receiveInUse + 1; totalSendAndReceiveBuffersInUse ← totalSendAndReceiveBuffersInUse + 1; b.currentOwner ← aH; b.bufFunc ← receive; END ELSE IF CommFlags.doStats THEN StatsDefs.StatIncr[statBufferWaits]; END; NILNetwork: PROCEDURE RETURNS [Network] = INLINE {RETURN[NIL]}; ReturnFreePupBuffer: PUBLIC PROCEDURE [PupBuffer] = LOOPHOLE[ReturnFreeBuffer]; ReturnFreeOisBuffer: PUBLIC PROCEDURE [OisBuffer] = LOOPHOLE[ReturnFreeBuffer]; ReturnFreeSppBuffer: PUBLIC PROCEDURE [SppBuffer] = LOOPHOLE[ReturnFreeBuffer]; ReturnFreeBuffer: PUBLIC ENTRY PROCEDURE [b: Buffer] = BEGIN aH: BufferAccessHandle = b.currentOwner; IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; IF CommFlags.doDebug AND aH.firstBuffer = NIL THEN Glitch[BufferPoolNotInitialized]; -- Note: we do some "initialization" of things here since there are several ways to get -- buffers from the freeQueue. b.requeueProcedure ← ReturnFreeBuffer; b.network ← NILNetwork[]; -- because of mokelumne compiler bug IF aH # systemAccessHandle THEN BEGIN SELECT b.bufFunc FROM send => aH.sendInUse ← aH.sendInUse - 1; receive => aH.receiveInUse ← aH.receiveInUse - 1; ENDCASE => Glitch[DontKnowHowToAllocateBuffer]; totalSendAndReceiveBuffersInUse ← totalSendAndReceiveBuffersInUse - 1; BROADCAST aH.bufferAvailable; END; enqueue[systemBufferQueue, b]; -- This is ugly, but there isn't any way to make things work without it, if 2 -- PROCESSes are waiting for buffers. BROADCAST systemFreeQueueNotEmpty; END; BuffersLeft: PUBLIC ENTRY PROCEDURE [aH: BufferAccessHandle] RETURNS [left: CARDINAL] = BEGIN IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; IF CommFlags.doDebug AND aH.seal # DriverTypes.bufferPoolSeal THEN Glitch[PoolSealBroken]; IF CommFlags.doDebug AND aH.firstBuffer = NIL THEN Glitch[BufferPoolNotInitialized]; IF aH = systemAccessHandle THEN BEGIN reserved: CARDINAL = SendAndReceiveBuffersInFreeQueue[] + totalReserveBuffers; IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; left ← systemBufferQueue.length; left ← (IF left > reserved THEN left - reserved ELSE 0); END ELSE BEGIN left ← aH.send + aH.receive - aH.sendInUse - aH.receiveInUse; END; END; SendBuffersLeft: PUBLIC ENTRY PROCEDURE [aH: BufferAccessHandle] RETURNS [CARDINAL] = BEGIN IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; IF CommFlags.doDebug AND aH.seal # DriverTypes.bufferPoolSeal THEN Glitch[PoolSealBroken]; IF CommFlags.doDebug AND aH.firstBuffer = NIL THEN Glitch[BufferPoolNotInitialized]; RETURN[aH.send - aH.sendInUse]; END; -- SendBufferLeftInPool ReceiveBuffersLeft: PUBLIC ENTRY PROCEDURE [aH: BufferAccessHandle] RETURNS [CARDINAL] = BEGIN IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; IF CommFlags.doDebug AND aH.seal # DriverTypes.bufferPoolSeal THEN Glitch[PoolSealBroken]; IF CommFlags.doDebug AND aH.firstBuffer = NIL THEN Glitch[BufferPoolNotInitialized]; RETURN[aH.receive - aH.receiveInUse]; END; -- ReceiveBufferLeftInPool EnumerateBuffersInPool: PUBLIC PROCEDURE [ pool: BufferAccessHandle, proc: PROCEDURE [Buffer]] = BEGIN b: BufferDefs.Buffer; IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; b ← pool.firstBuffer; IF CommFlags.doDebug AND pool.seal # DriverTypes.bufferPoolSeal THEN Glitch[PoolSealBroken]; FOR i: CARDINAL IN [0..pool.total) DO proc[b]; b ← b + pool.wordsPerBuffer; ENDLOOP; END; -- EnumerateBuffersInPool -- Get free buffer, but don't wait if there is none -- NB: These will return the last buffer, use with caution GetFreePupBuffer: PUBLIC PROCEDURE RETURNS [PupBuffer] = BEGIN RETURN[ LOOPHOLE[GetFreeBuffer[pup, systemAccessHandle, systemUse], PupBuffer]]; END; GetClumpOfPupBuffers: PUBLIC ENTRY PROCEDURE [ q: Queue, n: CARDINAL, wait: BOOLEAN] = BEGIN ENABLE UNWIND => NULL; IF CommFlags.doDebug AND useCount = 0 THEN Glitch[FreeQueueNotInitialized]; IF CommFlags.doDebug AND q.seal # DriverTypes.queueSeal THEN Glitch[QueueSealBroken]; UNTIL systemBufferQueue.length > n + SendAndReceiveBuffersInFreeQueue[] + totalReserveBuffers DO IF ~wait THEN RETURN; WAIT systemFreeQueueNotEmpty; ENDLOOP; THROUGH [0..n) DO b: PupBuffer ← DequeuePup[systemBufferQueue]; b.type ← pup; b.pupType ← data; IF CommFlags.doDebug THEN b.debug ← CommUtilDefs.GetReturnFrame[].accesslink; EnqueuePup[q, b]; ENDLOOP; END; -- Queue Manipulation Routines QueueInitialize: PUBLIC PROCEDURE [q: Queue] = BEGIN q↑ ← [length: 0, first: NIL, last: NIL, seal: DriverTypes.queueSeal]; END; -- Put all buffers back onto buffer pool's freeQueue QueueCleanup: PUBLIC PROCEDURE [q: Queue] = BEGIN b: Buffer; UNTIL (b ← Dequeue[q]) = NIL DO ReturnFreeBuffer[b]; ENDLOOP; END; ExtractPupFromQueue: PUBLIC PROCEDURE [Queue, PupBuffer] RETURNS [PupBuffer] = LOOPHOLE[ExtractFromQueue]; ExtractOisFromQueue: PUBLIC PROCEDURE [Queue, OisBuffer] RETURNS [OisBuffer] = LOOPHOLE[ExtractFromQueue]; ExtractSppFromQueue: PUBLIC PROCEDURE [Queue, SppBuffer] RETURNS [SppBuffer] = LOOPHOLE[ExtractFromQueue]; ExtractFromQueue: PUBLIC PROCEDURE [q: Queue, b: Buffer] RETURNS [Buffer] = BEGIN previousB, currentB: Buffer; IF q = NIL THEN Glitch[QueueScrambled]; IF CommFlags.doDebug AND q.seal # DriverTypes.queueSeal THEN Glitch[QueueSealBroken]; IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN Glitch[BufferSealBroken]; previousB ← NIL; currentB ← q.first; UNTIL currentB = b DO IF currentB = NIL THEN EXIT; previousB ← currentB; currentB ← currentB.next; ENDLOOP; IF currentB # NIL THEN BEGIN -- remove this buffer from the queue IF CommFlags.doDebug AND currentB.seal # DriverTypes.bufferSeal THEN Glitch[BufferSealBroken]; IF currentB = q.first THEN q.first ← currentB.next; IF currentB = q.last THEN q.last ← previousB; IF previousB # NIL THEN previousB.next ← currentB.next; q.length ← q.length - 1; currentB.queue ← NIL; currentB.next ← NIL; IF CommFlags.doStats THEN StatsDefs.StatIncr[statXqueue]; END ELSE IF CommFlags.doStats THEN StatsDefs.StatIncr[statXqueueNIL]; RETURN[currentB]; END; EnqueuePup: PUBLIC PROCEDURE [Queue, PupBuffer] = LOOPHOLE[Enqueue]; EnqueueOis: PUBLIC PROCEDURE [Queue, OisBuffer] = LOOPHOLE[Enqueue]; EnqueueSpp: PUBLIC PROCEDURE [Queue, SppBuffer] = LOOPHOLE[Enqueue]; Enqueue: PUBLIC PROCEDURE [q: Queue, b: Buffer] = BEGIN IF q = NIL OR b = NIL OR b.queue # NIL THEN Glitch[QueueScrambled]; IF CommFlags.doDebug AND q.seal # DriverTypes.queueSeal THEN Glitch[QueueSealBroken]; IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN Glitch[BufferSealBroken]; IF CommFlags.doDebug AND q.length # 0 AND (q.first = NIL OR q.last = NIL) THEN Glitch[QueueScrambled]; IF CommFlags.doDebug AND q.length > 256 THEN Glitch[QueueScrambled]; b.next ← NIL; IF CommFlags.doStats THEN StatsDefs.StatIncr[statEnqueue]; IF q.first = NIL THEN q.first ← b ELSE q.last↑.next ← b; q.last ← b; b.queue ← q; q.length ← q.length + 1; END; -- Enqueue DequeuePup: PUBLIC PROCEDURE [Queue] RETURNS [PupBuffer] = LOOPHOLE[Dequeue]; DequeueOis: PUBLIC PROCEDURE [Queue] RETURNS [OisBuffer] = LOOPHOLE[Dequeue]; DequeueSpp: PUBLIC PROCEDURE [Queue] RETURNS [SppBuffer] = LOOPHOLE[Dequeue]; Dequeue: PUBLIC PROCEDURE [q: Queue] RETURNS [b: Buffer] = BEGIN IF q = NIL THEN Glitch[QueueScrambled]; IF CommFlags.doDebug AND q.seal # DriverTypes.queueSeal THEN Glitch[QueueSealBroken]; IF (b ← q.first) = NIL THEN BEGIN IF CommFlags.doDebug AND q.length # 0 THEN Glitch[QueueScrambled]; IF CommFlags.doStats THEN StatsDefs.StatIncr[statDequeueNIL]; RETURN; END; IF (q.first ← q.first.next) = NIL THEN q.last ← NIL; IF CommFlags.doDebug AND q.length > 256 THEN Glitch[QueueScrambled]; q.length ← q.length - 1; IF CommFlags.doStats THEN StatsDefs.StatIncr[statDequeue]; IF b.queue # q THEN Glitch[QueueScrambled]; b.queue ← NIL; b.next ← NIL; IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN Glitch[BufferSealBroken]; IF CommFlags.doDebug AND q.length # 0 AND (q.first = NIL OR q.last = NIL) THEN Glitch[QueueScrambled]; END; -- Dequeue -- initialization --systemAccessHandle ← MakeBufferPool[total: 0, send: 0, receive: 0, reserve: 0, forSystemUse: TRUE]; systemBufferQueue ← NEW[QueueObject]; QueueInitialize[systemBufferQueue]; Process.InitializeCondition[ CommUtilDefs.MaybeShorten[@systemFreeQueueNotEmpty], Process.MsecToTicks[ 1000]]; IF CommFlags.doDebug THEN BEGIN GetGiantVector[].firstBuffer ← NIL; GetGiantVector[].freeQueue ← systemBufferQueue; END; END.