-- Copyright (C) 1982, 1984, 1985 by Xerox Corporation. All rights reserved.
-- Transport Mechanism Filestore - heap restart
-- HeapRestart.mesa
-- HGM, 15-Sep-85 13:02:36
-- Ted Wobber 3-Nov-82 12:54:42 --
-- Andrew Birrell November 1, 1982 3:55 pm --
-- Hankins 21-Aug-84 13:39:01
DIRECTORY
BitMapDefs USING [Create, MapIndex, Set],
Heap USING [MakeNode, systemZone],
HeapDefs USING [ObjectOffset, objectStart],
HeapFile,
HeapFileDefs USING [Address, AddToFreeList, NotifyFreeCount, RecordAllocation],
HeapXDefs USING [PageHeader, ObjectHeader],
Inline USING [LongCOPY],
LogDefs USING [ShowNumber, WriteLine, WriteLogEntry],
LogPrivateDefs USING [tty],
ObjectDir,
ObjectDirDefs --EXPORT only-- ,
ObjectDirXDefs USING [gapObjectNumber, ObjectNumber],
System USING [switches],
TTY USING [GetChar, GetDecimal, PutCR, PutChar, PutDecimal, PutLine, PutString],
VMDefs USING [
CloseFile, FileHandle, FullAddress, GetFileLength, MarkStartWait, MarkStart,
OpenFile, Page, PageAddress, PageIndex, PageNumber, pageSize, ReadPage,
Release, SetFileLength, UsePage, WaitFile];
HeapRestart: MONITOR RETURNS [initHeap: BOOLEAN]LOCKS ObjectDir.LOCK
IMPORTS
BitMapDefs, Heap, HeapFile, HeapFileDefs, Inline, LogDefs, LogPrivateDefs,
ObjectDir, System, TTY, VMDefs
EXPORTS ObjectDirDefs, HeapDefs
SHARES HeapFile, ObjectDir =
BEGIN
-- Opens files concerned with heap:
-- "Heap.ObjectDir" The object directory; initialised on every restart
-- and extended dynamically as needed during run.
-- "Heap.Data" The file containing the heap objects is considered
-- as a sequence of separate segments, numbered
-- [FIRST[HeapFileDefs.Segment]..LAST[HeapFileDefs.Segment]]
-- These segments are of equal size. Heap.Data should
-- be pre-allocated on consecutive pages of physical disk.
-- "Heap.Segments" Ordering for segments of Heap.Data; two pages only. If
-- this file is size 0 on entry, then 'initHeap' is set
-- to TRUE and all files are initialised to a state
-- corresponding to there being no message bodies.
-- The object directory and ancillary variables are set to correspond to
-- the heap objects found to exist in the heap file, read in the
-- order given by "Heap.Chain", with their reference counts set to zero
-- pending restart of steering list queues and mailboxes. After restart of
-- steering list queues and mailboxes, the Compactor should be STARTed, and
-- will then run as an asynchronous activity.
RestartObject: PUBLIC ENTRY PROCEDURE [obj: ObjectDirXDefs.ObjectNumber] =
BEGIN -- executes inside the ObjectDir monitor --
OPEN ObjectDir;
either: LONG POINTER TO DirData = findData[obj];
WITH data: either SELECT FROM
used =>
BEGIN
IF data.count = LAST[ObjectCount] THEN ERROR objectCountTooBig;
data.count ← data.count + 1;
END;
ENDCASE => ERROR objectNotInUse[];
releaseData[dirty];
END;
BadChainSize: ERROR = CODE;
NoWrittenHeapSegment: ERROR = CODE;
HeapDataTooSmall: ERROR = CODE;
BailOut: SIGNAL = CODE;
ReadChain: PROCEDURE =
BEGIN OPEN HeapFile, client;
chainPages: CARDINAL;
handle ← VMDefs.OpenFile[options: old, name: "Heap.Data"L, cacheFraction: 40];
IF VMDefs.GetFileLength[handle].page = 0 THEN ERROR HeapDataTooSmall[];
segmentCount ← VMDefs.GetFileLength[handle].page / segmentSize;
chainPages ← (segmentCount + segmentsPerPage - 1) / segmentsPerPage;
segmentCeiling ← segmentCount + chainPages * headerSize - 1;
chainHandle ← VMDefs.OpenFile[
options: oldOrNew, name: "Heap.Segments"L, cacheFraction: 0];
chain ← Heap.MakeNode[n: (segmentCeiling + 1) * SIZE[SegmentIndex]];
IF VMDefs.GetFileLength[chainHandle].page = 0 THEN
BEGIN
wish: CHARACTER;
DO
TTY.PutString[
LogPrivateDefs.tty,
"Grapevine Heap Initialization: Is this ok? (Y or N): "L];
wish ← TTY.GetChar[LogPrivateDefs.tty];
TTY.PutChar[LogPrivateDefs.tty, wish];
TTY.PutCR[LogPrivateDefs.tty];
SELECT wish FROM
'N, 'n => SIGNAL BailOut[];
'Y, 'y =>
BEGIN
TTY.PutString[
LogPrivateDefs.tty, "Do you know what you're doing? (Y or N): "L];
wish ← TTY.GetChar[LogPrivateDefs.tty];
TTY.PutChar[LogPrivateDefs.tty, wish];
TTY.PutCR[LogPrivateDefs.tty];
SELECT wish FROM
'N, 'n => SIGNAL BailOut[];
'Y, 'y => EXIT; -- go on to initialize
ENDCASE => LOOP;
END;
ENDCASE => LOOP;
ENDLOOP;
InitChain[chainPages];
InitData[];
END
ELSE
BEGIN
initHeap ← FALSE;
IF VMDefs.GetFileLength[chainHandle].page # chainPages * 2 THEN
ERROR BadChainSize;
FOR page: CARDINAL IN [0..chainPages) DO
seg: SegmentIndex = page * VMDefs.pageSize;
chain0: LONG POINTER TO SerialAndHead = LOOPHOLE[VMDefs.ReadPage[
[chainHandle, page * 2], 3]];
chain1: LONG POINTER TO SerialAndHead = LOOPHOLE[VMDefs.ReadPage[
[chainHandle, page * 2 + 1], 3]];
from: LONG POINTER TO SerialAndHead =
IF chain1.serialNumber > chain0.serialNumber THEN chain1 ELSE chain0;
Inline.LongCOPY[
from: from, to: @chain.header[page],
nwords: MIN[1 + segmentCeiling - seg, VMDefs.pageSize]];
VMDefs.Release[LOOPHOLE[chain0]];
VMDefs.Release[LOOPHOLE[chain1]];
ENDLOOP;
END;
IF chain.header[0].chainHead = noSegment THEN ERROR NoWrittenHeapSegment[];
InitFreeMap[chainPages];
END;
InitChain: PROCEDURE [chainPages: CARDINAL] =
BEGIN OPEN HeapFile, client;
initHeap ← TRUE;
LogDefs.WriteLogEntry["Initialising Heap.segments"L];
LogDefs.WriteLine["Initialising Heap.segments"L];
VMDefs.SetFileLength[chainHandle, [chainPages * 2, 0]];
FOR index: CARDINAL IN (0..segmentCeiling] DO
chain.next[index] ← HeapFile.noSegment ENDLOOP;
FOR index: CARDINAL IN [0..chainPages) DO
chain.header[index].serialNumber ← 0;
IF index = 0 -- init head of chain
THEN {
chain.header[index].chainHead ← headerSize;
chain.next[headerSize] ← noSegment};
HeapFileDefs.RecordAllocation[index * VMDefs.pageSize];
-- this incr's serial number for next call
HeapFileDefs.RecordAllocation[index * VMDefs.pageSize];
ENDLOOP;
END;
InitData: PROCEDURE =
BEGIN OPEN HeapFile, client;
LogDefs.WriteLogEntry["Initialising Heap.data"L];
LogDefs.WriteLine["Initialising Heap.data"L];
FOR page: VMDefs.PageNumber DECREASING IN
[0..VMDefs.GetFileLength[handle].page) DO EmptyPage[[handle, page]] ENDLOOP;
VMDefs.WaitFile[handle];
END;
InitFreeMap: PROCEDURE [chainPages: CARDINAL] =
BEGIN OPEN HeapFile, client;
IF freeMap # NIL THEN RETURN;
freeCount ← segmentCount;
freeMap ← BitMapDefs.Create[segmentCeiling + 1];
FOR index: CARDINAL IN [0..chainPages) DO
-- make nonexistent seg indices look 'busy'
firstBit: BitMapDefs.MapIndex = index * VMDefs.pageSize;
FOR j: CARDINAL IN [0..headerSize) DO
BitMapDefs.Set[freeMap, firstBit + j] ENDLOOP;
ENDLOOP;
FOR s: CARDINAL ← chain.header[0].chainHead, chain.next[s] UNTIL s = noSegment
DO BitMapDefs.Set[freeMap, s]; freeCount ← freeCount - 1; ENDLOOP;
HeapFileDefs.NotifyFreeCount[];
END;
CheckForExpansion: PROCEDURE =
BEGIN
DuringExpansion: ERROR = CODE;
IF System.switches['l] = down AND System.switches['i] = up THEN
BEGIN -- enlarge heap (but initialization overrides).
currentHeapSize, newHeapSize, currentChainSize, newChainSize,
newSegmentCount, oldSegmentCeiling, newSegmentCeiling: CARDINAL;
chain: LONG POINTER TO HeapFile.ChainBlock;
heapFile, chainFile: VMDefs.FileHandle;
-- Check that both files exist:
heapFile ← VMDefs.OpenFile[
options: old, name: "Heap.Data"L, cacheFraction: 40];
currentHeapSize ← VMDefs.GetFileLength[heapFile].page;
chainFile ← VMDefs.OpenFile[
options: old, name: "Heap.Segments"L, cacheFraction: 0];
currentChainSize ← (VMDefs.GetFileLength[chainFile].page) / 2;
IF currentHeapSize = 0 OR currentChainSize = 0 THEN ERROR DuringExpansion;
DO
wish: CHARACTER;
TTY.PutString[
LogPrivateDefs.tty,
"Grapevine Heap Expansion (files must already exist): Is this ok? (Y or N): "L];
wish ← TTY.GetChar[LogPrivateDefs.tty];
TTY.PutChar[LogPrivateDefs.tty, wish];
TTY.PutCR[LogPrivateDefs.tty];
SELECT wish FROM
'N, 'n => ERROR DuringExpansion[];
'Y, 'y =>
BEGIN
TTY.PutLine[
LogPrivateDefs.tty,
"Failure during expansion leaves undefined results, heap.data and heap.segments will then have to be deleted"L];
TTY.PutString[LogPrivateDefs.tty, "Heap is now "L];
TTY.PutDecimal[LogPrivateDefs.tty, currentHeapSize];
TTY.PutString[
LogPrivateDefs.tty,
" pages long, how many TOTAL pages do you wish? "L];
newHeapSize ← TTY.GetDecimal[LogPrivateDefs.tty];
TTY.PutCR[LogPrivateDefs.tty];
TTY.PutString[LogPrivateDefs.tty, "Confirm (Y or N): "L];
wish ← TTY.GetChar[LogPrivateDefs.tty];
TTY.PutChar[LogPrivateDefs.tty, wish];
TTY.PutCR[LogPrivateDefs.tty];
IF wish = 'Y OR wish = 'y THEN EXIT -- go on to initialize
ELSE LOOP;
END;
ENDCASE => LOOP;
ENDLOOP;
LogDefs.WriteLogEntry["Doing Heap Expansion"L];
-- init pages of heap file:
IF newHeapSize <= 77777B THEN -- some implementation limit
VMDefs.SetFileLength[heapFile, [page: newHeapSize, byte: 0]]
ELSE ERROR DuringExpansion;
FOR page: VMDefs.PageNumber IN [currentHeapSize..newHeapSize) DO
EmptyPage[[heapFile, page]] ENDLOOP;
VMDefs.WaitFile[heapFile];
-- set up chain file:
newSegmentCount ← newHeapSize / HeapFile.segmentSize;
newChainSize ←
(newSegmentCount + HeapFile.segmentsPerPage - 1) /
HeapFile.segmentsPerPage;
oldSegmentCeiling ←
(currentHeapSize / HeapFile.segmentSize) +
(((currentHeapSize / HeapFile.segmentSize) + HeapFile.segmentsPerPage -
1) / HeapFile.segmentsPerPage) * HeapFile.headerSize - 1;
newSegmentCeiling ←
newSegmentCount + newChainSize * HeapFile.headerSize - 1;
chain ← Heap.MakeNode[n: (newSegmentCeiling + 1) * SIZE[HeapFile.SegmentIndex]];
VMDefs.SetFileLength[chainFile, [page: newChainSize * 2, byte: 0]];
-- get old chain info
FOR page: CARDINAL IN [0..currentChainSize) DO
seg: HeapFile.SegmentIndex = page * VMDefs.pageSize;
chain0: LONG POINTER TO HeapFile.SerialAndHead = LOOPHOLE[VMDefs.ReadPage[
[chainFile, page * 2], 3]];
chain1: LONG POINTER TO HeapFile.SerialAndHead = LOOPHOLE[VMDefs.ReadPage[
[chainFile, page * 2 + 1], 3]];
from: LONG POINTER TO HeapFile.SerialAndHead =
IF chain1.serialNumber > chain0.serialNumber THEN chain1 ELSE chain0;
Inline.LongCOPY[
from: from, to: @chain.header[page],
nwords: MIN[1 + oldSegmentCeiling - seg, VMDefs.pageSize]];
VMDefs.Release[LOOPHOLE[chain0]];
VMDefs.Release[LOOPHOLE[chain1]];
ENDLOOP;
-- add new chain info:
FOR index: CARDINAL IN (oldSegmentCeiling..newSegmentCeiling] DO
chain.next[index] ← HeapFile.noSegment ENDLOOP;
FOR page: CARDINAL IN [currentChainSize..newChainSize) DO
chainFilePage: VMDefs.Page ← VMDefs.UsePage[[chainFile, page * 2]];
chain.header[page].serialNumber ← 0;
Inline.LongCOPY[
from: @chain.header[page], to: chainFilePage,
nwords: MIN[
1 + newSegmentCeiling - page * VMDefs.pageSize, VMDefs.pageSize]];
VMDefs.MarkStartWait[chainFilePage];
VMDefs.Release[chainFilePage];
chain.header[page].serialNumber ← 1;
chainFilePage ← VMDefs.UsePage[[chainFile, page * 2 + 1]];
Inline.LongCOPY[
from: @chain.header[page], to: chainFilePage,
nwords: MIN[
1 + newSegmentCeiling - page * VMDefs.pageSize, VMDefs.pageSize]];
VMDefs.MarkStartWait[chainFilePage];
VMDefs.Release[chainFilePage];
ENDLOOP;
Heap.systemZone.FREE[@chain];
VMDefs.CloseFile[heapFile];
VMDefs.CloseFile[chainFile];
END;
END;
EmptyPage: PROCEDURE [where: VMDefs.PageAddress] =
BEGIN OPEN VMDefs, HeapXDefs;
page: Page = ReadPage[where, 6]; -- faster than UsePage --
header: LONG POINTER TO PageHeader = LOOPHOLE[page, LONG POINTER] + FIRST[PageIndex];
obj: LONG POINTER TO ObjectHeader =
LOOPHOLE[page, LONG POINTER] + FIRST[PageIndex] + SIZE[PageHeader];
header.offset ← HeapDefs.objectStart;
obj.number ← ObjectDirXDefs.gapObjectNumber;
obj.size ←
1 + LAST[PageIndex] -
(FIRST[PageIndex] + SIZE[PageHeader] + SIZE[ObjectHeader]);
MarkStart[page];
Release[page];
END;
InitObjectDir: ENTRY PROCEDURE =
BEGIN -- after InitHeapFile, to get HeapFileDefs.handle --
OPEN ObjectDir;
handle ← VMDefs.OpenFile[
options: oldOrNew, name: "Heap.ObjectDir", cacheFraction: 10];
ObjectDir.heapHandle ← HeapFile.client.handle;
nextVirginPage ← VMDefs.GetFileLength[handle].page;
IF nextVirginPage = FIRST[VMDefs.PageNumber] THEN
nextVirginPage ← nextVirginPage + 1;
-- initialize each page with unchained "free" indexes,
-- and put each page on free page chain.
firstFreePage ← endOfChain;
FOR reqd: VMDefs.PageNumber IN [0..nextVirginPage) DO
IF dpPage # NIL THEN VMDefs.Release[LOOPHOLE[dpPage, VMDefs.Page]];
dpPage ← LOOPHOLE[VMDefs.UsePage[[handle, dpNumber ← reqd]]];
FOR index: DirIndex IN DirIndex DO
dpPage.data[index] ← [free[next: 0, dopc: 0]] ENDLOOP;
dpPage.header ← [nextFreePage: unchained, nextFreeIndex: noFreeIndex];
VMDefs.MarkStart[LOOPHOLE[dpPage, VMDefs.Page]]; --may cause file extension for page 0--
releaseData[dirty];
ENDLOOP;
END;
ReadSegments: PROCEDURE =
BEGIN OPEN HeapFile, client;
page: VMDefs.Page;
usedSegment: SegmentIndex ← lastChained ← chain.header[0].chainHead;
pos: VMDefs.FullAddress ← HeapFileDefs.Address[lastChained];
usedPos: VMDefs.FullAddress ← pos;
offset: HeapDefs.ObjectOffset;
current: ObjectDirXDefs.ObjectNumber ← ObjectDirXDefs.gapObjectNumber;
objectsFound: LONG CARDINAL ← 0;
DefineObject: ENTRY PROCEDURE [obj: ObjectDirXDefs.ObjectNumber] =
BEGIN OPEN ObjectDir;
data: LONG POINTER TO DirData = findData[obj];
data↑ ← [
used[
page: pos.page.page, word: pos.word, type: obj.type, count: zeroCount]];
releaseData[dirty];
END;
DefineObject[ObjectDirXDefs.gapObjectNumber];
DO -- Consider any page header --
IF pos.word = FIRST[VMDefs.PageIndex] THEN
BEGIN
pageHead: LONG POINTER TO HeapXDefs.PageHeader;
page ← VMDefs.ReadPage[pos.page, 2];
pageHead ← LOOPHOLE[page, LONG POINTER] + pos.word;
pos.word ← pos.word + SIZE[HeapXDefs.PageHeader];
offset ← pageHead.offset;
END
ELSE offset ← HeapDefs.objectStart;
BEGIN
object: LONG POINTER TO HeapXDefs.ObjectHeader =
LOOPHOLE[page, LONG POINTER] + pos.word;
-- check for non-empty segment --
IF object.number # ObjectDirXDefs.gapObjectNumber THEN
BEGIN usedPos ← pos; usedSegment ← lastChained; END;
IF
(current # ObjectDirXDefs.gapObjectNumber
--Inside an object, looking for continuation sub-object--
-- If a duplicate start is found for an object, then the
-- later start is chosen, so that all earlier starts are
-- overwritten before the object number is freed by the
-- compactor. Otherwise, confusion could arise by the
-- object number being re-used and a restart occuring
-- before the duplicate start has been overwritten.
AND object.number = current AND offset = HeapDefs.objectStart)
OR
(object.number # ObjectDirXDefs.gapObjectNumber
AND offset = HeapDefs.objectStart) THEN
BEGIN -- start of a new object --
DefineObject[object.number];
objectsFound ← objectsFound + 1;
current ← object.number;
END
-- ELSE we have one of:
-- continuation of ignorable object,
-- imbedded object which should be ignored,
-- unexpected partial object,
-- gap object -- ;
pos.word ← pos.word + SIZE[HeapXDefs.ObjectHeader];
pos.word ← pos.word + object.size;
END;
IF pos.word + SIZE[HeapXDefs.ObjectHeader] > LAST[VMDefs.PageIndex] THEN
BEGIN
VMDefs.Release[page];
-- similar to HeapFileDefs.NextPage, but different --
IF pos.page.page MOD segmentSize < segmentSize - 1 THEN
BEGIN
pos.page.page ← pos.page.page + 1;
pos.word ← FIRST[VMDefs.PageIndex];
END
ELSE
IF chain.next[lastChained] # noSegment THEN
BEGIN
old: SegmentIndex = lastChained;
pos ← HeapFileDefs.Address[lastChained ← chain.next[old]];
IF usedSegment # old THEN
BEGIN -- 'old' is entirely empty --
chain.next[usedSegment] ← lastChained;
HeapFileDefs.AddToFreeList[old];
END
END
ELSE EXIT -- note "pos" is last page in chain.written-- ;
END
ELSE -- end of any current object --
current ← ObjectDirXDefs.gapObjectNumber;
ENDLOOP;
lastPage ← pos.page;
lastWritten ← lastChained;
LogDefs.ShowNumber["Found "L, objectsFound, " objects in the heap."L];
END;
TidyObjectDir: ENTRY PROCEDURE =
-- Construct object directory page free chains --
-- Add pages with non-empty free chains to page free chain
BEGIN OPEN ObjectDir;
FOR p: ObjDirPageNumber DECREASING IN [0..nextVirginPage) DO
getDirPage[p];
dpPage.header.nextFreeIndex ← noFreeIndex;
FOR index: DirIndex IN DirIndex DO
WITH data: dpPage.data[index] SELECT FROM
free =>
BEGIN
dpPage.data[index] ← [
free[next: dpPage.header.nextFreeIndex, dopc: lastDofpc]];
dpPage.header.nextFreeIndex ← index;
END;
ENDCASE => NULL;
ENDLOOP;
IF dpPage.header.nextFreeIndex # noFreeIndex THEN {
dpPage.header.nextFreePage ← firstFreePage; firstFreePage ← dpNumber};
releaseData[dirty];
ENDLOOP;
END;
Go: PROCEDURE =
BEGIN
HeapFile.client ← [freeMap: NIL];
LogDefs.WriteLine["Restarting heap storage"L];
START HeapFile;
CheckForExpansion[];
ReadChain[];
START ObjectDir;
InitObjectDir[];
ReadSegments[];
TidyObjectDir[];
END;
Go[];
END.
log:
21-Aug-84 11:38:03 - blh: add expand code.