-- Transport Mechanism Filestore - restart --
-- REMOVE LOOPHOLES(COMMENTS) AROUND NSString/NSCommand CALLS
-- [Idun]<WServices>MS>StableStorageRestart.mesa
-- Wobber 9-Aug-82 15:07:04 --
-- BLyon 30-Oct-81 17:21:47 --
-- Randy Gobbel 2-Aug-82 18:42:09 --
-- Andrew Birrell 3-Jun-81 18:22:21 --
DIRECTORY
Alloc USING [shortTerm, longTerm],
Bitmap USING [Create, MapIndex, Set],
HeapString USING [AppendString, Free, Pickle],
Inline USING [LowHalf],
NSString USING [nullString, String, StringFromMesaString],
Object USING [Offset, objectStart],
ObjectDir --EXPORT only-- ,
ObjectDirImpl --LOCK--, -- Beware of re-compilation constraints --
ObjectDirInternal,
ObjectInternal USING [PageHeader, ObjectHeader],
Policy USING [AmountOfFreeHeap],
ServerLog USING [Handle, WriteLogEntry],
StableStorageImpl,
VMDefs USING [AllocateBlock, FileHandle, FullAddress, GetFileLength, MapPage,
MarkStart, MarkStartWait, OpenFile, Page, PageAddress, PageIndex, PageNumber,
pageSize, ReadPage, Release, RemapPage, SetFileLength, UnmapPage, UsePage,
WaitFile];
StableStorageRestart: MONITOR LOCKS ObjectDirImpl.LOCK
--for RestartObject--
IMPORTS Alloc, Bitmap, HeapString, Inline, NSString, Policy,
ObjectDirImpl, ServerLog, StableStorageImpl, VMDefs
EXPORTS ObjectDir --RestartObject, Client-- , Object --HeapRestart--
SHARES ObjectDirImpl, StableStorageImpl =
BEGIN
lz: UNCOUNTED ZONE = Alloc.longTerm;
-- ??? msID: Clearinghouse.TypeID = ClearinghouseDefinitions.fileserver;
Client: TYPE = ObjectDirInternal.Client;
ClientObject: PUBLIC TYPE = ObjectDirInternal.ClientObject;
S: PROCEDURE [s: LONG STRING] RETURNS [NSString.String] =
{RETURN[NSString.StringFromMesaString[s]]};
-- Opens files concerned with heap:
-- "<client>.objectDir" The object directory; initialised on every restart
-- and extended dynamically as needed during run.
-- "<client>.data" The file containing the heap objects is considered
-- as a sequence of separate segments, numbered
-- [FIRST[ObjectDirInternal.Segment]..LAST[ObjectDirInternal.Segment]]
-- These segments are of equal size. the file should
-- be pre-allocated on consecutive pages of physical
-- disk.
-- "<client>.Chain" Ordering for segments of the data file; two pages only.
-- 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 the chain file, 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.
IllegalObject: SIGNAL = CODE;
RestartObject: PUBLIC ENTRY PROCEDURE
[obj: ObjectDirInternal.ObjectNumber, client: Client] =
BEGIN OPEN ObjectDirImpl, ObjectDirInternal, client;
-- executes inside the ObjectDirImpl monitor --
either: LONG POINTER TO DirData = FindData[obj, client];
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, client];
END;
BadChainSize: ERROR = CODE;
NoWrittenHeapSegment: ERROR = CODE;
TooManySegments: ERROR = CODE;
InitialisingHeap: PUBLIC SIGNAL = CODE; -- resume if you really mean it!--
InitChain: PROCEDURE [client: Client] =
BEGIN OPEN client, ObjectDirInternal, StableStorageImpl;
bodyChainTitle: NSString.String ← NSString.nullString;
chainSize: CARDINAL = (segmentCount+segmentsPerPage-1) / segmentsPerPage;
index: CARDINAL;
segmentCeiling ← segmentCount + chainSize*headerSize - 1;
bodyChainTitle ← HeapString.AppendString[bodyChainTitle, name];
bodyChainTitle ← HeapString.AppendString[bodyChainTitle, S[".chain"L]];
chainHandle ← VMDefs.OpenFile[
options: oldOrNew, name: bodyChainTitle, cacheFraction: 0];
VMDefs.SetFileLength[chainHandle, [chainSize*2, 0]];
chain ← LOOPHOLE[VMDefs.AllocateBlock[chainSize]];
FOR index IN [0..chainSize) DO
VMDefs.RemapPage[
LOOPHOLE[chain + index*VMDefs.pageSize],
[chainHandle, index*2]];
ENDLOOP;
FOR index IN [0..segmentCeiling] DO
chain.next[index] ← noSegment ENDLOOP;
FOR index IN [0..chainSize) DO
chain.header[index].serialNumber ← 0;
IF index = 0 THEN chain.header[index].chainHead ← headerSize;
VMDefs.MarkStartWait[LOOPHOLE[chain+index*VMDefs.pageSize]];
ENDLOOP;
FOR index IN [0..chainSize) DO
VMDefs.RemapPage[
LOOPHOLE[chain + index*VMDefs.pageSize],
[chainHandle, index*2+1]];
chain.header[index].serialNumber ← 1;
IF index = 0 THEN chain.header[index].chainHead ← headerSize;
VMDefs.MarkStartWait[LOOPHOLE[chain+index*VMDefs.pageSize]];
ENDLOOP;
IF chain.header[0].chainHead = noSegment THEN ERROR NoWrittenHeapSegment[];
InitFreeMap[client, chainSize];
HeapString.Free[@bodyChainTitle];
END;
ReadChain: PROCEDURE [client: Client] =
BEGIN OPEN client, ObjectDirInternal, StableStorageImpl;
bodyChainTitle: NSString.String ← NSString.nullString;
chain0, chain1: LONG POINTER TO SerialAndHead;
chainSize: CARDINAL = (segmentCount+segmentsPerPage-1) / segmentsPerPage;
segmentCeiling ← segmentCount + chainSize*headerSize - 1;
bodyChainTitle ← HeapString.AppendString[bodyChainTitle, name];
bodyChainTitle ← HeapString.AppendString[bodyChainTitle, S[".chain"L]];
chainHandle ← VMDefs.OpenFile[
options: old, name: bodyChainTitle, cacheFraction: 0];
IF VMDefs.GetFileLength[chainHandle].page # chainSize*2 THEN ERROR BadChainSize;
chain ← LOOPHOLE[VMDefs.AllocateBlock[chainSize]];
FOR index: CARDINAL IN [0..chainSize) DO
chainAddr: VMDefs.PageAddress;
chain0 ← LOOPHOLE[VMDefs.ReadPage[[chainHandle, index*2], 3]];
chain1 ← LOOPHOLE[VMDefs.ReadPage[[chainHandle, index*2+1], 3]];
IF chain1.serialNumber > chain0.serialNumber THEN
chainAddr ← [chainHandle, index*2+1]
ELSE chainAddr ← [chainHandle, index*2];
VMDefs.UnmapPage[LOOPHOLE[chain0]]; VMDefs.UnmapPage[LOOPHOLE[chain1]];
VMDefs.MapPage[LOOPHOLE[chain + index*VMDefs.pageSize], chainAddr];
ENDLOOP;
IF chain.header[0].chainHead = noSegment THEN ERROR NoWrittenHeapSegment[];
InitFreeMap[client, chainSize];
HeapString.Free[@bodyChainTitle];
END;
InitFreeMap: PROCEDURE [client: Client, chainSize: CARDINAL] =
BEGIN OPEN client, ObjectDirInternal;
freeCount ← segmentCount;
freeMap ← Bitmap.Create[segmentCeiling+1];
FOR index: CARDINAL IN [0..chainSize) DO
-- make nonexistent seg indices look 'busy'
firstBit: Bitmap.MapIndex = index * VMDefs.pageSize;
FOR j: CARDINAL IN [0..headerSize) DO
Bitmap.Set[freeMap, firstBit+j] ENDLOOP;
ENDLOOP;
FOR s: CARDINAL ← chain.header[0].chainHead, chain.next[s]
UNTIL s = noSegment DO
Bitmap.Set[freeMap, s]; freeCount ← freeCount - 1;
ENDLOOP;
Policy.AmountOfFreeHeap[(freeCount*100 + segmentCount/2)/segmentCount];
END;
HeapDataTooSmall: SIGNAL = CODE;
EmptyPage: PROCEDURE [where: VMDefs.PageAddress] =
BEGIN OPEN VMDefs, ObjectInternal;
page: Page = UsePage[where];
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 ← Object.objectStart;
obj.number ← ObjectDirInternal.gapObjectNumber;
obj.size ←
1 + LAST[PageIndex] -
(FIRST[PageIndex] + SIZE[PageHeader] + SIZE[ObjectHeader]);
MarkStart[page];
Release[page];
END;
InitStableStorage: PROCEDURE [client: Client, fileSize: LONG CARDINAL] =
BEGIN OPEN client, ObjectDirInternal, StableStorageImpl;
dataName: NSString.String ← NSString.nullString;
page: VMDefs.PageNumber;
dataName ← HeapString.AppendString[dataName, name];
dataName ← HeapString.AppendString[dataName, S[".data"L]];
handle ← VMDefs.OpenFile[options: oldOrNew, name: dataName, cacheFraction: 20];
VMDefs.SetFileLength[handle, [page: fileSize, byte: 0]];
segmentCount ← Inline.LowHalf[fileSize / segmentSize];
FOR page IN [0..fileSize) DO EmptyPage[[handle, page]] ENDLOOP;
VMDefs.WaitFile[handle];
-- firstFree not yet initialised --
-- firstWritten not yet initialised --
HeapString.Free[@dataName];
END;
RestartStableStorage: PROCEDURE [client: Client] =
BEGIN OPEN client, ObjectDirInternal, StableStorageImpl;
dataName: NSString.String ← NSString.nullString;
dataName ← HeapString.AppendString[dataName, name];
dataName ← HeapString.AppendString[dataName, S[".data"L]];
handle ← VMDefs.OpenFile[options: old, name: dataName, cacheFraction: 20];
segmentCount ← Inline.LowHalf[VMDefs.GetFileLength[handle].page / segmentSize];
-- firstFree not yet initialised --
-- firstWritten not yet initialised --
HeapString.Free[@dataName];
END;
InitObjectDir: ENTRY PROCEDURE [client: Client] =
BEGIN
OPEN client, ObjectDirImpl, ObjectDirInternal;
fileName: NSString.String ← NSString.nullString;
fileName ← HeapString.AppendString[fileName, name];
fileName ← HeapString.AppendString[fileName, S[".objectDir"L]];
dirHandle ← VMDefs.OpenFile[
options: oldOrNew, name: fileName, cacheFraction: 5];
nextVirginPage ← Inline.LowHalf[VMDefs.GetFileLength[dirHandle].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: CARDINAL IN [0..nextVirginPage) DO
IF dpPage # NIL THEN VMDefs.Release[LOOPHOLE[dpPage, VMDefs.Page]];
dpPage ← LOOPHOLE[VMDefs.UsePage[[dirHandle, 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, client];
ENDLOOP;
HeapString.Free[@fileName];
END;
ReadSegments: PROCEDURE [client: Client] =
BEGIN OPEN client, StableStorageImpl, ObjectDirInternal;
page: VMDefs.Page;
usedSegment: SegmentIndex ← lastChained ← chain.header[0].chainHead;
pos: VMDefs.FullAddress ← Address[lastChained, client];
usedPos: VMDefs.FullAddress ← pos;
offset: Object.Offset;
current: ObjectDirInternal.ObjectNumber ← ObjectDirInternal.gapObjectNumber;
DefineObject: ENTRY PROCEDURE
[obj: ObjectDirInternal.ObjectNumber, client: Client] =
BEGIN OPEN client, ObjectDirImpl;
data: LONG POINTER TO DirData = FindData[obj, client];
data↑ ← [
used[
page: pos.page.page, word: pos.word, type: obj.type, count: zeroCount]];
ReleaseData[dirty, client];
END;
DefineObject[ObjectDirInternal.gapObjectNumber, client];
DO -- Consider any page header --
IF pos.word = FIRST[VMDefs.PageIndex] THEN
BEGIN
pageHead: LONG POINTER TO ObjectInternal.PageHeader;
page ← VMDefs.ReadPage[pos.page, 2];
pageHead ← LOOPHOLE[page, LONG POINTER] + pos.word;
pos.word ← pos.word + SIZE[ObjectInternal.PageHeader];
offset ← pageHead.offset;
END
ELSE offset ← Object.objectStart;
BEGIN
object: LONG POINTER TO ObjectInternal.ObjectHeader =
LOOPHOLE[page, LONG POINTER] + pos.word;
-- check for non-empty segment --
IF object.number # ObjectDirInternal.gapObjectNumber THEN
BEGIN usedPos ← pos; usedSegment ← lastChained; END;
IF
(current # ObjectDirInternal.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 = Object.objectStart)
OR
(object.number # ObjectDirInternal.gapObjectNumber
AND offset = Object.objectStart) THEN
BEGIN -- start of a new object --
DefineObject[object.number, client];
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[ObjectInternal.ObjectHeader];
pos.word ← pos.word + object.size;
END;
IF pos.word + SIZE[ObjectInternal.ObjectHeader] > LAST[VMDefs.PageIndex]
THEN
BEGIN
VMDefs.Release[page];
-- similar to StableStorage.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 ← Address[lastChained ← chain.next[old], client];
IF usedSegment # old THEN
BEGIN -- 'old' is entirely empty
chain.next[usedSegment] ← lastChained;
AddToFreeList[old, client];
END
END
ELSE EXIT -- note "pos" is last page in written chain ;
END
ELSE -- end of any current object
current ← ObjectDirInternal.gapObjectNumber;
ENDLOOP;
lastPage ← pos.page;
lastWritten ← lastChained;
END;
TidyObjectDir: ENTRY PROCEDURE [client: Client] =
-- Construct object directory page free chains --
-- Add pages with non-empty free chains to page free chain
BEGIN OPEN client, ObjectDirImpl, ObjectDirInternal;
FOR p: ObjDirPageNumber DECREASING IN [0..nextVirginPage) DO
GetDirPage[p, client];
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, client];
ENDLOOP;
END;
InitClient: PUBLIC PROCEDURE
[clientName: NSString.String, fileSize: LONG CARDINAL, log: ServerLog.Handle,
zone: UNCOUNTED ZONE]
RETURNS [client: Client] =
BEGIN
s: NSString.String ← NSString.nullString;
client ← lz.NEW[ObjectDirInternal.ClientObject];
client.log ← log;
client.name ← NSString.nullString;
client.zone ← zone;
client.name ← HeapString.AppendString[client.name, clientName];
client.name ← HeapString.Pickle[client.name];
s ← HeapString.AppendString[s, S["Initializing Object Store for client "L]];
s ← HeapString.AppendString[s, clientName];
ServerLog.WriteLogEntry[s, client.log];
-- ??? NSCommand.PutAsyncMessage[s, msID];
HeapString.Free[@s];
InitStableStorage[client, fileSize];
InitChain[client];
InitObjectDir[client];
ReadSegments[client];
TidyObjectDir[client];
END;
RestartClient: PUBLIC PROCEDURE
[clientName: NSString.String, log: ServerLog.Handle, zone: UNCOUNTED ZONE]
RETURNS [client: Client] =
BEGIN
s: NSString.String ← NSString.nullString;
client ← lz.NEW[ObjectDirInternal.ClientObject];
client.log ← log;
client.name ← NSString.nullString;
client.zone ← zone;
client.name ← HeapString.AppendString[client.name, clientName];
client.name ← HeapString.Pickle[client.name];
s ← HeapString.AppendString[s, S["Restarting Object Store for client "L]];
s ← HeapString.AppendString[s, clientName];
-- ??? NSCommand.PutAsyncMessage[s, msID];
HeapString.Free[@s];
RestartStableStorage[client];
ReadChain[client];
InitObjectDir[client];
ReadSegments[client];
TidyObjectDir[client];
END;
END.