LupineRuntimeExtrasExtrasImpl.mesa
Last Edited by: Bob Hagmann, October 26, 1984 8:31:28 am PDT
DIRECTORY
Process,
RPCLupineExtras,
VM,
VMInternal;
LupineRuntimeExtrasExtrasImpl: MONITOR LOCKS PacketBufferLock
IMPORTS Process, VM, VMInternal
EXPORTS RPCLupineExtras = {
Note: Make LupineRuntime a Monitor when this is folded in
Declarations
PacketBufferLock: PUBLIC MONITORLOCK;
MaxPacketBufferListSize: INT = RPCLupineExtras.MaxPacketBufferListSize; -- Maximum size kept in the cache
cacheAllocCount: INT ← 0 ;
allocRover: VM.PageNumber ← 0;
MaxPacketBufferIntervalInRec: INT = RPCLupineExtras.MaxPacketBufferIntervalInRec;
PacketBufferCacheListRec: TYPE = RPCLupineExtras.PacketBufferCacheListRec;
PacketBufferCacheList: PUBLIC ARRAY [1..MaxPacketBufferListSize] OF REF PacketBufferCacheListRec ← ALL[NIL];
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 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];
};
};
};
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;
};
Initialization
FOR i: INT IN [1..MaxPacketBufferListSize] DO
PacketBufferCacheList[i] ← NEW[PacketBufferCacheListRec ← [prev: NIL]];
ENDLOOP;
}.