BufferMgr.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
BLyon on: March 19, 1981 12:37 PM
HGM on: March 17, 1981 6:32 PM
Garlick on: January 26, 1981 1:52 PM
Levin, August 9, 1983 9:27 am
Taft, February 4, 1984 2:37:41 pm PST
Russ Atkinson (RRA) February 2, 1985 3:49:15 pm PST
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: CARDINALMAX[defaultDataWordsPerSystemBuffer, 271]; -- The 271 ensures that the raw buffers are big enough to hold a maximum-size ARPA IP packet (576 bytes plus 8 bytes of encapsulation). The constant was determined empirically; its computation is to disgusting to describe cleanly.
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
DO
c: CARDINALLOOPHOLE[PrincOpsUtils.LowHalf[@p.firstBuffer.encapsulation]];
IF PrincOpsUtils.BITAND[c, 3] = 3 THEN EXIT;
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.