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, December 26, 1985 3:54:23 pm 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:
BOOLEAN ←
TRUE;
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: CARDINAL ← NULL;
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: CARDINAL ← NULL;
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.
ROPE←
NIL, 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] = {
pName: Rope.ROPE = IF atom=NIL THEN NIL ELSE Atom.GetPName[atom];
RETURN[MarshalRope[pName, 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 ← IF pName=NIL THEN NIL ELSE 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