-- 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.