-- Swapper>SimpleSpaceImpl.mesa (February 4, 1981 by Knutsen)
-- This module is structurally at a level above the Swapper, and below the FileMgr. It is of type "Cached Descriptor", but the items exported to StoragePrograms must be Initially Resident.
DIRECTORY
CachedRegion USING [Apply, BackFileType, Desc, forceOut, Insert, kill, Operation, Outcome, pin, unmap],
CachedSpace USING [Desc, Get, Handle, handleNull, Insert, Level, SizeSwapUnit, Update],
Environment USING [maxPagesInMDS],
File USING [nullID, write],
Inline USING [BITAND],
MStore USING [Deallocate, Relocate],
PageMap USING [flagsNone, flagsWriteProtected],
SimpleSpace,
Space USING [defaultWindow, Handle, PageCount, PageNumber, WindowOrigin],
Transaction USING [Handle],
StoragePrograms USING [countHyperspace, createSimpleSpace, empty, EnumerateStartList, IsUtilityPilot, pageHyperspace, pageMDS, SpaceOptions, StartListProc, tableBase],
VM USING [Interval];
SimpleSpaceImpl: PROGRAM
IMPORTS CachedRegion, CachedSpace, Inline, MStore, StoragePrograms
EXPORTS SimpleSpace, StoragePrograms
SHARES File --USING [Capability]-- =
BEGIN OPEN SimpleSpace;
-- ++Space.++Handle: PUBLIC TYPE = CachedSpace.Handle; ++ CAN’T DO THIS because of a name confict between Space.Map and SimpleSpace.Map, etc.
-- Handle: TYPE = CachedSpace.Handle; ++ CAN’T DO THIS because of a name confict between Space.Handle "warning: Handle is private but matches an export"
SimpleSpaceHandle: TYPE = CachedSpace.Handle;
locationData: TYPE = RECORD [ total: VM.Interval, largestHole: VM.Interval];
locations: ARRAY Location OF POINTER TO locationData;
first64K, mds, hyperspace: locationData;
initializationDisabled: BOOLEANFALSE;
Bug: PRIVATE ERROR [BugType] = CODE;
BugType: TYPE = {badOutcome, initializationDisabled, memoryOverflow, notEndRegion, regionCacheOverflow, spaceCacheOverflow, spaceDMissing, notSimpleSpace};
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Exported to StoragePrograms:
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nullSpaceHandle: PUBLIC Space.Handle ← LOOPHOLE[CachedSpace.handleNull]; -- for use before Space.nullHandle is initialized.
nullTransactionHandle: PUBLIC Transaction.Handle ← -- for use before Transaction.nullHandle is initialized.
-- TransactionInternal.Handle[[txEdition: invalidTxEdition, index: nullTxIndex]];
LOOPHOLE[000077B]; -- (checked at runtime by TransactionStateImpl)
InitializeSimpleSpace: PUBLIC PROCEDURE [] =
BEGIN
InitializeLocations: StoragePrograms.StartListProc --[index]-- =
-- Initializes allocator data from empty spaces in StartPilot-allocated memory.
BEGIN
WITH e: StoragePrograms.tableBase[index] SELECT FROM
space => IF e.type.class=empty THEN
DescribeSpace[StoragePrograms.empty, e.vmpage, e.pages, Space.defaultWindow];
ENDCASE;
END;
--Total VM IntervalLargest Hole
first64K ←[ [Space.PageNumber[0], Environment.maxPagesInMDS],[0, 0]];
mds ←[ [StoragePrograms.pageMDS, Environment.maxPagesInMDS],[StoragePrograms.pageMDS, 0]];
hyperspace ←[ [StoragePrograms.pageHyperspace, StoragePrograms.countHyperspace],[StoragePrograms.pageHyperspace, 0]];
locations ← [
first64K: @first64K,
mds: IF StoragePrograms.pageMDS=first64K.total.page THEN @first64K ELSE @mds,
hyperspace: @hyperspace];
StoragePrograms.EnumerateStartList[InitializeLocations];
END;
AllocateVM: PUBLIC PROCEDURE [count: Space.PageCount, location: Location] RETURNS [page: Space.PageNumber] =
BEGIN OPEN loc: locations[location];
IF count > loc.largestHole.count THEN ERROR Bug[memoryOverflow]; -- free space exhausted.
page ← loc.largestHole.page;
loc.largestHole.page ← loc.largestHole.page+count;
loc.largestHole.count ← loc.largestHole.count-count;
END;
DescribeSpace: PUBLIC PROCEDURE [options: StoragePrograms.SpaceOptions,
page: Space.PageNumber, count: Space.PageCount, window: Space.WindowOrigin] =
BEGIN
DescribeSpaceInternal[options, page, count, window, SimpleSpace.noSwapUnits];
END;
DescribeSpaceInternal: PROCEDURE [
options: StoragePrograms.SpaceOptions, page: Space.PageNumber, count: Space.PageCount,
window: Space.WindowOrigin, sizeSwapUnit: SimpleSpace.SizeSwapUnit] =
BEGIN OPEN StoragePrograms;
location: Location;
regionVictim: CachedRegion.Desc;
space, spaceVictim: CachedSpace.Desc;
levelPrimarySpace: CachedSpace.Level = -- (VM is at level 0, MDS is at level 1.)
IF page IN [StoragePrograms.pageMDS .. StoragePrograms.pageMDS+Environment.maxPagesInMDS) THEN 2 ELSE 1;
writeProtected: BOOLEAN = Inline.BITAND[window.file.permissions, File.write]=0;
IF initializationDisabled THEN ERROR Bug[initializationDisabled];
IF count=0 THEN RETURN;
IF options.mStoreDeallocate THEN
MStore.Deallocate[interval: [page, count], promised: FALSE];
IF options.emptyInterval THEN --update allocator data--
FOR location IN Location DO OPEN loc: locations[location];
IF count>loc.largestHole.count AND page>=loc.total.page AND page+count<=loc.total.page+loc.total.count
THEN loc.largestHole ← [page: page, count: count];
ENDLOOP;
IF options.createRegion THEN
IF StoragePrograms.IsUtilityPilot[] AND options.pinned THEN NULL -- UtilityPilot doesn’t need/want region descs for pinned regions.
ELSE
BEGIN
levelRegion: CachedSpace.Level = levelPrimarySpace + (IF options.subspace THEN 1 ELSE 0);
regionVictim ← CachedRegion.Insert[[
interval: [page, count],
level: levelRegion,
levelMapped: levelPrimarySpace,
hasSwapUnits: sizeSwapUnit~=SimpleSpace.noSwapUnits,
dTemperature: , -- don’t care
dPinned: options.pinRegionD,
dDirty: TRUE,
state: SELECT TRUE FROM
options.pinned => inPinned,
options.initiallyResident => inSwappableWarmest,
options.mapped => outAlive,
ENDCASE => unmapped,
writeProtected: writeProtected,
needsLogging: FALSE,
beingFlushed: FALSE ]];
IF regionVictim.dDirty THEN ERROR Bug[regionCacheOverflow];
IF options.initiallyResident AND writeProtected THEN
-- The following depends on Relocate NOT temporarily vacating the pages
[] ← MStore.Relocate[interval: [page, count], pageDest: page, flagsKeep: PageMap.flagsNone,
flagsAdd: PageMap.flagsWriteProtected]; -- write protect the pages now in memory.
END;
IF options.createSpace THEN
-- For UtilityPilot, we would like to avoid creating space descs for spaces which only contain resident swap units, or which only contain initiallyResident, readOnly swap units.
-- IF StoragePrograms.IsUtilityPilot[] AND options.pinned THEN NULL ++ UtilityPilot doesn’t need/want space descs for pinned spaces.
-- ELSE
BEGIN
space ← [
interval: [page, count],
level: levelPrimarySpace,
dPinned: options.pinSpaceD,
dDirty: TRUE,
pinned: options.pinned,
state: IF options.mapped THEN mapped ELSE unmapped,
writeProtected: writeProtected, -- (ok if state=unmapped.)
hasSwapUnits: sizeSwapUnit~=SimpleSpace.noSwapUnits,
sizeSwapUnit: MAX[1, sizeSwapUnit], -- (avoid illegal swap unit size of 0)
dataOrFile: file,
pageRover: page,
vp: long[window: window, countMapped: count, transaction: nullTransactionHandle] ];
CachedSpace.Insert[@spaceVictim, @space];
IF spaceVictim.dDirty THEN ERROR Bug[spaceCacheOverflow];
END;
END;
HandleFromPage: PUBLIC PROCEDURE [page: Space.PageNumber] RETURNS [handle: Space.Handle] =
{ RETURN[ LOOPHOLE[ SimpleSpaceHandle[
level: IF page IN [StoragePrograms.pageMDS .. StoragePrograms.pageMDS+Environment.maxPagesInMDS) THEN 2 ELSE 1,
page: page] ] ] };
SuperFromPage: PUBLIC PROCEDURE [page: Space.PageNumber] RETURNS [handle: Space.Handle, superPage: Space.PageNumber] =
{ IF page IN [StoragePrograms.pageMDS .. StoragePrograms.pageMDS+Environment.maxPagesInMDS)
THEN RETURN[ handle: LOOPHOLE[ SimpleSpaceHandle[level: 1, page: StoragePrograms.pageMDS] ],
superPage: StoragePrograms.pageMDS]
ELSE RETURN[ handle: LOOPHOLE[ SimpleSpaceHandle[level: 0, page: FIRST[Space.PageNumber] ] ],
superPage: FIRST[Space.PageNumber] ] };
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- SimpleSpace implementation:
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ApplyToSpace: PROCEDURE [spaceD: POINTER TO CachedSpace.Desc, operation: CachedRegion.Operation] =
-- Performs, in ascending order, operation (successfully) for each swap unit of the space (exactly as required by CachedRegion.Apply).
BEGIN
page, pageNext: Space.PageNumber;
outcome: CachedRegion.Outcome;
FOR page ← spaceD.interval.page, pageNext WHILE page < spaceD.interval.page + spaceD.interval.count DO -- for all swapUnits..
DO --REPEAT operation UNTIL outcome=ok or notePinned--
[outcome, pageNext] ← CachedRegion.Apply[page, operation];
WITH outcome SELECT FROM
ok, notePinned => EXIT;
retry => NULL; -- go around again
ENDCASE => ERROR Bug[badOutcome];
ENDLOOP;
ENDLOOP;
IF pageNext ~= spaceD.interval.page + spaceD.interval.count THEN ERROR Bug[notEndRegion];
END;
CopyIn: PUBLIC PROCEDURE [handle: Space.Handle, window: Space.WindowOrigin, countMapped: Space.PageCount] =
BEGIN OPEN hnd: LOOPHOLE[handle, SimpleSpaceHandle];
spaceD, fromSpace: CachedSpace.Desc;
CachedSpace.Get[@spaceD, hnd];
IF spaceD.state=missing OR ~spaceD.dPinned THEN ERROR Bug[notSimpleSpace];
fromSpace ← spaceD; -- sets fromSpace.interval, .mapped, and .writeProtected (to keep swapper happy).
fromSpace.window ← window;
fromSpace.countMapped ← MIN[countMapped, spaceD.countMapped];
ApplyToSpace[@spaceD, [ ifMissing: report, ifCheckedOut: wait, afterForking: wait, vp: copyIn[ from: @fromSpace ] ] ];
END;
CopyOut: PUBLIC PROCEDURE [handle: Space.Handle, window: Space.WindowOrigin, countMapped: Space.PageCount] =
BEGIN OPEN hnd: LOOPHOLE[handle, SimpleSpaceHandle];
spaceD, toSpace: CachedSpace.Desc;
CachedSpace.Get[@spaceD, hnd];
IF spaceD.state=missing OR ~spaceD.dPinned THEN ERROR Bug[notSimpleSpace];
toSpace ← spaceD; -- sets fromSpace.interval, .mapped, and .writeProtected (to keep swapper happy).
toSpace.window ← window;
toSpace.countMapped ← MIN[countMapped, spaceD.countMapped];
ApplyToSpace[@spaceD, [ ifMissing: report, ifCheckedOut: wait, afterForking: wait, vp: copyOut[ to: @toSpace ] ] ];
END;
Create: PUBLIC PROCEDURE [count: Space.PageCount, location: Location, sizeSwapUnit: SimpleSpace.SizeSwapUnit] RETURNS [handle: Space.Handle] =
BEGIN OPEN locations[location];
hnd: SimpleSpaceHandle;
IF count > largestHole.count THEN ERROR Bug[memoryOverflow]; -- free space exhausted
hnd.page ← largestHole.page;
hnd.level← IF hnd.page IN [mds.total.page .. mds.total.page+mds.total.count) THEN 2 ELSE 1;
largestHole.page ← largestHole.page+count;
largestHole.count ← largestHole.count-count;
DescribeSpaceInternal[StoragePrograms.createSimpleSpace, hnd.page, count, Space.defaultWindow, sizeSwapUnit];
RETURN[ LOOPHOLE[hnd] ];
END;
DisableInitialization: PUBLIC PROCEDURE =
{ initializationDisabled ← TRUE };
ForceOut: PUBLIC PROCEDURE [handle: Space.Handle] =
BEGIN OPEN hnd: LOOPHOLE[handle, SimpleSpaceHandle];
spaceD: CachedSpace.Desc;
CachedSpace.Get[@spaceD, hnd];
IF spaceD.state=missing OR ~spaceD.dPinned THEN ERROR Bug[notSimpleSpace];
ApplyToSpace[@spaceD, CachedRegion.forceOut];
END;
Kill: PUBLIC PROCEDURE [handle: Space.Handle] =
BEGIN OPEN hnd: LOOPHOLE[handle, SimpleSpaceHandle];
spaceD: CachedSpace.Desc;
CachedSpace.Get[@spaceD, hnd];
IF spaceD.state=missing OR ~spaceD.dPinned THEN ERROR Bug[notSimpleSpace];
ApplyToSpace[@spaceD, CachedRegion.kill];
END;
Map: PUBLIC PROCEDURE [handle: Space.Handle, window: Space.WindowOrigin, andPin: BOOLEAN, countMapped: Space.PageCount] =
-- If window.file.fID=nullID, then we allocate real memory and pin it (no backing file); in this case, andPin and countMapped are ignored.
-- Else (backing file) assumes window.file is writable and file doesn’t end before space.
BEGIN OPEN hnd: LOOPHOLE[handle, SimpleSpaceHandle];
spaceD: CachedSpace.Desc;
backFileType: CachedRegion.BackFileType;
CachedSpace.Get[@spaceD, hnd];
IF --spaceD.state=missing OR-- spaceD.state~=unmapped OR ~spaceD.dPinned THEN ERROR Bug[notSimpleSpace]; -- we check for unmapped here because we will update the SpaceD before doing Apply, which would make debugging harder.
spaceD.window ← window; -- either a real window or Space.defaultWindow
IF window.file.fID=File.nullID THEN
BEGIN --create space with no backing file--
spaceD.pinned ← TRUE;
spaceD.state ← mapped; -- this is a convenient lie.
spaceD.writeProtected ← FALSE;
-- spaceD.hasSwapUnits is set when the simpleSpace is created.
spaceD.dataOrFile ← data;
spaceD.countMapped ← spaceD.interval.count; -- this is a convenient lie.
backFileType ← none;
END
ELSE
BEGIN --create space with backing file--
spaceD.pinned ← andPin;
spaceD.state ← mapped;
spaceD.writeProtected ← ( Inline.BITAND[window.file.permissions, File.write]=0 );
-- spaceD.hasSwapUnits is set when the simpleSpace is created.
spaceD.dataOrFile ← file;
spaceD.countMapped ← IF countMapped=defaultCount THEN spaceD.interval.count ELSE countMapped;
backFileType ← file;
END;
IF ~CachedSpace.Update[@spaceD].found THEN ERROR Bug[spaceDMissing];
ApplyToSpace[@spaceD,
[ ifMissing: report, ifCheckedOut: wait, afterForking: ,
vp: map[ spaceD.level, backFileType, spaceD.writeProtected, --andNeedsLogging:-- FALSE] ] ];
IF backFileType=file AND andPin THEN
ApplyToSpace[@spaceD, CachedRegion.pin];
END;
Unmap: PUBLIC PROCEDURE [handle: Space.Handle] =
BEGIN OPEN hnd: LOOPHOLE[handle, SimpleSpaceHandle];
spaceD: CachedSpace.Desc;
CachedSpace.Get[@spaceD, hnd];
IF --spaceD.state=missing OR--spaceD.state~=mapped OR ~spaceD.dPinned THEN ERROR Bug[notSimpleSpace]; -- we check for unmapped here because the Swapper can’t.
ApplyToSpace[@spaceD, CachedRegion.unmap];
spaceD.pinned ← FALSE;
spaceD.state ← unmapped;
IF ~CachedSpace.Update[@spaceD].found THEN ERROR Bug[spaceDMissing];
END;
END.
LOG
(For earlier log entries, see Pilot 4.0 archive version.)
April 16, 1980 12:27 PMKnutsenAdded InitializeSimpleSpace, AllocateVM; fix up for nonzero MDS; implement simpleSpaces with swap units.
July 1, 1980 5:32 PMKnutsenNew StartPilot/StartList; different SpaceOptions; move handling of empty spaces to InitializeSimpleSpace; fix bug unmapping spaces w/ swapUnits; export nullSpaceHandle.
July 14, 1980 5:22 PMGobbelMade compatible with new CachedRegion.Desc, make Unmap do equivalent of ApplyToInterval so we get all swap units.
July 16, 1980 2:43 PMGobbelMade DescribeSpaceInternal look at U switch and pinned boolean before making new cache entries.
July 19, 1980 1:03 PMMcJonesAdded missing "=CODE" to definition of Error.
July 1, 1980 5:32 PMKnutsenMade compatible with new CachedRegion.Desc.
September 2, 1980 2:52 PMKnutsenImplemented Kill, CopyIn, CopyOut. Handle swap units in region. Page[] now an inline. Tried to convert to exported type Handle.. so sorry, Compiler won’t allow!
January 12, 1981 10:43 AMKnutsenAR6881: nullTransaction had wrong value.
February 4, 1981 9:41 AMKnutsenAlways create region descs for swappable stuff, even in UtilityPilot. For now, always create space descs in UtilityPilot, even for resident and initiallyResident, readOnly stuff.