CommDriverImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Hal Murray, May 29, 1986 2:26:51 am PDT
Doug Wyatt, June 10, 1986 2:04:15 pm PDT
DIRECTORY
Basics USING [BITAND],
Endian USING [bytesPerFWord, bytesPerHWord, FWORD, HWORD],
CommBufferExtras USING [gapFreeList, gapNoList],
CommDriver USING [Buffer, BufferObject, Network, wordsInIocb],
DebuggerSwap USING [CallDebugger],
IO USING [EndOfStream, STREAM, UnsafeGetBlock, UnsafePutBlock],
IOExtras USING [],
PrincOpsUtils USING [], -- Needed by Process.SetPriority
Process USING [Detach, priorityBackground, SetPriority],
ProcessorFace USING [processorID],
SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ, PinObject],
VM USING [lowCore],
XNS USING [Host];
CommDriverImpl: CEDAR MONITOR
IMPORTS Basics, DebuggerSwap, IO, Process, ProcessorFace, SafeStorage, VM
EXPORTS CommDriver, IOExtras, XNS = {
Buffer: TYPE = CommDriver.Buffer;
Network: TYPE = CommDriver.Network;
IOCB: TYPE = ARRAY [0..CommDriver.wordsInIocb) OF WORD;
Next: PROC [b: Buffer] RETURNS [Buffer] = TRUSTED INLINE {
RETURN[LOOPHOLE[b.ovh.next]]; };
Buffer allocation
buffers: Buffer ← NIL;
totalBuffersCreated: INT ← 0;
totalBuffersAlloced: INT ← 0;
freeBuffers: INT ← 0;
AllocBuffer: PUBLIC PROC RETURNS [buffer: Buffer] = {
buffer ← AllocBufferInner[];
IF buffer = NIL THEN {
This is where buffers actually get created. Beware: D0s and DLions have buffer alignment constraints. If anybody wants to run this code on D0s, the microcode should be fixed. DLions (luckily) only require that the first 3 words of the buffer be on the same page. For now, a BufferObject is big enough so that the Allocator gives each one a separate clump of pages. If that ever changes, don't be surprised by obscure troubles.
buffer ← NEW[CommDriver.BufferObject];
SafeStorage.PinObject[buffer];
TRUSTED {
iocb: LONG POINTER TO IOCB ← LOOPHOLE[VM.lowCore.NEW[IOCB]];
buffer.ovh.iocb ← iocb;
iocb^ ← ALL[0]; };
CountNewBuffer[buffer];
SafeStorage.EnableFinalization[buffer];
};
buffer.ovh.next ← NIL;
buffer.ovh.gap ← CommBufferExtras.gapNoList; -- DKW: not on any list
};
AllocBufferInner: PUBLIC ENTRY PROC RETURNS [buffer: Buffer] = {
IF buffers = NIL THEN RETURN[NIL];
buffer ← buffers;
buffers ← Next[buffers];
totalBuffersAlloced ← totalBuffersAlloced.SUCC;
freeBuffers ← freeBuffers.PRED;
};
CountNewBuffer: PUBLIC ENTRY PROC [buffer: Buffer] = {
totalBuffersCreated ← totalBuffersCreated.SUCC;
totalBuffersAlloced ← totalBuffersAlloced.SUCC;
};
FreeBuffer: PUBLIC ENTRY PROC [b: Buffer] = {
TRUSTED { b ← NARROW[LOOPHOLE[b, REF ANY]]; }; -- check that runtime type is correct
IF b.ovh.socket # NIL THEN ERROR;
IF b.ovh.gap#CommBufferExtras.gapNoList THEN
DebuggerSwap.CallDebugger["Buffer freed while in a list!"];
b.ovh.next ← buffers;
buffers ← b;
b.ovh.gap ← CommBufferExtras.gapFreeList; -- DKW: buffer now on the free list
freeBuffers ← freeBuffers.SUCC;
};
Discard unwanted packets
This lives here since this module is already reasonably hot.
NoThankYou: PUBLIC PROC [network: Network, b: Buffer, bytes: NAT] RETURNS [Buffer] = {
RETURN[b]; };
NSImpl - Host Number Manipulations - this lives here to save a GFI
GetThisHost: PUBLIC PROC RETURNS [XNS.Host] ~ {
BEWARE. Is this byte-order dependent? Not if ProcessorFace.ProcessorID guaranteed to be stored in the same format as an XNS.Host on the wire, but who knows ...
TRUSTED { RETURN[LOOPHOLE[ProcessorFace.processorID]]; };
};
IsMulticastHost: PUBLIC PROC [host: XNS.Host] RETURNS [BOOL] ~ {
RETURN [Basics.BITAND[host.a, 1] # 0 ];
};
IOExtrasImpl - Extensions to IO for Endian - this lives here to save a GFI
GetHWord: PUBLIC PROC [self: IO.STREAM] RETURNS [hword: Endian.HWORD] = TRUSTED {
bytes: INT;
bytes ← IO.UnsafeGetBlock[self, [LOOPHOLE[LONG[@hword]], 0, Endian.bytesPerHWord]];
IF bytes # Endian.bytesPerHWord THEN ERROR IO.EndOfStream[self];
};
PutHWord: PUBLIC PROC [self: IO.STREAM, hword: Endian.HWORD] = TRUSTED {
IO.UnsafePutBlock[self, [LOOPHOLE[LONG[@hword]], 0, Endian.bytesPerHWord]];
};
GetFWord: PUBLIC PROC [self: IO.STREAM] RETURNS [fword: Endian.FWORD] = TRUSTED {
bytes: INT;
bytes ← IO.UnsafeGetBlock[self, [LOOPHOLE[LONG[@fword]], 0, Endian.bytesPerFWord]];
IF bytes # Endian.bytesPerHWord THEN IO.EndOfStream[self];
};
PutFWord: PUBLIC PROC [self: IO.STREAM, fword: Endian.FWORD] = TRUSTED {
IO.UnsafePutBlock[self, [LOOPHOLE[LONG[@fword]], 0, Endian.bytesPerFWord]];
};
Finalization
droppedBuffers: INT ← 0;
BufferFinalizer: PROC = {
Process.SetPriority[Process.priorityBackground];
DO
b: Buffer ← NARROW[SafeStorage.FQNext[bfq]];
SafeStorage.EnableFinalization[b];
FreeBuffer[b];
b ← NIL;
droppedBuffers ← droppedBuffers.SUCC;
ENDLOOP;
};
DropTest: PROC [n: NAT ← 100] = {
FOR i: NAT IN [0..n) DO
[] ← AllocBuffer[];
ENDLOOP;
};
bfq: SafeStorage.FinalizationQueue ← SafeStorage.NewFQ[];
SafeStorage.EstablishFinalization[type: CODE[CommDriver.BufferObject], npr: 0, fq: bfq];
TRUSTED { Process.Detach[FORK BufferFinalizer[]]; };
}.