XNSSocketImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Hal Murray, January 21, 1986 2:41:44 pm PST
Demers, December 19, 1986 6:28:41 pm PST
DIRECTORY
Allocator USING [NHeaderP, NormalHeader],
Basics USING [BITAND, bytesPerWord],
Checksum USING [ComputeChecksum],
CommBuffer USING [Direction, Encapsulation],
CommDriver USING [AllocBuffer, Buffer, BufferObject, FreeBuffer, GetNetworkChain, Network],
Endian USING [bytesPerFWord, bytesPerHWord, CardFromH, HFromCard, HWORD],
PrincOpsUtils USING [LongMove],
Process USING [Detach, DisableTimeout, EnableAborts, MsecToTicks, Pause, priorityBackground, SecondsToTicks, SetPriority, SetTimeout, Ticks],
SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ],
XNS USING [Address, broadcastHost, broadcastNet, GetThisHost, Host, IsMulticastHost, Net, Socket, unknownAddress, unknownNet, unknownSocket],
XNSBuf USING [Buffer, BufferObject, hdrBytes, maxBodyBytes, maxBodyFWords, maxBodyHWords, noChecksum],
XNSErrorBuf USING [Buffer, hdrBytes, minBodyBytes],
XNSErrorTypes USING [badChecksumErr, ErrorType, noSocketErr, nullParam, resourceLimitsErr, unspecifiedInRouteErr],
XNSRouterPrivate USING [Route],
XNSSocket USING [dontWait, Milliseconds, waitForever],
XNSSocketBackdoor USING [ReceiveProc],
XNSWKS USING [last];
XNSSocketImpl: CEDAR MONITOR
LOCKS handle USING handle: Handle
IMPORTS Basics, Checksum, CommDriver, Endian, XNS, XNSRouterPrivate, PrincOpsUtils, Process, SafeStorage
EXPORTS XNSRouterPrivate, XNSSocket, XNSSocketBackdoor
~ {
Address: TYPE ~ XNS.Address;
Net: TYPE ~ XNS.Net;
Host: TYPE ~ XNS.Host;
Socket: TYPE ~ XNS.Socket;
Buffer: TYPE ~ XNSBuf.Buffer;
Network: TYPE ~ CommDriver.Network;
Milliseconds: TYPE ~ XNSSocket.Milliseconds;
ReceiveProc: TYPE ~ XNSSocketBackdoor.ReceiveProc;
Direction: TYPE ~ CommBuffer.Direction;
HWORD: TYPE ~ Endian.HWORD;
bytesPerHWord: NAT ~ Endian.bytesPerHWord;
bytesPerFWord: NAT ~ Endian.bytesPerFWord;
bytesPerWord: NAT ~ Basics.bytesPerWord;
thisHost: Host ~ XNS.GetThisHost[];
Stuff for smashing types of buffers between CommDriver.Buffer and XNSBuf.Buffer. See the comments near CommDriver.Buffer. BEWARE: this stuff may be word size dependent.
RealDriverBuffer: PROC [b: Buffer] RETURNS [CommDriver.Buffer] = TRUSTED INLINE {
nhp: Allocator.NHeaderP ← LOOPHOLE[b, Allocator.NHeaderP]-SIZE[Allocator.NormalHeader];
nhp.type ← CODE[CommDriver.BufferObject];
b.ovh.direction ← none;
b.ovh.socket ← NIL;
RETURN[LOOPHOLE[b]]; };
TempDriverBuffer: PROC [b: Buffer] RETURNS [CommDriver.Buffer] = TRUSTED INLINE {
RETURN[LOOPHOLE[b]]; };
RealXNSBuffer: PROC [b: CommDriver.Buffer, d: Direction] RETURNS [Buffer] = TRUSTED INLINE {
nhp: Allocator.NHeaderP ← LOOPHOLE[b, Allocator.NHeaderP]-SIZE[Allocator.NormalHeader];
nhp.type ← CODE[XNSBuf.BufferObject];
b.ovh.next ← NIL;
b.ovh.direction ← d;
RETURN[LOOPHOLE[b]]; };
Next: PROC [b: Buffer] RETURNS [Buffer] = TRUSTED INLINE {
RETURN[LOOPHOLE[b.ovh.next]]; };
Socket Objects
Handle: TYPE ~ REF Object;
Object: PUBLIC TYPE ~ MONITORED RECORD [
local, remote: Address,
sendBuffersInUse: CARDINAL ← 0,
sendBuffersAllocated: CARDINAL,
recvBuffersInUse: CARDINAL ← 0,
recvBuffersAllocated: CARDINAL,
routingCache: Routing ← NIL,
receiveProc: ReceiveProc ←,
receiveClientData: REF ANYNIL,
sendChecksum, recvChecksum: BOOLTRUE,
dead, noErrors: BOOLFALSE,
dontWait: BOOLFALSE,
waitForInput, waitForSendBuffer: CONDITION,
firstInput, lastInput: Buffer ← NIL,
next: Handle ← NIL ];
Routing: TYPE ~ REF RoutingInfo;
RoutingInfo: TYPE ~ RECORD [
network: Network,
immediate: Host, -- Not Necessary ????
encap: CommBuffer.Encapsulation ];
Hash Table for Socket Objects
numHashHeaders: CARDINAL ~ 64;
HashIndex: TYPE ~ [0 .. numHashHeaders);
ObjectTable: TYPE ~ REF ObjectTableRep;
ObjectTableRep: TYPE ~ ARRAY HashIndex OF Handle;
objectTable: ObjectTable ~ NEW[ObjectTableRep];
objectTableLock: Handle ~ NEW[Object ← [local~, remote~, sendBuffersAllocated~, recvBuffersAllocated~, receiveProc~NIL]];
Hash: PROC [socket: Socket] RETURNS [HashIndex] ~ INLINE {
RETURN [socket.b MOD numHashHeaders] };
AddNewHandle: ENTRY PROC [handle: Handle ← objectTableLock, newHandle: Handle] ~ {
Insert newHandle in hash table. Called before finalization of newHandle is enabled.
i: HashIndex ~ Hash[newHandle.local.socket];
newHandle.next ← objectTable^[i];
objectTable^[i] ← newHandle;
};
RemoveOldHandle: ENTRY PROC [handle: Handle ← objectTableLock, oldHandle: Handle] ~ {
Delete oldHandle from hash table. Called during finalization of oldHandle.
i: HashIndex ~ Hash[oldHandle.local.socket];
IF oldHandle = objectTable^[i]
THEN {
objectTable^[i] ← oldHandle.next }
ELSE {
prev: Handle;
FOR prev ← objectTable^[i], prev.next WHILE prev.next # oldHandle DO NULL ENDLOOP;
prev.next ← oldHandle.next };
oldHandle.next ← NIL; -- Help finalization of oldHandle.next^
};
FindHandle: PROC [localSocket: Socket] RETURNS [Handle] ~ {
i: HashIndex ~ Hash[localSocket];
FOR handle: Handle ← objectTable^[i], handle.next UNTIL handle = NIL DO -- ATOMIC
IF handle.local.socket = localSocket THEN RETURN[handle];
ENDLOOP;
RETURN[NIL] };
Unique Local Socket Numbers
uniqueSocketLock: Handle ~ NEW[Object ← [local~[net~, host~, socket~XNSWKS.last], remote~, sendBuffersAllocated~, recvBuffersAllocated~, receiveProc~NIL]];
NextLocalSocket: ENTRY PROC [handle: Handle ← uniqueSocketLock] RETURNS [Socket] ~ TRUSTED {
temp: HWORDLOOPHOLE[uniqueSocketLock.local.socket];
temp ← Endian.HFromCard[Endian.CardFromH[temp] + 1];
RETURN [uniqueSocketLock.local.socket ← LOOPHOLE[temp]] };
Checksum Utilities
GetBytesRounded: PROC [b: Buffer] RETURNS [CARDINAL] ~ INLINE {
Get packet bytes, rounded up to include the garbage byte if this is an odd length packet.
RETURN[Basics.BITAND[(1+Endian.CardFromH[b.hdr1.length]),0FFFEH]]; };
InlineSetChecksum: PROC [b: Buffer, bytes: CARDINAL, computed: BOOLTRUE] ~ TRUSTED INLINE {
Caller must already have rounded bytes up, e.g. by using GetBytesRounded[b]
BEWARE: word-size dependent, LONG POINTERs to HWORDs.
I believe the checksum calculation is NOT byte-order dependent, though!
IF computed
THEN {
words: CARDINAL ← (bytes - bytesPerHWord) / bytesPerWord;
checksumLoc: LONG POINTER ← @b.hdr1.checksum;
checksumLoc^ ← Checksum.ComputeChecksum[0, words, @b.hdr1.length] }
ELSE {
b.hdr1.checksum ← XNSBuf.noChecksum };
};
InlineTestChecksum: PROC [b: Buffer, bytes: CARDINAL] RETURNS [BOOL] ~ TRUSTED INLINE {
Caller must already have rounded bytes up, e.g. by using GetBytesRounded[b]
Return TRUE if checksum is correct or noChecksum.
BEWARE: word-size dependent, LONG POINTERs to HWORDs.
I believe the checksum calculation is NOT byte-order dependent, though!
IF b.hdr1.checksum # XNSBuf.noChecksum
THEN {
words: CARDINAL ~ (bytes - bytesPerHWord) / bytesPerWord;
myChecksum: HWORD ~ Checksum.ComputeChecksum[0, words, @b.hdr1.length];
RETURN [ b.hdr1.checksum = myChecksum ] }
ELSE RETURN[TRUE];
};
SetChecksum: PUBLIC PROC [b: Buffer] ~ {
InlineSetChecksum[b, GetBytesRounded[b]] };
Creation and parameter setting
Create: PUBLIC PROC [
remote: Address ← XNS.unknownAddress,
sendBuffers: NAT ← 1,
recvBuffers: NAT ← 5,
getTimeout: Milliseconds ← 10000,
local: Socket ← XNS.unknownSocket ]
RETURNS [handle: Handle] ~ {
network: Network;
network ← XNSRouterPrivate.Route[remote].network;
IF network = NIL THEN network ← CommDriver.GetNetworkChain[];
IF local = XNS.unknownSocket THEN local ← NextLocalSocket[];
handle ← NEW [Object ← [
local~[net~network.xns.net, host~thisHost, socket~local],
remote~remote,
sendBuffersAllocated~sendBuffers,
recvBuffersAllocated~recvBuffers,
receiveProc~NormalReceive ] ];
TRUSTED {
Process.EnableAborts[@handle.waitForInput];
Process.EnableAborts[@handle.waitForSendBuffer] };
SetGetTimeout[handle, getTimeout];
AddNewHandle[newHandle~handle];
SafeStorage.EnableFinalization[handle];
};
GetLocalAddress: PUBLIC PROC [handle: Handle] RETURNS [Address] ~ {
RETURN[handle.local];
};
GetRemoteAddress: PUBLIC PROC [handle: Handle] RETURNS [Address] ~ {
RETURN[handle.remote];
};
SetRemoteAddress: PUBLIC PROC [handle: Handle, address: Address] ~ {
handle.remote ← address;
handle.routingCache ← NIL; -- FlushCache[handle]
};
SetGetTimeout: PUBLIC PROC [handle: Handle, ms: Milliseconds] ~ {
handle.dontWait ← FALSE;
SELECT ms FROM
XNSSocket.dontWait => handle.dontWait ← TRUE;
XNSSocket.waitForever => TRUSTED {
Process.DisableTimeout[@handle.waitForInput]; };
< CARDINAL.LAST => TRUSTED {
Process.SetTimeout[@handle.waitForInput, Process.MsecToTicks[ms] ]; };
ENDCASE => TRUSTED {
Process.SetTimeout[@handle.waitForInput, Process.SecondsToTicks[ms/1000] ]; };
};
SetSoftwareChecksumming: PUBLIC PROC [handle: Handle, send, recv: BOOL] ~ {
handle.sendChecksum ← send;
handle.recvChecksum ← recv;
};
SetNoErrors: PUBLIC PROC [handle: Handle, noErrors: BOOLTRUE] ~ {
handle.noErrors ← noErrors;
};
Kick: PUBLIC ENTRY PROC [handle: Handle] ~ {
NOTIFY handle.waitForInput;
};
Destroy: PUBLIC ENTRY PROC [handle: Handle] ~ {
handle.dead ← TRUE;
UNTIL handle.firstInput = NIL DO
b: Buffer ← handle.firstInput;
handle.firstInput ← Next[b];
handle.recvBuffersInUse ← handle.recvBuffersInUse - 1;
CommDriver.FreeBuffer[RealDriverBuffer[b]];
ENDLOOP;
handle.lastInput ← NIL; -- Help Buffer finalization.
IF handle.noErrors
OR (handle.recvBuffersInUse # 0) OR (handle.sendBuffersInUse # 0)
THEN TRUSTED { Process.Detach[FORK DestroyDally[handle]]; RETURN };
Drop handle, let finalization remove it from table.
};
DestroyDally: PROC [handle: Handle] ~ {
oneSecond: Process.Ticks = Process.SecondsToTicks[1];
Process.SetPriority[Process.priorityBackground];
IF handle.noErrors THEN Process.Pause[10*oneSecond];
WHILE (handle.recvBuffersInUse # 0) OR (handle.sendBuffersInUse # 0) DO
Process.Pause[oneSecond];
ENDLOOP;
Drop handle, let finalization remove it from table.
};
Sending
AllocBuffer: PUBLIC PROC [handle: Handle] RETURNS [b: Buffer] ~ {
InnerAllocBuffer[handle];
b ← RealXNSBuffer[CommDriver.AllocBuffer[], send];
b.ovh.next ← NIL;
b.ovh.socket ← handle;
};
InnerAllocBuffer: ENTRY PROC [handle: Handle] ~ {
ENABLE UNWIND => NULL;
UNTIL handle.sendBuffersInUse < handle.sendBuffersAllocated DO
WAIT handle.waitForSendBuffer;
ENDLOOP;
handle.sendBuffersInUse ← handle.sendBuffersInUse + 1;
};
SetUserBytes: PUBLIC PROC [b: Buffer, bytes: [0 .. XNSBuf.maxBodyBytes]] ~ {
b.hdr1.length ← Endian.HFromCard[bytes + XNSBuf.hdrBytes];
};
SetUserHWords: PUBLIC PROC [b: Buffer, hWords: [0 .. XNSBuf.maxBodyHWords]] ~ {
b.hdr1.length ← Endian.HFromCard[hWords*bytesPerHWord + XNSBuf.hdrBytes];
};
SetUserFWords: PUBLIC PROC [b: Buffer, fWords: [0 .. XNSBuf.maxBodyFWords]] ~ {
b.hdr1.length ← Endian.HFromCard[fWords*bytesPerFWord + XNSBuf.hdrBytes];
};
SetUserSize: PUBLIC PROC [b: Buffer, sizeUnits: NAT] ~ {
b.hdr1.length ← Endian.HFromCard[sizeUnits*bytesPerWord + XNSBuf.hdrBytes]; -- Fix when BYTES[...] is implemented
};
Broadcast: PUBLIC PROC [b: Buffer, socket: Socket] ~ {
handle: Handle ~ NARROW[b.ovh.socket];
bytes: CARDINAL ~ GetBytesRounded[b];
b.hdr1.transportCtl ← [trace~FALSE, filler~0, hopCount~0];
b.hdr1.dest.socket ← socket;
b.hdr1.dest.host ← XNS.broadcastHost;
b.hdr1.source ← handle.local;
FOR network: Network ← CommDriver.GetNetworkChain[], network.next UNTIL network = NIL DO
b.ovh.encap ← network.xns.getEncapsulation[network, XNS.broadcastHost];
b.hdr1.dest.net ← network.xns.net;
b.hdr1.source.net ← network.xns.net;
InlineSetChecksum[b, bytes, handle.sendChecksum];
network.xns.send[network, TempDriverBuffer[b], bytes];
ENDLOOP;
FreeBuffer[b];
};
Put: PUBLIC PROC [b: Buffer] ~ {
handle: Handle ~ NARROW[b.ovh.socket];
Send[b, handle.remote]; -- What if it's UNKNOWN???
};
Send: PUBLIC PROC [b: Buffer, dest: Address] ~ {
handle: Handle ~ NARROW[b.ovh.socket];
bytes: CARDINAL = GetBytesRounded[b];
network: Network;
immediate: Host;
IF (dest.net = XNS.broadcastNet) AND (dest.host = XNS.broadcastHost)
THEN { Broadcast[b, dest.socket]; RETURN; };
b.hdr1.transportCtl ← [trace~FALSE, filler~0, hopCount~0];
b.hdr1.dest ← dest;
b.hdr1.source ← handle.local;
[network, immediate] ← XNSRouterPrivate.Route[dest];
IF network # NIL THEN {
b.ovh.encap ← network.xns.getEncapsulation[network, immediate];
InlineSetChecksum[b, bytes, handle.sendChecksum];
network.xns.send[network, TempDriverBuffer[b], bytes];
};
FreeBuffer[b];
};
Backdoor Sending
PutCached: PUBLIC PROC [b: Buffer] ~ {
handle: Handle ~ NARROW[b.ovh.socket];
bytes: CARDINAL = GetBytesRounded[b];
network: Network;
immediate: Host;
routingCache: Routing ← handle.routingCache;
IF routingCache = NIL THEN {
[network, immediate] ← XNSRouterPrivate.Route[handle.remote];
IF network = NIL THEN RETURN;
handle.routingCache ← routingCache ←
NEW [RoutingInfo ← [network~network, immediate~immediate, encap~network.xns.getEncapsulation[network, immediate]]];
};
b.hdr1.transportCtl ← [trace~FALSE, filler~0, hopCount~0];
b.hdr1.dest ← handle.remote;
b.hdr1.source ← handle.local;
b.ovh.encap ← routingCache.encap;
InlineSetChecksum[b, bytes, handle.sendChecksum];
routingCache.network.xns.send[routingCache.network, TempDriverBuffer[b], bytes];
};
FlushCache: PUBLIC PROC [handle: Handle] ~ {
handle.routingCache ← NIL;
};
Receiving
Get: PUBLIC ENTRY PROC [handle: Handle] RETURNS [b: Buffer] ~ {
ENABLE UNWIND => NULL;
IF (handle.firstInput = NIL) AND NOT handle.dontWait
THEN WAIT handle.waitForInput;
IF (b ← handle.firstInput) # NIL THEN {
handle.firstInput ← Next[b];
b.ovh.socket ← handle; -- needed for finalization
};
};
GetUserBytes: PUBLIC PROC [b: Buffer] RETURNS [bytes: [0 .. XNSBuf.maxBodyBytes]] ~ {
bytes ← Endian.CardFromH[b.hdr1.length] - XNSBuf.hdrBytes;
};
GetUserHWords: PUBLIC PROC [b: Buffer] RETURNS [hWords: [0 .. XNSBuf.maxBodyHWords]] ~ {
bytes: [0 .. XNSBuf.maxBodyBytes] ← Endian.CardFromH[b.hdr1.length] - XNSBuf.hdrBytes;
hWords ← bytes/bytesPerHWord;
};
GetUserFWords: PUBLIC PROC [b: Buffer] RETURNS [fWords: [0 .. XNSBuf.maxBodyFWords]] ~ {
bytes: [0 .. XNSBuf.maxBodyBytes] ← Endian.CardFromH[b.hdr1.length] - XNSBuf.hdrBytes;
fWords 𡤋ytes/bytesPerFWord;
};
GetUserSize: PUBLIC PROC [b: Buffer] RETURNS [sizeUnits: NAT] ~ {
bytes: [0 .. XNSBuf.maxBodyBytes] ← Endian.CardFromH[b.hdr1.length] - XNSBuf.hdrBytes;
sizeUnits ← bytes/bytesPerWord; -- Fix when BYTES[...] is implemented.
};
AccountForReturnedBuffer: ENTRY PROC [handle: Handle, b: Buffer] ~ {
SELECT b.ovh.direction FROM
send => {
handle.sendBuffersInUse ← handle.sendBuffersInUse - 1;
NOTIFY handle.waitForSendBuffer };
recv => handle.recvBuffersInUse ← handle.recvBuffersInUse - 1;
ENDCASE => ERROR;
};
FreeBuffer: PUBLIC PROC [b: Buffer] ~ {
h: Handle ~ NARROW[b.ovh.socket];
IF h # NIL THEN AccountForReturnedBuffer[h, b];
CommDriver.FreeBuffer[RealDriverBuffer[b]];
};
FixupSourceAndDest: PUBLIC PROC [b: Buffer] ~ {
network: Network ← NARROW[b.ovh.network];
IF b.hdr1.source.net = XNS.unknownNet THEN b.hdr1.source.net ← network.xns.net;
IF b.hdr1.dest.net = XNS.unknownNet THEN b.hdr1.dest.net ← network.xns.net;
IF b.hdr1.dest.host = XNS.broadcastHost THEN b.hdr1.dest.host ← thisHost;
};
ReturnToSender: PUBLIC PROC [b: Buffer] ~ {
Return buffer to sender, unless it was multicast or sender is (invalidly) multicast.
handle: Handle ~ NARROW[b.ovh.socket];
bytes: CARDINAL ~ GetBytesRounded[b];
sendChecksum: BOOL ~ IF handle = NIL THEN TRUE ELSE handle.sendChecksum;
network: Network ~ NARROW[b.ovh.network];
FixupSourceAndDest[b];
{ temp: Address ~ b.hdr1.source; b.hdr1.source ← b.hdr1.dest; b.hdr1.dest ← temp };
IF XNS.IsMulticastHost[b.hdr1.dest.host] THEN { FreeBuffer[b]; RETURN };
b.hdr1.transportCtl ← [trace~FALSE, filler~0, hopCount~0];
InlineSetChecksum[b, bytes, sendChecksum];
network.xns.return[network, TempDriverBuffer[b], bytes];
FreeBuffer[b];
};
ReturnError: PUBLIC PROC [b: Buffer, type: XNSErrorTypes.ErrorType] ~ {
bodyBytes: CARDINAL ~ XNSErrorBuf.minBodyBytes;
eb: XNSErrorBuf.Buffer;
IF b.hdr1.type = error OR XNS.IsMulticastHost[b.hdr1.dest.host]
THEN { FreeBuffer[b]; RETURN };
TRUSTED { -- ???? WORD-SIZE DEPENDENT ????
eb ← LOOPHOLE[b];
PrincOpsUtils.LongMove[
from: @b.hdr1,
nwords: (bodyBytes+1)/2,
to: @eb.body];
};
b.hdr1.type ← error;
eb.hdr2.type ← type;
eb.hdr2.param ← XNSErrorTypes.nullParam;
SetUserBytes[b, XNSErrorBuf.hdrBytes + bodyBytes];
ReturnToSender[b];
};
Backdoor Receive
SetDirectReceive: PUBLIC ENTRY PROC
[handle: Handle, receiveProc: ReceiveProc, clientData: REF ANY] ~ {
handle.receiveProc ← (IF receiveProc # NIL THEN receiveProc ELSE NormalReceive);
handle.receiveClientData ← clientData;
};
See also NormalReceive, below.
Incoming packets from Drivers
Error Statistics.
errorTooShort: INT ← 0;
errorNoGateway: INT ← 0;
errorNoSocket: INT ← 0;
errorDeadSocket: INT ← 0;
errorBadChecksum: INT ← 0;
errorBuffersFull: INT ← 0;
TakeThis: PUBLIC PROC [network: Network, buffer: CommDriver.Buffer, bytes: NAT] RETURNS [CommDriver.Buffer] ~ {
b: Buffer ← RealXNSBuffer[buffer, recv];
bytesNeeded: CARDINAL ~ GetBytesRounded[b];
dest: Address ← b.hdr1.dest;
localSocket: Socket ~ dest.socket;
handle: Handle;
receiveProc: ReceiveProc;
receiveClientData: REF ANY;
BEGIN
IF (bytes < bytesNeeded) OR (Endian.CardFromH[b.hdr1.length] < XNSBuf.hdrBytes) THEN {
errorTooShort ← errorTooShort.SUCC;
GOTO Out; };
IF b.hdr1.dest.host # thisHost AND b.hdr1.dest.host # XNS.broadcastHost THEN {
errorNoGateway ← errorNoGateway.SUCC;
ReturnError[b, XNSErrorTypes.unspecifiedInRouteErr]; b ← NIL; -- What's the RIGHT error to use here?
GOTO Out; };
handle ← FindHandle[localSocket];
IF handle = NIL THEN {
errorNoSocket ← errorNoSocket.SUCC;
ReturnError[b, XNSErrorTypes.noSocketErr]; b ← NIL;
GOTO Out; };
IF handle.dead THEN {
errorDeadSocket ← errorDeadSocket.SUCC;
IF NOT handle.noErrors THEN
{ ReturnError[b, XNSErrorTypes.noSocketErr]; b ← NIL };
GOTO Out; };
IF handle.recvChecksum AND NOT InlineTestChecksum[b, bytesNeeded] THEN {
errorBadChecksum ← errorBadChecksum.SUCC;
ReturnError[b, XNSErrorTypes.badChecksumErr]; b ← NIL;
GOTO Out; };
b.ovh.next ← NIL; -- This can't hurt, but what is it doing here ????
[receiveProc, receiveClientData] ← GetReceiveProc[handle, b];
IF receiveProc # NIL THEN {
b ← receiveProc[handle, b, receiveClientData];
IF (b # NIL) AND (b.ovh.socket # NIL) THEN
AccountForReturnedBuffer[NARROW[b.ovh.socket], b];
GOTO Out; };
errorBuffersFull ← errorBuffersFull.SUCC;
IF NOT handle.noErrors THEN
{ ReturnError[b, XNSErrorTypes.resourceLimitsErr]; b ← NIL };
GOTO Out;
EXITS
Out => NULL;
END;
RETURN[ IF b = NIL THEN NIL ELSE RealDriverBuffer[b] ];
};
GetReceiveProc: ENTRY PROC [handle: Handle, b: Buffer]
RETURNS [ReceiveProc, REF ANY] ~ {
IF handle.recvBuffersInUse >= handle.recvBuffersAllocated THEN RETURN [NIL, NIL];
handle.recvBuffersInUse ← handle.recvBuffersInUse.SUCC;
b.ovh.socket ← handle;
RETURN [handle.receiveProc, handle.receiveClientData];
};
NormalReceive: PUBLIC ENTRY ReceiveProc ~ {
IF handle.firstInput = NIL
THEN handle.firstInput ← b
ELSE handle.lastInput.ovh.next ← b;
handle.lastInput ← b;
NOTIFY handle.waitForInput;
RETURN[NIL];
};
Finalization
Statistics
droppedSockets: INT ← 0;
finishedSockets: INT ← 0;
droppedBuffers: INT ← 0;
ofq: SafeStorage.FinalizationQueue ~ SafeStorage.NewFQ[]; -- for Objects
bfq: SafeStorage.FinalizationQueue ~ SafeStorage.NewFQ[]; -- for Buffers
ObjectFinalizer: PROC = {
Process.SetPriority[Process.priorityBackground];
DO
handle: Handle ← NARROW[SafeStorage.FQNext[ofq]];
IF NOT handle.dead
THEN { -- User forgot to call Destroy
SafeStorage.EnableFinalization[handle];
Destroy[handle];
droppedSockets ← droppedSockets.SUCC; }
ELSE { -- Normal end of life
RemoveOldHandle[oldHandle~handle];
finishedSockets ← finishedSockets.SUCC; };
handle ← NIL;
ENDLOOP;
};
BufferFinalizer: PROC = {
Beware: See the comments near CommDriver.Buffer
Process.SetPriority[Process.priorityBackground];
DO
b: Buffer ← NARROW[SafeStorage.FQNext[bfq]];
handle: Handle ← NARROW[b.ovh.socket];
SafeStorage.EnableFinalization[b];
FreeBuffer[b];
b ← NIL;
droppedBuffers ← droppedBuffers.SUCC;
ENDLOOP;
};
SafeStorage.EstablishFinalization[type: CODE[Object], npr: 1, fq: ofq];
SafeStorage.EstablishFinalization[type: CODE[XNSBuf.BufferObject], npr: 0, fq: bfq];
TRUSTED { Process.Detach[FORK ObjectFinalizer[]] };
TRUSTED { Process.Detach[FORK BufferFinalizer[]] };
}.