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 ← MAX[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: CARDINAL ← LOOPHOLE[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.