<> <> <> <> <> DIRECTORY Allocator USING[bsiEscape, EHeaderP, ExtendedHeader, PUZone], AllocatorOps USING[DoubleFreeHeader, Rover, DFHeaderP, DoCreateMediumSizedObject, DoFreeMediumSizedObject, nonNullType], Process USING [Pause], UnsafeStoragePrivate USING[], VMInternal USING[partitions], -- move this to VMSideDoor VM USING[AddressForPageNumber, Allocate, CantAllocate, Free, Interval, MakeUnchanged, nullInterval, PageCount, PagesForWords, PageNumber, PageNumberForAddress, wordsPerPage]; UnsafeAllocatorImpl: MONITOR <> IMPORTS AllocatorOps, Process, VM, VMInternal EXPORTS AllocatorOps, UnsafeStoragePrivate = BEGIN OPEN AllocatorOps; <> InsufficientVM: ERROR = CODE; <> UZHandle: TYPE = LONG POINTER TO UZObject; UZObject: TYPE = MACHINE DEPENDENT RECORD [ procs(0:0..31): LONG POINTER TO UZProcs ]; UZProcs: TYPE = MACHINE DEPENDENT RECORD [ new(0): PROC[self: UZHandle, size: CARDINAL] RETURNS[LONG POINTER], free(1): PROC[self: UZHandle, object: LONG POINTER] ]; <> <> ppzProcs: UZProcs _ [new: PPZNew, free: NIL]; ppzObject: UZObject _ [procs: @ppzProcs]; permanentPageZone: PUBLIC UNCOUNTED ZONE _ LOOPHOLE[LONG[@ppzObject]]; <> <> <> heapRoot: DoubleFreeHeader _ [ <> eh: [extendedSize: 0, normalHeader: [blockSizeIndex: Allocator.bsiEscape]], nextFree: @heapRoot, prevFree: @heapRoot ]; heapRover: Rover _ @heapRoot; <<>> <> skipHeader: CARDINAL = SIZE[Allocator.ExtendedHeader]; skipToHeader: CARDINAL = VM.wordsPerPage - skipHeader; MaxTransientPageSize: INT = 8; -- Maximum size kept in the cache transientPageAllocCount: INT _ 0 ; transientPageAllocRover: VM.PageNumber _ 0; MaxTransientPageIntervalInRec: INT = 10; TransientPageListRec: TYPE = RECORD [ prev: REF TransientPageListRec, next: REF TransientPageListRec _ NIL, putIndex: CARDINAL _ 1, intervalStarts: ARRAY [1..MaxTransientPageIntervalInRec] OF VM.PageNumber _ ALL[0] ]; TransientPageList: PUBLIC ARRAY [1..MaxTransientPageSize] OF REF TransientPageListRec _ ALL[NIL]; <<>> <> <<>> <> PPZNew: PROC[self: UZHandle, size: CARDINAL] RETURNS[lp: LONG POINTER] = { nPages: INT = VM.PagesForWords[size]; lp _ VM.AddressForPageNumber [VM.Allocate[count: nPages, in64K: TRUE ! VM.CantAllocate => GOTO noVM].page]; EXITS noVM => ERROR InsufficientVM; }; <<>> <> <<>> <> <= SIZE[DoubleFreeHeader].>> <> <> CreateMediumSizedHeapObject: PUBLIC ENTRY PROC[size: NAT] RETURNS[LONG POINTER] = { ENABLE UNWIND => NULL; RETURN[ AllocatorOps.DoCreateMediumSizedObject[size, nonNullType, @heapRover, FALSE]]; }; <> <> FreeMediumSizedHeapObject: PUBLIC ENTRY PROC[ptr: DFHeaderP] = { ENABLE UNWIND => NULL; AllocatorOps.DoFreeMediumSizedObject[ptr, heapRover]; }; <<>> <> <> NewTransientPageObject: PUBLIC PROC[self: Allocator.PUZone, size: CARDINAL] RETURNS[p: LONG POINTER] = { nPages: VM.PageCount = MAX[2, VM.PagesForWords[size]+1]; -- both PagesForWords and MAX calls are inlines interval: VM.Interval _ VM.nullInterval; ehp: Allocator.EHeaderP; IF nPages > MaxTransientPageSize THEN { <> DO interval _ VM.Allocate[count: nPages ! VM.CantAllocate => CONTINUE;]; IF interval.page # 0 THEN EXIT; Process.Pause[1]; ENDLOOP; p _ VM.AddressForPageNumber[interval.page]; -- an inline } ELSE { found: BOOL; [found, p] _ getFromTransientCache[nPages]; -- an inline ENTRY IF ~found THEN { <> IF transientPageAllocCount > 10 THEN transientPageAllocCount _ 0; IF transientPageAllocCount = 0 THEN transientPageAllocRover _ VMInternal.partitions[normalVM].page; transientPageAllocCount _ transientPageAllocCount + 1; DO interval _ VM.Allocate[count: nPages, start: transientPageAllocRover ! VM.CantAllocate => CONTINUE;]; IF interval.page # 0 THEN { p _ VM.AddressForPageNumber[interval.page]; EXIT}; Process.Pause[1]; ENDLOOP; transientPageAllocRover _ interval.page; }; }; <

> p _ p + skipToHeader; ehp _ LOOPHOLE[p, Allocator.EHeaderP]; ehp^ _ [ sizeTag: pages, extendedSize: nPages - 1, normalHeader: [blockSizeIndex: Allocator.bsiEscape, type: AllocatorOps.nonNullType] ]; p _ p + skipHeader; }; FreeTransientPageObject: PUBLIC PROC [self: Allocator.PUZone, object: LONG POINTER] = { IF LOOPHOLE[object, INT] # 0 THEN { <> objectHdr: Allocator.EHeaderP = LOOPHOLE[object - skipHeader]; nPages: VM.PageCount = objectHdr.extendedSize+1; interval: VM.Interval _ [page: VM.PageNumberForAddress[objectHdr], count: nPages]; IF nPages > MaxTransientPageSize THEN { VM.Free[interval]; -- does procedure call RETURN; } ELSE { putIntoTransientCache[interval]; -- this calls an inline, but it does a procedure call to VM.MakeUnchanged }; }; }; <> getFromTransientCache: ENTRY PROC [nPages: VM.PageCount] RETURNS [found: BOOL, address: LONG POINTER TO WORD] = INLINE { putIndex: INT _ TransientPageList[nPages].putIndex; IF putIndex > 1 THEN { putIndex_ putIndex - 1; address _ VM.AddressForPageNumber[TransientPageList[nPages].intervalStarts[putIndex]]; TransientPageList[nPages].putIndex _ putIndex; found _ TRUE; } ELSE { IF TransientPageList[nPages].prev = NIL THEN found _ FALSE ELSE { TransientPageList[nPages] _ TransientPageList[nPages].prev; address _ VM.AddressForPageNumber[ TransientPageList[nPages].intervalStarts[MaxTransientPageIntervalInRec]]; TransientPageList[nPages].putIndex _ MaxTransientPageIntervalInRec; found _ TRUE; }; }; }; putIntoTransientCache: ENTRY PROC [interval: VM.Interval] = INLINE { count: INT = interval.count; putIndex: INT _ TransientPageList[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 > MaxTransientPageIntervalInRec THEN { putIndex _ 1; IF TransientPageList[count].next = NIL THEN { TransientPageList[count].next _ NEW [TransientPageListRec _ [prev: TransientPageList[count]]]; TransientPageList[count].next.prev _ TransientPageList[count]; }; TransientPageList[count] _ TransientPageList[count].next; TransientPageList[count].putIndex _ 1; }; TransientPageList[count].intervalStarts[putIndex] _ interval.page; TransientPageList[count].putIndex _ putIndex + 1; }; <> <> InitializeTransientPageZone: PUBLIC PROC[] = { FOR i: INT IN [1..MaxTransientPageSize] DO TransientPageList[i] _ NEW[TransientPageListRec _ [prev: NIL]]; ENDLOOP; }; END. <> <> <<>> <> <> <<>>