-- 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 DIRECTORY BufferDefs USING [ defaultDataWordsPerSystemBuffer, BufferAccessHandle, BufferAccessObject, BufferFunction, BufferType, Buffer, PupBuffer, OisBuffer, SppBuffer, BufferObject, Queue, QueueObject, wordsPerNonVarientBufferOverhead], CommFlags USING [doDebug, doStats], CommUtilDefs USING [ AllocateBuffers, FreeBuffers, LockBuffers, UnlockBuffers, AllocateIocbs, FreeIocbs, GetReturnFrame, MaybeShorten], DriverDefs USING [Glitch, GetGiantVector, Network], DriverTypes USING [bufferSeal, bufferPoolSeal, queueSeal], Heap USING [FreeNode, MakeNode], Inline USING [BITAND, LowHalf], OISCP USING [], OISCPTypes USING [BufferBody, wordPerPktHeader, wordsPerLevel2SppHeader], Process USING [InitializeCondition, MsecToTicks], PupDefs USING [], StatsDefs USING [StatBump, StatIncr], System USING [GetClockPulses, Pulses, PulsesToMicroseconds]; BufferMgr: MONITOR IMPORTS CommUtilDefs, DriverDefs, Heap, Inline, Process, StatsDefs, System 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 _ Heap.MakeNode[n: SIZE[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 Inline.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]; CommUtilDefs.LockBuffers[p.firstBuffer]; -- This determines the buffer alignment: see DriverTypes.Encapsulation UNTIL Inline.BITAND[Inline.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 iocb: LONG POINTER; iocb _ CommUtilDefs.AllocateIocbs[wordsPerIocb*total + 3]; b _ p.firstBuffer; FOR i IN [0..total) DO -- this does NOT align each individual iocb b.iocbChain _ iocb; iocb _ iocb + 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 CommUtilDefs.UnlockBuffers[p.firstBuffer]; IF p.firstBuffer.iocbChain # NIL THEN CommUtilDefs.FreeIocbs[p.firstBuffer.iocbChain]; CommUtilDefs.FreeBuffers[p.firstBuffer]; 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]; useCount _ useCount - 1; IF (inactivePools _ inactivePools - 1) = 0 AND enqueue = EnqueueActiveBuffersOnly THEN BEGIN IF CommFlags.doStats THEN StatsDefs.StatBump[ statRecyclingZombieBuffersTime, Inline.LowHalf[ System.PulsesToMicroseconds[[System.GetClockPulses[]-startReCyclingTime]]/1000]]; enqueue _ Enqueue; END; END; startReCyclingTime: System.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 _ System.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 PROCEDURE RETURNS [CARDINAL] = 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 _ Heap.MakeNode[n: SIZE[QueueObject]]; QueueInitialize[systemBufferQueue]; Process.InitializeCondition[ CommUtilDefs.MaybeShorten[@systemFreeQueueNotEmpty], Process.MsecToTicks[ 1000]]; IF CommFlags.doDebug THEN BEGIN GetGiantVector[].firstBuffer _ NIL; GetGiantVector[].freeQueue _ systemBufferQueue; END; END.