-- Copyright (C) 1981, 1984, 1985 by Xerox Corporation. All rights reserved.
-- Compactor.mesa, Transport Mechanism Filestore - heap compactor
-- HGM, 15-Sep-85 10:20:25
-- Randy Gobbel 19-May-81 13:24:41 --
-- Andrew Birrell 3-Jun-81 13:00:58 --
DIRECTORY
Heap USING [systemZone],
HeapDefs USING [Buffer, HeapReadData, objectStart, ReaderHandle],
HeapFileDefs USING [
FirstSegment, InsertFirstSegment, NextPage, NoMorePages, FreeSegment],
HeapXDefs USING [
PageHeader, ObjectHeader, ReaderData, StopAllReaders, RestartAllReaders],
LogDefs USING [ShowLine],
ObjectDirXDefs USING [
DOPC, ObjectNumber, gapObjectNumber, GetObjectState, MoveObject, ReportDofpc],
PolicyDefs USING [CompactorPause, CompactorStart],
Process USING [DisableTimeout, InitializeCondition, InitializeMonitor],
String USING [AppendLongDecimal, AppendString],
System USING [GetGreenwichMeanTime, GreenwichMeanTime],
VMDefs USING [
Deactivate, Page, PageIndex, ReadPage, MarkStartWait, UsePage, PageNumber,
PageAddress, FullAddress];
Compactor: PROGRAM
IMPORTS
Heap, HeapDefs, HeapFileDefs, HeapXDefs, LogDefs, ObjectDirXDefs, PolicyDefs, Process,
String, System, VMDefs
EXPORTS HeapDefs --Compactor-- =
BEGIN OPEN HeapXDefs, ObjectDirXDefs;
-- Current state --
wPos: VMDefs.FullAddress;
wPage: VMDefs.Page;
flushedBefore: VMDefs.FullAddress;
reader: HeapXDefs.ReaderData;
readerHandle: HeapDefs.ReaderHandle = LOOPHOLE[LONG[@reader]];
object: LONG POINTER TO ObjectHeader;
dorpc: ObjectDirXDefs.DOPC ← 0; --see comments in ObjectDirXDefs
-- record of objects moved by compactor --
ObjRec: TYPE = RECORD [
obj: ObjectNumber, where: VMDefs.FullAddress, next: ObjPtr];
ObjPtr: TYPE = LONG POINTER TO ObjRec;
endObj: ObjPtr = NIL;
freeChain: ObjPtr ← endObj;
moveChain: ObjPtr ← endObj;
freeCount: CARDINAL ← 0;
-- Statistics
freed, copied, firstGap: LONG CARDINAL;
startTime: System.GreenwichMeanTime;
waitSeconds, pauseSeconds: LONG CARDINAL;
GetObjRec: PROCEDURE [chain: LONG POINTER TO ObjPtr] RETURNS [ptr: ObjPtr] = INLINE
BEGIN
IF freeChain = endObj THEN ptr ← Heap.systemZone.NEW[ObjRec]
ELSE {ptr ← freeChain; freeChain ← freeChain.next; freeCount ← freeCount - 1};
ptr.next ← chain↑;
chain↑ ← ptr;
END;
FreeObjRec: PROC [ptr: ObjPtr] = INLINE
BEGIN ptr.next ← freeChain; freeChain ← ptr; freeCount ← freeCount + 1; END;
Start: PROCEDURE =
BEGIN -- called before each compaction --
LogDefs.ShowLine["Compactor starting..."];
waitSeconds ← (System.GetGreenwichMeanTime[]-startTime);
startTime ← System.GetGreenwichMeanTime[];
freed ← copied ← 0;
firstGap ← LAST[LONG CARDINAL];
pauseSeconds ← 0;
reader.where ← HeapFileDefs.FirstSegment[];
reader.page ← VMDefs.ReadPage[reader.where.page, 0];
dorpc ← dorpc + 1;
reader.offset ← HeapDefs.objectStart;
reader.object ← ObjectDirXDefs.gapObjectNumber;
wPos ← HeapFileDefs.InsertFirstSegment[];
wPage ← NIL -- set later by SetHeader -- ;
flushedBefore ← wPos;
Flush[];
END;
Finish: PROCEDURE =
BEGIN
log: LONG STRING ← Heap.systemZone.NEW[StringBody[300]];
runTime: LONG CARDINAL ← (System.GetGreenwichMeanTime[]-startTime);
startTime ← System.GetGreenwichMeanTime[];
String.AppendString[log, "Compactor: Waited "L];
String.AppendLongDecimal[log, waitSeconds];
String.AppendString[log, ", ran "L];
String.AppendLongDecimal[log, runTime];
String.AppendString[log, ", paused "L];
String.AppendLongDecimal[log, pauseSeconds];
String.AppendString[log, " seconds. Reclaimed "L];
String.AppendLongDecimal[log, freed];
String.AppendString[log, " segments. Copied "L];
String.AppendLongDecimal[log, copied];
String.AppendString[log, " pages. First gap was at page "L];
String.AppendLongDecimal[log, firstGap];
String.AppendString[log, "."L];
LogDefs.ShowLine[log];
Heap.systemZone.FREE[@log];
END;
FindObject: PROCEDURE =
BEGIN -- skips until non-ignorable object --
-- If no more objects exist, HeapFileDefs.NextPage generates a signal. --
-- This should be caught by the caller of FindObject --
OPEN VMDefs;
reader.object ← gapObjectNumber --indicates "no current object"-- ;
reader.end ← FALSE;
DO -- Consider any page header --
IF reader.where.word = FIRST[PageIndex] THEN
BEGIN
pageHead: LONG POINTER TO PageHeader =
LOOPHOLE[reader.page, LONG POINTER] + reader.where.word;
reader.where.word ← reader.where.word + SIZE[PageHeader];
reader.offset ← pageHead.offset;
END
ELSE reader.offset ← HeapDefs.objectStart;
BEGIN -- read sub-object header --
object: LONG POINTER TO ObjectHeader =
LOOPHOLE[reader.page, LONG POINTER] + reader.where.word;
IF
(reader.object # gapObjectNumber
-- Inside an object, looking for continuation sub-object --
-- If a duplicate start is found, it may be non-ignorable --
AND object.number = reader.object
AND reader.offset = HeapDefs.objectStart)
OR
(object.number # gapObjectNumber
AND reader.offset = HeapDefs.objectStart) THEN
BEGIN -- start of a new object --
SELECT GetObjectState[object.number, reader.where, dorpc] FROM
inUse =>
BEGIN
ptr: ObjPtr = GetObjRec[@moveChain];
ptr.where ← wPos;
ptr.obj ← reader.object ← object.number;
EXIT
END;
unused => -- ignorable object, marked as deleted by ObjectDir --
NULL;
duplicate => NULL; -- ignorable object --
ENDCASE => ERROR;
reader.object ← object.number --now the current object-- ;
END
-- ELSE we have one of:
-- continuation of ignorable object,
-- imbedded object which should be ignored,
-- unexpected partial object,
-- gap object -- ;
reader.where.word ← reader.where.word + SIZE[ObjectHeader];
reader.where.word ← reader.where.word + object.size;
END;
-- check for end of page --
IF reader.where.word + SIZE[ObjectHeader] > LAST[PageIndex] THEN
BEGIN
start: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
Deactivate[reader.page]; -- not "Release", for better cache -
PolicyDefs.CompactorPause[];
pauseSeconds ← pauseSeconds + (System.GetGreenwichMeanTime[]-start);
reader.where ← HeapFileDefs.NextPage[reader.where];
-- That may have generated a signal, terminating FindObject --
-- Note that there is no current page here. --
reader.page ← ReadPage[reader.where.page, 0];
dorpc ← dorpc + 1;
END
ELSE -- end of any current object --
reader.object ← gapObjectNumber;
ENDLOOP;
--the interlock with the readers occurs in TerminatePage --
END;
Flush: PROCEDURE =
BEGIN --write empty pages upto, but excluding, 'limit' --
WHILE flushedBefore.page.page # reader.where.page.page DO
EmptyPage[flushedBefore.page];
flushedBefore ← HeapFileDefs.NextPage[flushedBefore]
ENDLOOP;
ObjectDirXDefs.ReportDofpc[dorpc - 1];
END;
EmptyPage: PROCEDURE [where: VMDefs.PageAddress] =
BEGIN OPEN VMDefs;
page: Page = UsePage[where];
header: LONG POINTER TO PageHeader = LOOPHOLE[page];
obj: LONG POINTER TO ObjectHeader = LOOPHOLE[page, LONG POINTER] + SIZE[PageHeader];
header.offset ← HeapDefs.objectStart;
obj.number ← ObjectDirXDefs.gapObjectNumber;
obj.size ← 1 + LAST[PageIndex] - (SIZE[PageHeader] + SIZE[ObjectHeader]);
MarkStartWait[page];
Deactivate[page];
END;
SetHeader: PROCEDURE [number: ObjectDirXDefs.ObjectNumber] =
BEGIN
-- Write page header, if needed --
IF wPos.word = FIRST[VMDefs.PageIndex] THEN
BEGIN
wPage ← VMDefs.UsePage[wPos.page];
BEGIN
header: LONG POINTER TO PageHeader = LOOPHOLE[wPage, LONG POINTER] + wPos.word;
header.offset ← reader.offset;
wPos.word ← wPos.word + SIZE[PageHeader];
END;
END;
-- Write object or sub-object header --
object ← LOOPHOLE[wPage, LONG POINTER] + wPos.word;
wPos.word ← wPos.word + SIZE[ObjectHeader];
object.number ← number;
object.size ← 0;
END;
TerminatePage: PROCEDURE [current: ObjectDirXDefs.ObjectNumber] =
BEGIN --write wPage to disk and update object directory and free files--
VMDefs.MarkStartWait[wPage];
copied ← copied + 1;
VMDefs.Deactivate[wPage];
-- altering object directory is now ok, even although old copies of --
-- the objects in wPage still exist on other pages --
WHILE moveChain # endObj DO
BEGIN
ptr: ObjPtr = moveChain;
moveChain ← moveChain.next;
ObjectDirXDefs.MoveObject[ptr.obj, ptr.where];
--ensure the readers are not confused --
HeapXDefs.RestartAllReaders[ptr.obj];
FreeObjRec[ptr];
END;
ENDLOOP;
PolicyDefs.CompactorPause[];
HeapXDefs.StopAllReaders[stoppedObj ← current];
Flush[];
wPos ← HeapFileDefs.NextPage[wPos];
freed ← freed + HeapFileDefs.FreeSegment[wPos, reader.where];
-- leaves readers for "current" stopped --
IF freed # 0 THEN firstGap ← MIN[firstGap, copied-1];
END;
stoppedObj: ObjectDirXDefs.ObjectNumber ← ObjectDirXDefs.gapObjectNumber;
RestartStoppedObj: PROCEDURE =
BEGIN
IF stoppedObj # ObjectDirXDefs.gapObjectNumber THEN
HeapXDefs.RestartAllReaders[stoppedObj];
stoppedObj ← ObjectDirXDefs.gapObjectNumber;
END;
BufferNotFilled: ERROR = CODE;
ObjectNotDestroyed: ERROR = CODE;
RunCompactor: PROCEDURE =
BEGIN
startTime ← System.GetGreenwichMeanTime[];
-- Never returns --
DO
PolicyDefs.CompactorStart[];
Start[];
DO
FindObject[ ! HeapFileDefs.NoMorePages => EXIT];
BEGIN
objectEnded: BOOLEAN ← FALSE;
objectStartPage: VMDefs.PageNumber = reader.where.page.page;
SetHeader[reader.object];
UNTIL objectEnded DO -- copy data into wPage --
BEGIN
buffer: HeapDefs.Buffer;
buffer ← [wPage + wPos.word, 1 + LAST[VMDefs.PageIndex] - wPos.word];
[objectEnded, object.size] ← HeapDefs.HeapReadData[
readerHandle, buffer];
-- always 'objectEnded' and/or object.size=buffer.length --
wPos.word ← wPos.word + object.size;
END;
-- move to new page, if needed, even if objectEnded --
IF wPos.word + SIZE[ObjectHeader] > LAST[VMDefs.PageIndex] THEN
BEGIN
RestartStoppedObj[];
TerminatePage[reader.object];
SetHeader[reader.object];
END
ELSE IF NOT objectEnded THEN ERROR BufferNotFilled[];
ENDLOOP --for each object-- ;
RestartStoppedObj[];
IF reader.where.page.page # objectStartPage THEN -- HeapReadData moved us beyond last deleted object page --
dorpc ← dorpc + 1;
END;
ENDLOOP --end of last object-- ;
--pad last page with gap, and write to disk--
SetHeader[ObjectDirXDefs.gapObjectNumber];
object.size ← 1 + LAST[VMDefs.PageIndex] - wPos.word;
TerminatePage[ObjectDirXDefs.gapObjectNumber];
RestartStoppedObj[];
EmptyPage[reader.where.page];
ObjectDirXDefs.ReportDofpc[dorpc]; -- everything has been flushed --
Finish[];
ENDLOOP -- eternal loop of compactions -- ;
END --Compact-- ;
-- Main Program --
Process.InitializeMonitor[@reader.LOCK];
Process.InitializeCondition[@reader.canStart, 0];
Process.DisableTimeout[@reader.canStart];
reader.stopped ← FALSE;
RunCompactor[];
END.