-- 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.