LupineRuntimeImpl.mesa - used at runtime for marshaling and other common functions
Copyright © 1985 by Xerox Corporation. All rights reserved.
BZM on 12-May-82 19:17:55.
Birrell, September 12, 1983 2:07 pm
Swinehart, February 8, 1985 8:37:39 am PST
Bob Hagmann May 13, 1985 12:57:04 pm PDT
Russ Atkinson (RRA) February 28, 1985 2:05:04 am PST
Polle Zellweger (PTZ) August 2, 1985 2:30:18 pm PDT
DIRECTORY
Atom USING [MakeAtom, GetPName],
Basics USING [DivMod],
LupineRuntime USING [CheckPktLength, CopyFromPkt, CopyToPkt, NilHeader, RopeHeader, Words, WordsForChars],
PrincOpsUtils USING [LongCopy],
Process USING [Pause],
Rope USING [ InlineFlatten, NewText, ROPE, Text ],
RPCLupine USING [DataLength, maxDataLength, MaxPacketBufferListSize, maxShortStringLength, PacketBufferCacheListRec, MaxPacketBufferIntervalInRec, ReceiveExtraPkt, RejectUnbound, RPCPkt, SendPrelimPkt ],
RPC USING [CallFailed, Zones],
SafeStorage USING [GetSystemZone],
UnsafeStorage USING [GetSystemUZone],
VM USING [AddressForPageNumber, Allocate, CantAllocate, Free, Interval, MakeUnchanged, nullInterval, PageCount, PageNumber, PageNumberForAddress, PagesForWords, SwapIn],
VMInternal USING [partitions];
LupineRuntimeImpl: MONITOR LOCKS PacketBufferLock
IMPORTS Atom, Basics, Lupine: LupineRuntime, PrincOpsUtils, Process, Rope, RpcPrivate: RPCLupine, RpcPublic: RPC, SafeStorage, UnsafeStorage, VM, VMInternal
EXPORTS LupineRuntime, RPCLupine
SHARES Rope = {
Words: TYPE = LupineRuntime.Words;
Lupine's Packet Buffer Monitor.
PacketBufferLock: PUBLIC MONITORLOCK;
Lupine's errors.
TranslationError: PUBLIC ERROR = CODE;
BindingError: PUBLIC ERROR = CODE;
RuntimeError: PUBLIC ERROR = CODE;
These procedures map into ERRORs (for now).
MarshalingError: PUBLIC PROC = {ERROR};
MarshalingExprError: PUBLIC PROC RETURNS [never: UNSPECIFIED] = {
RETURN[ERROR]};
These procedures map into RpcRuntime ERRORs.
UnmarshalingError: PUBLIC PROC = {
ERROR RpcPublic.CallFailed[why: stubProtocol]};
UnmarshalingExprError: PUBLIC PROC RETURNS [never: UNSPECIFIED] = {
ERROR RpcPublic.CallFailed[why: stubProtocol]};
DispatchingError: PUBLIC PROC RETURNS [never: UNSPECIFIED] = {
ERROR RpcPrivate.RejectUnbound};
Default parameter storage zones.
defaultZones: PUBLIC RPC.Zones ← [
gc: SafeStorage.GetSystemZone[],
heap: UnsafeStorage.GetSystemUZone[],
mds: NIL--??--
];
Parameter marshaling routines.
Checking: BOOLEANTRUE;
Turning this off is okay; critical runtime checks are always on.
FinishThisPkt: PUBLIC PROC [pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength ] RETURNS [--zeroPktLength:-- RpcPrivate.DataLength] = {
Lupine.CheckPktLength[pkt: pkt, lastPkt: FALSE, pktLength: pktLength];
[] ← RpcPrivate.ReceiveExtraPkt[prevPkt: pkt];
RETURN[0];
};
StartNextPkt: PUBLIC PROC [ pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength ] RETURNS [--zeroPktLength:-- RpcPrivate.DataLength] = {
RpcPrivate.SendPrelimPkt[pkt: pkt, length: pktLength];
RETURN[0];
};
CopyToMultiplePkts: PUBLIC PROC [ pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength, dataAdr: LONG POINTER, dataLength: Words ] RETURNS [--newPktLength:-- RpcPrivate.DataLength] = {
initialWords: Words = RpcPrivate.maxDataLength - pktLength;
wholePkts: CARDINALNULL;
finalWords: Words ← NULL;
IF Checking AND NOT (
Preconditions:
pktLength IN [0..RpcPrivate.maxDataLength] AND
dataLength > 0 AND
pktLength+dataLength > RpcPrivate.maxDataLength )
THEN ERROR;
[quotient: wholePkts, remainder: finalWords] ← Basics.DivMod[
num: dataLength-initialWords,
------------------------
den: RpcPrivate.maxDataLength ];
Put initial data at the end of the current pkt.
PrincOpsUtils.LongCopy[
from: dataAdr, to: @pkt.data[pktLength], nwords: initialWords];
dataAdr ← dataAdr + initialWords;
Put intermediate data into whole pkts.
THROUGH [0..wholePkts) DO
RpcPrivate.SendPrelimPkt[pkt: pkt, length: RpcPrivate.maxDataLength];
PrincOpsUtils.LongCopy[
from: dataAdr, to: @pkt.data[0], nwords: RpcPrivate.maxDataLength];
dataAdr ← dataAdr + RpcPrivate.maxDataLength;
ENDLOOP;
Put final data at the beginning of a fresh pkt.
IF finalWords > 0
THEN {
RpcPrivate.SendPrelimPkt[pkt: pkt, length: RpcPrivate.maxDataLength];
PrincOpsUtils.LongCopy[from: dataAdr, to: @pkt.data[0], nwords: finalWords];
RETURN[finalWords];
}
ELSE RETURN[RpcPrivate.maxDataLength];
};
CopyFromMultiplePkts: PUBLIC PROC [ pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength, dataAdr: LONG POINTER, dataLength: Words ] RETURNS [--newPktLength:-- RpcPrivate.DataLength] = {
firstDataAdr: LONG POINTER = dataAdr;
{ ENABLE UNWIND =>
If there's a fault, zero everything. This ensures that any
REF-containing fields are NIL and not chaos-causing garbage.
IF dataLength > 0 THEN {
firstDataAdr^ ← 0;
PrincOpsUtils.LongCopy[
from: firstDataAdr, to: firstDataAdr+1, nwords: dataLength-1 ];
};
initialWords: Words = RpcPrivate.maxDataLength - pktLength;
wholePkts: CARDINALNULL;
finalWords: Words ← NULL;
IF Checking AND NOT (
Preconditions:
pktLength IN [0..RpcPrivate.maxDataLength] AND
dataLength > 0 AND
pktLength+dataLength > RpcPrivate.maxDataLength )
THEN ERROR;
[quotient: wholePkts, remainder: finalWords] ← Basics.DivMod[
num: dataLength-initialWords,
------------------------
den: RpcPrivate.maxDataLength ];
Get initial data from the end of the current pkt.
PrincOpsUtils.LongCopy[
to: dataAdr, from: @pkt.data[pktLength], nwords: initialWords];
dataAdr ← dataAdr + initialWords;
Get intermediate data from whole pkts.
THROUGH [0..wholePkts) DO
Lupine.CheckPktLength[
pkt: pkt, lastPkt: FALSE, pktLength: RpcPrivate.maxDataLength ];
[] ← RpcPrivate.ReceiveExtraPkt[prevPkt: pkt];
PrincOpsUtils.LongCopy[
to: dataAdr, from: @pkt.data[0], nwords: RpcPrivate.maxDataLength];
dataAdr ← dataAdr + RpcPrivate.maxDataLength;
ENDLOOP;
Get final data from the beginning of a fresh pkt.
IF finalWords > 0
THEN {
Lupine.CheckPktLength[
pkt: pkt, lastPkt: FALSE, pktLength: RpcPrivate.maxDataLength ];
[] ← RpcPrivate.ReceiveExtraPkt[prevPkt: pkt];
PrincOpsUtils.LongCopy[to: dataAdr, from: @pkt.data[0], nwords: finalWords];
RETURN[finalWords];
}
ELSE RETURN[RpcPrivate.maxDataLength];
}; -- ENABLE UNWIND.
};
MarshalRope: PUBLIC PROC[ rope: Rope.ROPE, pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength, assertShortRope: BOOL] RETURNS[newLength: RpcPrivate.DataLength] = {
textRope: Rope.Text;
IF pktLength+2 > RpcPrivate.maxDataLength THEN
pktLength ← StartNextPkt[pkt: pkt, pktLength: pktLength];
pkt.data[pktLength] ← rope=NIL;
newLength ← pktLength+1;
IF rope=NIL THEN RETURN;
textRope ← Rope.InlineFlatten[r: rope];
IF assertShortRope AND textRope.length > RpcPrivate.maxShortStringLength THEN MarshalingError;
pkt.data[newLength] ← textRope.length; newLength ← newLength+1;
newLength ← Lupine.CopyToPkt[
pkt: pkt, pktLength: newLength, dataAdr: BASE[DESCRIPTOR[textRope.text]],
dataLength: Lupine.WordsForChars[textRope.length], alwaysOnePkt: FALSE];
};
UnmarshalRope: PUBLIC PROC[pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength, assertShortRope: BOOL] RETURNS[rope: Rope.ROPENIL, newLength: RpcPrivate.DataLength] = {
ropeIsNIL: Lupine.NilHeader;
ropeLength: Lupine.RopeHeader;
textRope: Rope.Text;
IF pktLength+2 > RpcPrivate.maxDataLength
THEN pktLength ← FinishThisPkt[pkt: pkt, pktLength: pktLength];
ropeIsNIL ← pkt.data[pktLength];
newLength ← pktLength+1;
IF ropeIsNIL THEN RETURN;
ropeLength ← pkt.data[newLength]; newLength ← newLength+1;
IF assertShortRope AND ropeLength > RpcPrivate.maxShortStringLength THEN UnmarshalingError;
rope ← textRope ← Rope.NewText[size: ropeLength];
newLength ← Lupine.CopyFromPkt[
pkt: pkt, pktLength: newLength, dataAdr: BASE[DESCRIPTOR[textRope.text]],
dataLength: Lupine.WordsForChars[ropeLength], alwaysOnePkt: FALSE];
};
MarshalAtom: PUBLIC PROC[ atom: ATOM, pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength] RETURNS[newLength: RpcPrivate.DataLength] = {
IF atom=NIL THEN atom ← Atom.MakeAtom[""];
RETURN[MarshalRope[Atom.GetPName[atom], pkt, pktLength, FALSE]];
};
UnmarshalAtom: PUBLIC PROC[pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength] RETURNS[atom: ATOM, newLength: RpcPrivate.DataLength] = {
pName: Rope.ROPE;
[pName, newLength] ← UnmarshalRope[pkt, pktLength, FALSE];
atom ← Atom.MakeAtom[pName];
};
Packet Buffers
Note that there are inline routines in RPCLupine.
Packet Buffers Data
MaxPacketBufferListSize: INT = RPCLupine.MaxPacketBufferListSize; -- Maximum size kept in the cache
cacheAllocCount: INT ← 0 ;
allocRover: VM.PageNumber ← 0;
MaxPacketBufferIntervalInRec: INT = RPCLupine.MaxPacketBufferIntervalInRec;
PacketBufferCacheListRec: TYPE = RPCLupine.PacketBufferCacheListRec;
PacketBufferCacheList: PUBLIC ARRAY [1..MaxPacketBufferListSize] OF REF PacketBufferCacheListRec ← ALL[NIL];
Packet Buffers Exported Procedures
Alloc: PUBLIC PROC [nWords: CARDINAL] RETURNS [address: LONG POINTER TO WORD] = {
nPages: VM.PageCount = MAX[1, VM.PagesForWords[nWords]];
interval: VM.Interval ← VM.nullInterval;
IF nPages > MaxPacketBufferListSize THEN {
DO
interval ← VM.Allocate[count: nPages ! VM.CantAllocate => CONTINUE;];
IF interval.page # 0 THEN {
VM.SwapIn[interval];
EXIT;
};
Process.Pause[1];
ENDLOOP;
address ← VM.AddressForPageNumber[interval.page];
RETURN;
}
ELSE {
found: BOOL;
[found, address] ← getFromCache[nPages];
IF ~found THEN {
IF cacheAllocCount > 10 THEN cacheAllocCount ← 0;
IF cacheAllocCount = 0 THEN allocRover ← VMInternal.partitions[normalVM].page;
cacheAllocCount ← cacheAllocCount + 1;
DO
interval ← VM.Allocate[count: nPages, start: allocRover ! VM.CantAllocate => CONTINUE;];
IF interval.page # 0 THEN EXIT;
Process.Pause[1];
ENDLOOP;
allocRover ← interval.page;
address ← VM.AddressForPageNumber[interval.page];
};
};
};
DeAlloc: PUBLIC PROC[address: LONG POINTER TO WORD, nWords: CARDINAL] = {
nPages: VM.PageCount = MAX[1, VM.PagesForWords[nWords]];
interval: VM.Interval ← [page: VM.PageNumberForAddress[address], count: nPages];
IF LOOPHOLE[address, INT] # 0 THEN {
IF nPages > MaxPacketBufferListSize THEN {
VM.Free[interval];
RETURN;
}
ELSE {
putIntoCache[interval];
};
};
};
Packet Buffers Internal Procedures
getFromCache: ENTRY PROC [nPages: VM.PageCount] RETURNS [found: BOOL, address: LONG POINTER TO WORD] = INLINE {
putIndex: INT ← PacketBufferCacheList[nPages].putIndex;
IF putIndex > 1 THEN {
putIndex← putIndex - 1;
address ← VM.AddressForPageNumber[PacketBufferCacheList[nPages].intervalStarts[putIndex]];
PacketBufferCacheList[nPages].putIndex ← putIndex;
found ← TRUE;
}
ELSE {
IF PacketBufferCacheList[nPages].prev = NIL THEN found ← FALSE
ELSE {
PacketBufferCacheList[nPages] ← PacketBufferCacheList[nPages].prev;
address ← VM.AddressForPageNumber[ PacketBufferCacheList[nPages].intervalStarts[MaxPacketBufferIntervalInRec]];
PacketBufferCacheList[nPages].putIndex ← MaxPacketBufferIntervalInRec;
found ← TRUE;
};
};
};
putIntoCache: ENTRY PROC [interval: VM.Interval] = INLINE {
count: INT = interval.count;
putIndex: INT ← PacketBufferCacheList[count].putIndex;
VM.MakeUnchanged[interval]; -- by making the pages "unchanged", we retain the physical memory associated with this interval (decreases page faults); however we also make the page "clean" in that if it is reclaimed it will not be written to the backing file
IF putIndex > MaxPacketBufferIntervalInRec THEN {
putIndex ← 1;
IF PacketBufferCacheList[count].next = NIL THEN {
PacketBufferCacheList[count].next ← NEW [PacketBufferCacheListRec ← [prev: PacketBufferCacheList[count]]];
PacketBufferCacheList[count].next.prev ← PacketBufferCacheList[count];
};
PacketBufferCacheList[count] ← PacketBufferCacheList[count].next;
PacketBufferCacheList[count].putIndex ← 1;
};
PacketBufferCacheList[count].intervalStarts[putIndex] ← interval.page;
PacketBufferCacheList[count].putIndex ← putIndex + 1;
};
Module initialization.
CopyTo/FromMultiplePkts assume that zero is the representation of NIL.
IF NIL # LOOPHOLE[0, POINTER] THEN ERROR;
FOR i: INT IN [1..MaxPacketBufferListSize] DO
PacketBufferCacheList[i] ← NEW[PacketBufferCacheListRec ← [prev: NIL]];
ENDLOOP;
}.
Bob Hagmann February 8, 1985 3:35:26 pm PST
changes to: DIRECTORY
Bob Hagmann February 11, 1985 9:51:22 am PST
changes to: LupineRuntimeImpl, PacketBufferLock, Packet, MaxPacketBufferListSize, PacketBufferCacheList, DIRECTORY, MaxPacketBufferIntervalInRec, PacketBufferCacheListRec
Bob Hagmann March 15, 1985 4:40:18 pm PST
changes to: LupineRuntimeImpl
Bob Hagmann May 13, 1985 12:48:26 pm PDT
add VM.SwapIn call
changes to: Alloc
Bob Hagmann May 13, 1985 12:56:13 pm PDT
changes to: DIRECTORY
Polle Zellweger (PTZ) August 2, 1985 2:25:46 pm PDT
changes to: MarshalRope