-- Transport Mechanism Filestore - object directory -- -- [Juniper]<Grapevine>MS>ObjectDir.mesa -- Andrew Birrell 10-Jun-81 10:46:42 -- -- M. D. Schroeder 7-Feb-83 15:34:06 -- DIRECTORY HeapFileDefs USING[ ObjectAbandoned ], ObjectDirXDefs USING[ DOPC, ObjectNumber, ObjectState ], ObjectDirDefs USING[ noObject, ObjectType ], PolicyDefs USING[ GapExists ], VMDefs USING[ Page, pageSize, PageNumber, FileHandle, PageIndex, ReadPage, UsePage, Mark, MarkStart, Release, FullAddress ]; ObjectDir: MONITOR IMPORTS HeapFileDefs, PolicyDefs, VMDefs EXPORTS ObjectDirDefs, ObjectDirXDefs SHARES ObjectDirDefs = BEGIN -- Reference counts -- ObjectCount: TYPE = CARDINAL; zeroCount: ObjectCount = 0; oneCount: ObjectCount = zeroCount+1; -- Structure of object directory pages -- handle: VMDefs.FileHandle; -- set by HeapRestart -- -- Object directory is sequence of pages, each of type "DirPage" -- Pages containing unused indexes are on "firstFreePage" chain -- Unused indexes within page are in header.nextFreeIndex chain -- Unused indexes are not usable until lastDofpc is high enough -- See ObjectDirDefs comments about DOPC. -- There may be pages on "firstFreePage" chain having no free indexes -- Object directory page numbers (and markers) fit into 8 bits! ObjDirPageSpace: TYPE = [0..255]; unchained: ObjDirPageSpace = LAST[ObjDirPageSpace];-- "not on free chain" endOfChain:ObjDirPageSpace = PRED[unchained];-- "end of free chain" ObjDirPageNumber: TYPE = [FIRST[ObjDirPageSpace]..PRED[endOfChain]]; DirData: TYPE = RECORD[ SELECT freedom: * --w0,b15-- FROM free => [ next: VMDefs.PageIndex,--w0,b[7..0]-- dopc: ObjectDirXDefs.DOPC --w[1..2]-- ], used => [ type: ObjectDirDefs.ObjectType,--w0,b[14..11]-- word: VMDefs.PageIndex,--w0,b[7..0]-- page: VMDefs.PageNumber,--w1-- count: ObjectCount --w2-- ], ENDCASE]; DirPageHeader: TYPE = RECORD[ nextFreePage: ObjDirPageSpace, nextFreeIndex: VMDefs.PageIndex ]; entriesPerPage: CARDINAL = (VMDefs.pageSize-SIZE[DirPageHeader])/SIZE[DirData]; DirIndex: TYPE = [ 0 .. entriesPerPage ); -- NOTE: DirIndex values must fit in the "index" field of an object number -- DirPage: TYPE = RECORD[ header: DirPageHeader, data: ARRAY DirIndex OF DirData ]; noFreeIndex: [0..LAST[VMDefs.PageIndex]] = SUCC[LAST[DirIndex]]; firstFreePage: ObjDirPageSpace ← endOfChain; -- first page containing free numbers -- nextVirginPage: ObjDirPageSpace; -- current size of file -- dpNumber: ObjDirPageNumber; dpPage: POINTER TO DirPage ← NIL; GetDirPage: INTERNAL PROCEDURE[ reqd: ObjDirPageNumber ] = BEGIN IF dpPage = NIL OR dpNumber # reqd THEN BEGIN IF dpPage # NIL THEN VMDefs.Release[LOOPHOLE[dpPage, VMDefs.Page]]; dpPage ← LOOPHOLE[VMDefs.ReadPage[[handle,dpNumber←reqd],0--lookAhead--], POINTER TO DirPage]; END; END; -- Allocation of object numbers -- InitPage: PROCEDURE = BEGIN -- initialize current page as empty and chain onto firstFreePage -- index: DirIndex; dpPage.header ← [nextFreePage:firstFreePage, nextFreeIndex:FIRST[DirIndex]]; firstFreePage ← dpNumber; FOR index IN DirIndex DO dpPage.data[index] ← [free[next:SUCC[index],dopc:lastDofpc]] ENDLOOP END; BadFreeObjChain: ERROR = CODE; ObjectDirectoryFull: ERROR = CODE; NewObject: PUBLIC ENTRY PROCEDURE[ at: VMDefs.FullAddress, type: ObjectDirDefs.ObjectType ] RETURNS[ obj: ObjectDirXDefs.ObjectNumber ] = BEGIN obj.page ← firstFreePage; DO --until we find a suitable page -- IF obj.page = endOfChain THEN BEGIN -- allocate a new page by extending object dir file -- obj.page ← nextVirginPage; nextVirginPage ← nextVirginPage+1; IF dpPage # NIL THEN VMDefs.Release[LOOPHOLE[dpPage,VMDefs.Page]]; dpPage ← LOOPHOLE[VMDefs.UsePage[[handle,dpNumber←obj.page]], POINTER TO DirPage]; InitPage[] -- puts it on firstFreePage list --; VMDefs.MarkStart[LOOPHOLE[dpPage,VMDefs.Page]]; --to cause file extension-- END ELSE GetDirPage[obj.page]; IF obj.page > LAST[ObjDirPageNumber] THEN ERROR ObjectDirectoryFull[]; IF dpPage.header.nextFreePage = unchained THEN ERROR BadFreeObjChain[]; -- if page is empty and is start of free page list IF dpPage.header.nextFreeIndex = noFreeIndex AND obj.page = firstFreePage THEN BEGIN firstFreePage ← obj.page ← dpPage.header.nextFreePage; dpPage.header.nextFreePage ← unchained; ReleaseData[dirty]; END ELSE BEGIN -- scan free chain on this page -- prev: POINTER TO DirData ← NIL; possible: VMDefs.PageIndex ← dpPage.header.nextFreeIndex; UNTIL possible = noFreeIndex DO -- find re-usable object number on this page's free list -- WITH data: dpPage.data[possible] SELECT FROM free => IF data.dopc <= lastDofpc THEN BEGIN -- take it! -- obj.index ← possible; IF prev = NIL THEN dpPage.header.nextFreeIndex ← data.next ELSE WITH prevData: prev SELECT FROM free => prevData.next ← data.next; ENDCASE => ERROR; GOTO found END ELSE BEGIN prev ← @(dpPage.data[possible]); possible ← data.next; END; ENDCASE => ERROR BadFreeObjChain[]; ENDLOOP; obj.page ← dpPage.header.nextFreePage; ReleaseData[clean]; END; REPEAT found => NULL; ENDLOOP; dpPage.data[obj.index] ← [used[ page: at.page.page, word: at.word, type: type, count: oneCount ]]; ReleaseData[dirty]; obj.fill ← 0; --to allow for expansion of type field-- obj.type ← type; END; CacheState: TYPE = { clean, dirty }; IllegalObjectNumber: ERROR = CODE; FindData: INTERNAL PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber ] RETURNS[ data: POINTER TO DirData ] = BEGIN IF obj.index > LAST[DirIndex] THEN ERROR IllegalObjectNumber[]; GetDirPage[ obj.page ]; RETURN[ @(dpPage.data[obj.index]) ] END; ReleaseData: INTERNAL PROCEDURE[ d: CacheState ] = BEGIN IF d=dirty THEN VMDefs.Mark[LOOPHOLE[dpPage,VMDefs.Page]]; END; -- Operations on individual object numbers -- ObjectWithZeroCount: ERROR = CODE; IllegalDestroyObject: ERROR = CODE; ObjectNotInUse: ERROR = CODE; ObjectCountTooBig: ERROR = CODE; heapHandle: VMDefs.FileHandle; -- set by HeapRestart -- ObjectBase: PUBLIC ENTRY PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber ] RETURNS[ addr: VMDefs.FullAddress ] = BEGIN either: POINTER TO DirData = FindData[obj]; WITH data: either SELECT FROM used => BEGIN IF data.count < oneCount THEN ERROR ObjectWithZeroCount; addr ← [ page: [heapHandle, data.page], word: data.word ]; END; ENDCASE => ERROR ObjectNotInUse[]; ReleaseData[clean]; END; UseObject: PUBLIC ENTRY PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber ] = BEGIN either: POINTER TO DirData = FindData[obj]; WITH data: either SELECT FROM used => BEGIN IF data.count < oneCount THEN ERROR ObjectWithZeroCount; IF data.count = LAST[ObjectCount] THEN ERROR ObjectCountTooBig; data.count ← data.count + 1; END; ENDCASE => ERROR ObjectNotInUse[]; ReleaseData[dirty]; END; FreeObject: PUBLIC ENTRY PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber ] = BEGIN either: POINTER TO DirData = FindData[obj]; WITH data: either SELECT FROM used => BEGIN IF data.count < oneCount THEN ERROR ObjectWithZeroCount; IF ( data.count ← data.count - 1 ) = zeroCount THEN BEGIN IF obj.type = temp THEN BEGIN HeapFileDefs.ObjectAbandoned[[heapHandle,data.page]]; AddToFreeList[obj,either,0]; END ELSE PolicyDefs.GapExists[]; END; END; ENDCASE => ERROR ObjectNotInUse[]; ReleaseData[dirty]; END; IllegalRelease: ERROR = CODE; ReleaseObject: PUBLIC ENTRY PROCEDURE [ obj: ObjectDirXDefs.ObjectNumber ] = BEGIN either: POINTER TO DirData = FindData[obj]; IF obj.type = temp THEN ERROR IllegalRelease[]; WITH data: either SELECT FROM used => IF data.count # zeroCount THEN ERROR IllegalRelease[] ELSE {AddToFreeList[obj, either, 0]; ReleaseData[dirty]}; ENDCASE => ERROR IllegalRelease[]; END; -- ReleaseObject -- IllegalEnumerate: ERROR = CODE; Enumerate: PUBLIC PROCEDURE[type: ObjectDirDefs.ObjectType, proc: PROCEDURE[ObjectDirXDefs.ObjectNumber]RETURNS[BOOLEAN] ] RETURNS[ObjectDirXDefs.ObjectNumber] = BEGIN Check: ENTRY PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber ] RETURNS[ wanted: BOOLEAN ] = INLINE BEGIN either: POINTER TO DirData = FindData[obj]; WITH data: either SELECT FROM used => BEGIN wanted ← data.type = obj.type; IF wanted THEN data.count ← data.count+1; END; ENDCASE => wanted ← FALSE; ReleaseData[IF wanted THEN dirty ELSE clean]; END; IF type = temp THEN ERROR IllegalEnumerate[]; FOR page: VMDefs.PageNumber ← 0, page+1 DO Last: ENTRY PROCEDURE RETURNS[ BOOLEAN] = INLINE BEGIN RETURN[ page = nextVirginPage ] END; IF Last[] THEN EXIT; FOR index: DirIndex IN [ FIRST[DirIndex] .. LAST[DirIndex] ] DO BEGIN obj: ObjectDirXDefs.ObjectNumber = [page:page, fill:0, type:type, index:index]; IF Check[obj] THEN BEGIN result: BOOLEAN ← proc[obj ! UNWIND => FreeObject[obj]]; FreeObject[obj]; IF result THEN RETURN[obj]; END; END; ENDLOOP; ENDLOOP; RETURN[ObjectDirDefs.noObject]; END; -- Compactor interface -- lastDofpc: ObjectDirXDefs.DOPC ← 0; ReportDofpc: PUBLIC ENTRY PROCEDURE[ dofpc: ObjectDirXDefs.DOPC ] = { lastDofpc ← dofpc }; MoveObject: PUBLIC ENTRY PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber, where: VMDefs.FullAddress ] = BEGIN either: POINTER TO DirData = FindData[obj]; -- the compactor may occasionally move objects whose count has -- -- decreased to zero after the compactor called 'ObjectInUse' -- WITH data: either SELECT FROM used => BEGIN data.page ← where.page.page; data.word ← where.word; END; ENDCASE => ERROR ObjectNotInUse[]; ReleaseData[dirty]; END; GetObjectState: PUBLIC ENTRY PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber, where: VMDefs.FullAddress, dorpc: ObjectDirXDefs.DOPC ] RETURNS[ state: ObjectDirXDefs.ObjectState ] = BEGIN either: POINTER TO DirData = FindData[obj]; -- TRUE iff non-zero count and on appropriate page -- -- Word position within page is not relevant: position is considered -- -- only to eliminate duplicates left behind by compactor crashes -- WITH data: either SELECT FROM used => BEGIN IF data.page # where.page.page THEN { state ← duplicate; ReleaseData[clean] } ELSE IF data.count = zeroCount THEN BEGIN AddToFreeList[obj,either,dorpc]; state ← unused; ReleaseData[dirty]; END ELSE { state ← inUse; ReleaseData[clean] }; END; ENDCASE => ERROR ObjectNotInUse[]; END; AddToFreeList: PROC[obj: ObjectDirXDefs.ObjectNumber, either: POINTER TO DirData, dorpc: ObjectDirXDefs.DOPC] = BEGIN IF dpPage.header.nextFreePage = unchained THEN { dpPage.header.nextFreePage←firstFreePage; firstFreePage ← dpNumber }; either↑ ← [free[dpPage.header.nextFreeIndex,dorpc]]; dpPage.header.nextFreeIndex ← obj.index; END; END.