DIRECTORY Basics USING [BITAND, BITSHIFT, Comparison], Camelot USING [btidT, DSLogNewValue, DSPinObject, -- ErSuccess, -- optrT, segmentIdT, -- TABegin, TAEnd, -- tidT], CamelotRecoverable USING [CamelotRecoveryComplete], Mach USING [kernReturnT, -- KernSuccess, -- pointerT, taskSelf, vmAddressT, vmAllocate, vmAllocateWithPager, vmDeallocate], PBasics USING [bytesPerWord, Copy, ShiftRight, Word], RedBlackTree USING [Compare, Create, Delete, GetKey, Insert, Lookup, Node, Table, UserData], Rope USING [ROPE], YggCamelotSegment USING [DIDMapLeaderPage, NextDIDPage, PageUse, PageZero, SegmentUseType, SegPort], YggDID USING [DID], YggDIDPrivate USING [DIDRep, HighDIDFirstClient], YggDIDMap USING [AddRuns, NullRun, RemoveRuns, Run, RunList, SetByteSize], YggEnvironment USING [dsPort, nullTransID, Outcome -- , taPort -- ], YggFile USING [bytesPerPage, Error, logBytesPerPage, logWordsPerPage, PageCount, PageNumber, Reason, wordsPerPage], YggFileInternal USING[Alloc, AllocAndFree, AllocForCreate, AllocWord, ClearLatches, FileHandleRep, FileMod, Free, Lookup, Modification, ModType, opRun, PTAllocBitmap, RunOpsList, SegmentMetadata, SegmentMetadataList], YggInternal USING[FileHandle], YggLogBasic USING[EstablishLogFile, OpenForPut], YggTransaction USING[CreateTrans, EqualTrans, Finish, GetParent, IsNullTrans, IsTopLevel]; YggFileImpl: CEDAR MONITOR IMPORTS Basics, Camelot, CamelotRecoverable, Mach, PBasics, RedBlackTree, YggDIDMap, YggEnvironment, YggFile, YggFileInternal, YggLogBasic, YggTransaction EXPORTS YggDID, YggFile, YggFileInternal, YggInternal ~ BEGIN ROPE: TYPE ~ Rope.ROPE; DID: PUBLIC TYPE ~ REF DIDRep; DIDRep: PUBLIC TYPE ~ YggDIDPrivate.DIDRep; Error: PUBLIC ERROR[why: YggFile.Reason, diskPage: INT] = CODE; FileHandle: TYPE = YggInternal.FileHandle; FileHandleRep: PUBLIC TYPE = YggFileInternal.FileHandleRep; SegmentMetadata: TYPE = YggFileInternal.SegmentMetadata; SegmentMetadataList: TYPE = YggFileInternal.SegmentMetadataList; SegMetadataList: PUBLIC SegmentMetadataList _ NIL; nextUID: CARD _ 1000; transactionToFileMap: RedBlackTree.Table; myCondition: CONDITION; Open: PUBLIC PROC [runList: YggDIDMap.RunList, byteSize: INT, did: YggDID.DID, fileUse: ATOM, verifyLeaderNow: BOOL] RETURNS [file: FileHandle _ NIL] ~ { assignUID: ENTRY PROC ~ { IF file.uid < 1000 THEN { file.uid _ nextUID; nextUID _ nextUID + 1; }; }; file _ YggFileInternal.Lookup[runList, did, fileUse]; file.sizeInBytes _ byteSize; assignUID[]; }; Create: PUBLIC PROC [size: YggFile.PageCount, did: YggDID.DID, fileUse: ATOM, nearToDid: YggDID.DID, tid: Camelot.tidT] RETURNS [file: FileHandle _ NIL] ~ { assignUID: ENTRY PROC ~ { file.uid _ nextUID; nextUID _ nextUID + 1; }; file _ YggFileInternal.AllocForCreate[]; -- gives us a handle not yet in FileTable assignUID[]; file.did _ did; file.fileUse _ fileUse; lockFile[file]; InnerSetSize[file, size, TRUE, tid, nearToDid]; unlockFile[file]; }; Delete: PUBLIC PROC [file: FileHandle, tid: Camelot.tidT] ~ { lockFile[file]; InnerSetSize[file, 0, FALSE, tid, NIL]; AddMod[file, tid, delete, YggDIDMap.NullRun, 0]; noteUseInTransaction[file, tid]; unlockFile[file]; }; nullCount: INT _ 0; nullSetSizeCount: INT _ 0; Info: PUBLIC PROC [file: FileHandle, tid: Camelot.tidT] RETURNS [did: YggDID.DID, size: YggFile.PageCount, byteSize: INT, runList: YggDIDMap.RunList] ~ { IF YggTransaction.IsNullTrans[tid] THEN nullCount _ nullCount + 1; lockFile[file]; did _ file.did; runList _ InnerLocate[file, tid, [0], INT.LAST]; [size, byteSize] _ fileSize[file, tid]; unlockFile[file]; }; SetSize: PUBLIC PROC [file: FileHandle, size: YggFile.PageCount, tid: Camelot.tidT] ~ { currentSize: YggFile.PageCount; lockFile[file]; currentSize _ fileSize[file, tid].size; IF size = currentSize THEN {unlockFile[file]; RETURN}; IF currentSize = -1 THEN {unlockFile[file]; ERROR YggFile.Error[unknownFile, -1]}; IF YggTransaction.IsNullTrans[tid] THEN nullSetSizeCount _ nullSetSizeCount + 1; InnerSetSize[file, size, FALSE, tid, file.did]; unlockFile[file]; }; SetByteSize: PUBLIC PROC [file: FileHandle, byteSize: YggFile.PageCount, tid: Camelot.tidT] ~ { lockFile[file]; AddMod [file: file, tid: tid, modType: setByteSize, run: YggDIDMap.NullRun, size: byteSize]; noteUseInTransaction[file, tid]; unlockFile[file]; }; Locate: PUBLIC PROC [file: FileHandle, tid: Camelot.tidT, from: YggFile.PageNumber, nPages: YggFile.PageCount] RETURNS [runList: YggDIDMap.RunList _ NIL] ~ { lockFile[file]; runList _ InnerLocate[file, tid, from, nPages]; unlockFile[file]; }; PagesForBytes: PUBLIC PROC [bytes: CARD] RETURNS [pages: YggFile.PageCount] ~ { IF bytes = 0 THEN RETURN [0] ELSE RETURN[PBasics.ShiftRight[[int[(bytes-1)]], YggFile.logBytesPerPage].int + 1]; }; WordsForPages: PUBLIC PROC [pages: YggFile.PageCount] RETURNS [words: CARD] ~ { RETURN[pages*YggFile.wordsPerPage]; }; PagesForWords: PUBLIC PROC [words: CARD] RETURNS [pages: YggFile.PageCount] ~ { IF words = 0 THEN RETURN [0] ELSE RETURN[PBasics.ShiftRight[[int[(words-1)]], YggFile.logWordsPerPage].int + 1]; }; BytesForPages: PUBLIC PROC [pages: YggFile.PageCount] RETURNS [bytes: CARD] ~ { RETURN[pages*YggFile.bytesPerPage]; }; ServerInfo: PUBLIC PROC RETURNS [blockSize: CARD, secondaryBlocks: CARD _ 0, secondaryBlocksFree: CARD _ 0, tertiaryBlocks: CARD _ 0, tertiaryBlocksFree: CARD _ 0] ~ { blockSize _ YggFile.bytesPerPage; FOR seg: YggFileInternal.SegmentMetadataList _ SegMetadataList, seg.rest UNTIL seg = NIL DO secondaryBlocks _ seg.first.numberOfPages + secondaryBlocks; secondaryBlocksFree _ seg.first.freePages + secondaryBlocksFree; ENDLOOP; }; minFactor: INT _ 10; -- => initial minimal runs are 1/10th of size change lockFile: ENTRY PROC [file: FileHandle] ~ { ENABLE UNWIND => {}; WHILE file.interlock DO {file.needBroadcast _ TRUE; WAIT myCondition; } ENDLOOP; file.interlock _ TRUE; }; unlockFile: ENTRY PROC [file: FileHandle] ~ { ENABLE UNWIND => {}; IF ~file.interlock THEN ERROR; file.interlock _ FALSE; IF file.needBroadcast THEN { file.needBroadcast _ FALSE; BROADCAST myCondition; }; }; segMetaFromSegID: PROC [segmentId: Camelot.segmentIdT] RETURNS [seg: LIST OF SegmentMetadata _ NIL] ~ { FOR seg _ SegMetadataList, seg.rest UNTIL seg = NIL DO IF seg.first.segmentId = segmentId THEN EXIT; ENDLOOP; }; fileSize: PROC [file: FileHandle, tid: Camelot.tidT] RETURNS [size: YggFile.PageCount, sizeInBytes: INT _ 0] ~ { sizeInBytes _ file.sizeInBytes; IF file.modificationList = NIL THEN RETURN[file.sizeInPages, sizeInBytes] ELSE { size _ file.sizeInPages; FOR modTL: LIST OF YggFileInternal.Modification _ file.modificationList, modTL.rest UNTIL modTL = NIL DO scratchTid: Camelot.tidT _ tid; DO -- once for each parent of the modification transFound: BOOL _ FALSE; IF YggTransaction.EqualTrans[modTL.first.tid, scratchTid] THEN { FOR mods: LIST OF YggFileInternal.FileMod _ modTL.first.mods, mods.rest UNTIL mods = NIL DO SELECT mods.first.type FROM addPages => { newSize:YggFile.PageCount; newSize _ mods.first.run.firstPage + mods.first.run.pages; IF newSize <= size THEN ERROR; size _ newSize; }; removePages => { IF mods.first.size >= size THEN ERROR; size _ mods.first.size; }; delete => { size _ -1; }; setByteSize => { sizeInBytes _ mods.first.size; }; ENDCASE; ENDLOOP; EXIT; }; IF YggTransaction.IsTopLevel[scratchTid] THEN EXIT; [transFound, scratchTid] _ YggTransaction.GetParent[scratchTid]; IF ~transFound THEN ERROR; ENDLOOP; ENDLOOP; }; }; InnerLocate: PROC [file: FileHandle, tid: Camelot.tidT, from: YggFile.PageNumber, nPages: YggFile.PageCount, ignoreDelete: BOOL _ FALSE] RETURNS [runList: YggDIDMap.RunList _ NIL] ~ { lastFrom: INT _ from + nPages - 1; FOR rl: YggDIDMap.RunList _ file.runList, rl.rest UNTIL rl = NIL DO firstPage: INT _ rl.first.firstPage; lastPage: INT _ rl.first.firstPage + rl.first.pages - 1; firstIntersection: INT _ -1; lastIntersection: INT _ -1; IF lastPage < from OR firstPage > lastFrom THEN LOOP; -- no intersection firstIntersection _ MAX[from, firstPage]; lastIntersection _ MIN[lastFrom, lastPage]; IF lastIntersection < firstIntersection THEN ERROR; runList _ CONS[[segmentId: rl.first.segmentId, segmentPage: rl.first.segmentPage + firstIntersection - rl.first.firstPage, firstPage: firstIntersection, pages: lastIntersection-firstIntersection+1, leader: FALSE], runList]; ENDLOOP; FOR modTL: LIST OF YggFileInternal.Modification _ file.modificationList, modTL.rest UNTIL modTL = NIL DO scratchTid: Camelot.tidT _ tid; DO -- once for each parent of the modification transFound: BOOL _ FALSE; IF YggTransaction.EqualTrans[modTL.first.tid, scratchTid] THEN { FOR mods: LIST OF YggFileInternal.FileMod _ modTL.first.mods, mods.rest UNTIL mods = NIL DO SELECT mods.first.type FROM addPages => { firstPage: INT _ mods.first.run.firstPage; lastPage: INT _ mods.first.run.firstPage + mods.first.run.pages - 1; firstIntersection: INT _ -1; lastIntersection: INT _ -1; IF lastPage < from OR firstPage > lastFrom THEN LOOP; -- no intersection firstIntersection _ MAX[from, firstPage]; lastIntersection _ MIN[lastFrom, lastPage]; IF lastIntersection < firstIntersection THEN ERROR; runList _ CONS[[segmentId: mods.first.run.segmentId, segmentPage: mods.first.run.segmentPage + firstIntersection - mods.first.run.firstPage, firstPage: firstIntersection, pages: lastIntersection-firstIntersection+1, leader: FALSE], runList]; }; removePages => { firstPage: INT _ mods.first.run.firstPage; lastPage: INT _ mods.first.run.firstPage + mods.first.run.pages - 1; firstIntersection: INT _ -1; lastIntersection: INT _ -1; prevRL: YggDIDMap.RunList _ NIL; IF lastPage < from OR firstPage > lastFrom THEN LOOP; -- no intersection firstIntersection _ MAX[from, firstPage]; lastIntersection _ MIN[lastFrom, lastPage]; IF lastIntersection < firstIntersection THEN ERROR; FOR rl: YggDIDMap.RunList _ runList, rl.rest UNTIL rl = NIL DO firstP: INT _ rl.first.firstPage; lastP: INT _ rl.first.firstPage + rl.first.pages - 1; firstI: INT _ -1; lastI: INT _ -1; IF lastP < from OR firstP > lastFrom THEN { -- no intersection prevRL _ rl; LOOP; }; firstI _ MAX[from, firstP]; lastI _ MIN[lastFrom, lastP]; IF lastI < firstI THEN ERROR; IF firstI = firstP AND lastI = lastP THEN { -- remove entry IF prevRL = NIL THEN runList _ runList.rest ELSE prevRL.rest _ rl.rest; LOOP; -- to avoid setting prevRL } ELSE { -- shrink entry rl.first.segmentPage _ rl.first.segmentPage + firstIntersection - rl.first.firstPage; rl.first.firstPage _ firstI; rl.first.pages _ lastI - firstI + 1; }; prevRL _ rl; ENDLOOP; }; delete => { IF ~ignoreDelete THEN runList _ NIL; }; ENDCASE; ENDLOOP; EXIT; }; IF YggTransaction.IsTopLevel[scratchTid] THEN EXIT; [transFound, scratchTid] _ YggTransaction.GetParent[scratchTid]; IF ~transFound THEN ERROR; ENDLOOP; ENDLOOP; }; InnerSetSize: PROC [file: FileHandle, size: YggFile.PageCount, create: BOOL, tid: Camelot.tidT, nearToDid: YggDID.DID] ~ { uncommittedSize: YggFile.PageCount _ 0 ; -- smallest run we will accept minRun: INT _ MIN[size, 20]; -- smallest run we will accept small: BOOL = size < 8; nearToSegment: LIST OF SegmentMetadata; nearToPage: YggFile.PageNumber; [nearToSegment, nearToPage] _ FindNearTo[nearToDid, small]; WHILE size # (uncommittedSize _ fileSize[file: file, tid: tid].size) DO delta: INT; delta _ size-uncommittedSize; SELECT TRUE FROM delta > 0 => { IF delta < minRun THEN minRun _ delta; [] _ Extend[file, delta, minRun, small, tid, nearToSegment, nearToPage]; minRun _ MAX[1, minRun/2]; }; delta < 0 => { Contract[file, tid]; EXIT; -- Fix this when Contract does something! }; ENDCASE => ERROR; [nearToSegment, nearToPage] _ FindNearTo[NIL, small]; ENDLOOP; }; FindNearTo: PROC [nearToDid: YggDID.DID, small: BOOL] RETURNS [nearToSegment: LIST OF SegmentMetadata, nearToPage: YggFile.PageNumber] ~ { nearToSegment _ SegMetadataList; FOR seg: LIST OF SegmentMetadata _ SegMetadataList.rest, seg.rest UNTIL seg = NIL DO IF seg.first.freePages > nearToSegment.first.freePages THEN nearToSegment _ seg; ENDLOOP; nearToPage _ [0]; -- fix to use the most free "cylinder group" -- fix to use the nearToDid }; AddMod: PROC [file: FileHandle, tid: Camelot.tidT, modType: YggFileInternal.ModType, run: YggDIDMap.Run, size: YggFile.PageCount] ~ { IF YggTransaction.IsNullTrans[tid] THEN nullCount _ nullCount + 1; IF file.modificationList = NIL THEN file.modificationList _ LIST[[tid, LIST[[modType, run, size]]]] ELSE { modForTid: LIST OF YggFileInternal.Modification; FOR modForTid _ file.modificationList, modForTid.rest UNTIL modForTid = NIL DO IF YggTransaction.EqualTrans[modForTid.first.tid, tid] THEN { modL: LIST OF YggFileInternal.FileMod; FOR modL _ modForTid.first.mods, modL.rest UNTIL modL.rest = NIL DO ENDLOOP; modL.rest _ CONS[[modType, run, size], modL.rest]; EXIT; }; REPEAT FINISHED => { file.modificationList _ CONS[[tid, LIST[[modType, run, size]]], file.modificationList]; }; ENDLOOP; }; }; Extend: PROC [file: FileHandle, delta, min: INT, small: BOOL, tid: Camelot.tidT, nearToSegment: LIST OF SegmentMetadata, nearToPage: YggFile.PageNumber] RETURNS [done: BOOL _ TRUE, runList: LIST OF YggFileInternal.FileMod _ NIL] = TRUSTED { uncommittedSize: YggFile.PageCount; amount: INT _ delta; extendInRange: PROC [minPage: YggFile.PageNumber, maxPage: YggFile.PageNumber, suppressErrors: BOOL] RETURNS [done: BOOL _ FALSE] = TRUSTED { WHILE amount > 0 DO run: YggDIDMap.Run; run _ YggFileInternal.Alloc[segment: nearToSegment, first: nearToPage, size: amount, min: min, minPage: minPage, maxPage: maxPage ]; IF run.pages <= 0 THEN RETURN [FALSE]; run.firstPage _ uncommittedSize; nearToPage _ [run.segmentPage + run.pages]; -- hint for next call of Alloc -- AddMod[file, tid, addPages, run, 0]; uncommittedSize _ uncommittedSize + run.pages; amount _ amount - run.pages; ENDLOOP -- Loop for each allocated disk run --; RETURN [TRUE]; }; uncommittedSize _ fileSize[file, tid].size; IF ~extendInRange[[0], [nearToSegment.first.numberOfPages-1], FALSE] THEN RETURN [FALSE]; noteUseInTransaction[file, tid]; }; Contract: PROC [file: FileHandle, tid: Camelot.tidT] = { }; OpsList: TYPE = LIST OF OpsListRec; OpsListRec: TYPE = RECORD[ segmentId: Camelot.segmentIdT, runOps: YggFileInternal.RunOpsList ]; PreCommit: PUBLIC PROC [tid: Camelot.tidT] ~ { IF YggTransaction.IsTopLevel[tid] THEN { -- top level transaction data: RedBlackTree.UserData; scrTIDObj: TIDObj _ CheckoutTIDObj[tid.top]; checkForOnlyTopLevelTrans[tid]; data _ RedBlackTree.Lookup[ transactionToFileMap, scrTIDObj]; IF data # NIL THEN { opsToDo: OpsList _ NIL; addOp: PROC [ op: YggFileInternal.opRun, segmentId: Camelot.segmentIdT, run: YggDIDMap.Run] ~ { prevOpsToDo: OpsList _ NIL; FOR ol: OpsList _ opsToDo, ol.rest UNTIL ol = NIL DO IF ol.first.segmentId = segmentId THEN { ol.first.runOps _ CONS[[op, run], ol.first.runOps]; EXIT; }; IF segmentId < ol.first.segmentId THEN { IF prevOpsToDo = NIL THEN { opsToDo _ LIST[[segmentId, LIST[[op, run]]]]; opsToDo.rest _ prevOpsToDo; } ELSE { newOps: OpsList _ NIL; newOps _ LIST[[segmentId, LIST[[op, run]]]]; newOps.rest _ prevOpsToDo.rest; prevOpsToDo.rest _ newOps; }; EXIT; }; prevOpsToDo _ ol; REPEAT FINISHED => { IF opsToDo = NIL THEN opsToDo _ LIST[[segmentId, LIST[[op, run]]]] ELSE prevOpsToDo.rest _ LIST[[segmentId, LIST[[op, run]]]]; }; ENDLOOP; }; ft: FileTrans; ft _ NARROW[data]; FOR fH: LIST OF FileHandle _ ft.files, fH.rest UNTIL fH = NIL DO -- look at all the file handles FOR fileMods: LIST OF YggFileInternal.Modification _ fH.first.modificationList, fileMods.rest UNTIL fileMods = NIL DO -- for each file, look at the modification lists IF ~YggTransaction.EqualTrans[fileMods.first.tid, tid] THEN ERROR; FOR mods: LIST OF YggFileInternal.FileMod _ fileMods.first.mods, mods.rest UNTIL mods = NIL DO SELECT mods.first.type FROM addPages => { addOp[stableAlloc, mods.first.run.segmentId, mods.first.run]; YggDIDMap.AddRuns[fH.first.did, tid, fH.first.fileUse, LIST[mods.first.run]]; }; removePages => { addOp[stableAndVolatileFree, mods.first.run.segmentId, mods.first.run]; YggDIDMap.RemoveRuns[fH.first.did, tid, LIST[mods.first.run]]; }; delete => { runList: YggDIDMap.RunList _ NIL; runList _ InnerLocate[fH.first, tid, [0], INT.LAST, TRUE]; YggDIDMap.RemoveRuns[fH.first.did, tid, runList]; FOR rl: YggDIDMap.RunList _ runList, rl.rest UNTIL rl = NIL DO addOp[stableAndVolatileFree, rl.first.segmentId, rl.first]; ENDLOOP; }; setByteSize => { YggDIDMap.SetByteSize[did: fH.first.did, tid: tid, fileUse: fH.first.fileUse, bytes: mods.first.size]; }; ENDCASE; ENDLOOP; ENDLOOP; IF fH.first.modificationList # NIL THEN { [fH.first.sizeInPages, fH.first.sizeInBytes] _ fileSize[fH.first, tid]; fH.first.runList _ InnerLocate[fH.first, tid, [0], INT.LAST, TRUE]; }; fH.first.modificationList _ NIL; ENDLOOP; IF opsToDo # NIL THEN { ft.opsToDo _ opsToDo; FOR ol: OpsList _ opsToDo, ol.rest UNTIL ol = NIL DO YggFileInternal.AllocAndFree[segment: segMetaFromSegID[ol.first.segmentId], tid: tid, runOpsList: ol.first.runOps]; ENDLOOP; }; ft.didPreCommit _ TRUE; }; RecycleTIDObj[scrTIDObj]; } ELSE { -- nested transaction promoteTidsToParent: ENTRY PROC ~ { ENABLE UNWIND => {}; data: RedBlackTree.UserData; scratchTIDObj.btid _ tid.top; data _ RedBlackTree.Lookup[ transactionToFileMap, scratchTIDObj]; IF data # NIL THEN { ft: FileTrans; ft _ NARROW[data]; FOR fH: LIST OF FileHandle _ ft.files, fH.rest UNTIL fH = NIL DO -- look at all the file handles prevMods: LIST OF YggFileInternal.Modification _ NIL; FOR fileMods: LIST OF YggFileInternal.Modification _ fH.first.modificationList, fileMods.rest UNTIL fileMods = NIL DO -- for each file, look at the modification lists IF YggTransaction.EqualTrans[fileMods.first.tid, tid] THEN { -- got some modifications to promote FOR fileMods2: LIST OF YggFileInternal.Modification _ fH.first.modificationList, fileMods2.rest UNTIL fileMods2 = NIL DO IF YggTransaction.EqualTrans[fileMods.first.tid, parentTid] THEN { -- parent has some modifications of its own; add subtransaction modifications and remove subtransaction entry lastMod: LIST OF YggFileInternal.FileMod _ fileMods.first.mods; FOR mods: LIST OF YggFileInternal.FileMod _ fileMods.first.mods.rest, mods.rest UNTIL mods = NIL DO ENDLOOP; lastMod.rest _ fileMods.first.mods; IF prevMods = NIL THEN fH.first.modificationList _ fileMods.rest ELSE prevMods.rest _ prevMods.rest.rest; } ELSE { -- no parent; change tid in header to promote fileMods.first.tid _ parentTid; }; ENDLOOP; EXIT; -- done with file; go do the next file }; prevMods _ fileMods; ENDLOOP; ENDLOOP; }; }; parentTid: Camelot.tidT; transFound: BOOL _ FALSE; [transFound, parentTid] _ YggTransaction.GetParent[tid]; IF ~transFound THEN ERROR; promoteTidsToParent[]; }; }; Commit: PUBLIC PROC [tid: Camelot.tidT] ~ { IF YggTransaction.IsTopLevel[tid] THEN { -- top level transaction deletedNode: RedBlackTree.Node; scrTIDObj: TIDObj _ CheckoutTIDObj[tid.top]; checkForOnlyTopLevelTrans[tid]; deletedNode _ RedBlackTree.Delete[ transactionToFileMap, scrTIDObj]; RecycleTIDObj[scrTIDObj]; IF deletedNode # NIL THEN { ft: FileTrans; ft _ NARROW[deletedNode.data]; FOR ol: OpsList _ ft.opsToDo, ol.rest UNTIL ol = NIL DO YggFileInternal.ClearLatches[segment: segMetaFromSegID[ol.first.segmentId], tid: tid]; ENDLOOP; FOR fH: LIST OF FileHandle _ ft.files, fH.rest UNTIL fH = NIL DO -- look at all the file handles fH.first.modificationList _ NIL; ENDLOOP; }; }; }; Abort: PUBLIC PROC [tid: Camelot.tidT] ~ { IF YggTransaction.IsTopLevel[tid] THEN { -- top level transaction data: RedBlackTree.UserData; scrTIDObj: TIDObj _ CheckoutTIDObj[tid.top]; checkForOnlyTopLevelTrans[tid]; data _ RedBlackTree.Lookup[ transactionToFileMap, scrTIDObj]; IF data # NIL THEN { ft: FileTrans; ft _ NARROW[data]; FOR fH: LIST OF FileHandle _ ft.files, fH.rest UNTIL fH = NIL DO -- look at all the file handles FOR fileMods: LIST OF YggFileInternal.Modification _ fH.first.modificationList, fileMods.rest UNTIL fileMods = NIL DO -- for each file, look at the modification lists prevMod: LIST OF YggFileInternal.FileMod _ NIL; FOR mods: LIST OF YggFileInternal.FileMod _ fileMods.first.mods, mods.rest UNTIL mods = NIL DO SELECT mods.first.type FROM addPages => { seg: LIST OF SegmentMetadata _ NIL; seg _ segMetaFromSegID[mods.first.run.segmentId]; IF seg = NIL THEN ERROR; YggFileInternal.Free[seg, mods.first.run]; }; removePages => { }; delete => { }; ENDCASE; prevMod _ mods; ENDLOOP; ENDLOOP; ENDLOOP; FOR ol: OpsList _ ft.opsToDo, ol.rest UNTIL ol = NIL DO YggFileInternal.ClearLatches[segment: segMetaFromSegID[ol.first.segmentId], tid: tid]; ENDLOOP; }; [] _ RedBlackTree.Delete[ transactionToFileMap, scrTIDObj]; RecycleTIDObj[scrTIDObj]; } ELSE { -- nested transaction obliterateSubtransaction: ENTRY PROC ~ { ENABLE UNWIND => {}; data: RedBlackTree.UserData; scratchTIDObj.btid _ tid.top; data _ RedBlackTree.Lookup[ transactionToFileMap, scratchTIDObj]; IF data # NIL THEN { ft: FileTrans; ft _ NARROW[data]; FOR fH: LIST OF FileHandle _ ft.files, fH.rest UNTIL fH = NIL DO -- look at all the file handles prevMods: LIST OF YggFileInternal.Modification _ NIL; FOR fileMods: LIST OF YggFileInternal.Modification _ fH.first.modificationList, fileMods.rest UNTIL fileMods = NIL DO -- for each file, look at the modification lists IF YggTransaction.EqualTrans[fileMods.first.tid, tid] THEN { -- got some modifications to obliterate FOR mods: LIST OF YggFileInternal.FileMod _ fileMods.first.mods, mods.rest UNTIL mods = NIL DO SELECT mods.first.type FROM addPages => { seg: LIST OF SegmentMetadata _ NIL; seg _ segMetaFromSegID[mods.first.run.segmentId]; IF seg = NIL THEN ERROR; YggFileInternal.Free[seg, mods.first.run]; }; removePages => { }; delete => { }; ENDCASE; ENDLOOP; IF prevMods = NIL THEN fH.first.modificationList _ fileMods.rest ELSE prevMods.rest _ prevMods.rest.rest; EXIT; -- done with file; go do the next file }; prevMods _ fileMods; ENDLOOP; ENDLOOP; }; }; obliterateSubtransaction[]; }; }; TIDObj: TYPE = REF TIDObjRep; TIDObjRep: TYPE = RECORD [ btid: Camelot.btidT]; scratchTIDObj: TIDObj _ NEW[TIDObjRep]; -- must own the monitor to manipulate this FileTrans: TYPE = REF FileTransRep; FileTransRep: TYPE = RECORD [ btidForFT: TIDObj, didPreCommit: BOOL _ FALSE, files: LIST OF FileHandle, opsToDo: OpsList _ NIL ]; checkForOnlyTopLevelTrans: ENTRY PROC [tid: Camelot.tidT] ~ { ENABLE UNWIND => {}; data: RedBlackTree.UserData; scratchTIDObj.btid _ tid.top; data _ RedBlackTree.Lookup[ transactionToFileMap, scratchTIDObj]; IF data # NIL THEN { ft: FileTrans; ft _ NARROW[data]; FOR fH: LIST OF FileHandle _ ft.files, fH.rest UNTIL fH = NIL DO FOR fileMods: LIST OF YggFileInternal.Modification _ fH.first.modificationList, fileMods.rest UNTIL fileMods = NIL DO IF fileMods.first.tid # tid THEN ERROR; ENDLOOP; ENDLOOP; }; }; noteUseInTransaction: ENTRY PROC [file: FileHandle, tid: Camelot.tidT] ~ { ENABLE UNWIND => {}; data: RedBlackTree.UserData; scratchTIDObj.btid _ tid.top; data _ RedBlackTree.Lookup[ transactionToFileMap, scratchTIDObj]; IF data = NIL THEN { ft: FileTrans _ NEW[FileTransRep]; ft.btidForFT _ NEW[TIDObjRep _ [tid.top]]; ft.files _ LIST[file]; RedBlackTree.Insert[transactionToFileMap, ft, scratchTIDObj]; } ELSE { ft: FileTrans; ft _ NARROW[data]; FOR fH: LIST OF FileHandle _ ft.files, fH.rest UNTIL fH = NIL DO IF fH.first.uid = file.uid THEN RETURN; ENDLOOP; ft.files _ CONS[file, ft.files]; }; }; GetKeyProc: RedBlackTree.GetKey = { fileTrans: FileTrans _ NARROW[data]; RETURN[ fileTrans.btidForFT ]; }; CompareProc: RedBlackTree.Compare = { fileTransData: FileTrans _ NARROW[data]; keyData: TIDObj _ NARROW[k]; SELECT keyData.highTicker FROM > fileTransData.btidForFT.highTicker => RETURN [greater]; < fileTransData.btidForFT.highTicker => RETURN [less]; ENDCASE => { SELECT keyData.lowTicker FROM > fileTransData.btidForFT.lowTicker => RETURN [greater]; < fileTransData.btidForFT.lowTicker => RETURN [less]; ENDCASE => RETURN [equal]; }; }; stockTIDObj: TIDObj _ NIL; CheckoutTIDObj: ENTRY PROC [btid: Camelot.btidT] RETURNS [k: TIDObj] = { IF stockTIDObj = NIL THEN k _ NEW [TIDObjRep] ELSE { k _ stockTIDObj; stockTIDObj _ NIL }; k.btid _ btid; }; RecycleTIDObj: ENTRY PROC [k: TIDObj] = { stockTIDObj _ k; }; InitializeFile: PUBLIC PROC [segmentList: LIST OF YggCamelotSegment.SegPort] RETURNS [firstTime : BOOL _ FALSE ]~ { lastSegmentMetadata: SegmentMetadataList _ NIL; gotMetaData: BOOL _ FALSE; FOR sl: LIST OF YggCamelotSegment.SegPort _ segmentList, sl.rest UNTIL sl = NIL DO smd: SegmentMetadataList; mappedAddress: Mach.vmAddressT; segmentUse: YggCamelotSegment.SegmentUseType; DIDMapLogicalPage0: CARD32; NextDIDLogicalPage: CARD32; page0: LONG POINTER TO YggCamelotSegment.PageZero; freePages: INT _ 0; vmAddressForSegmentAllocMap: YggFileInternal.PTAllocBitmap; vmAddressForShadowAllocMap: YggFileInternal.PTAllocBitmap; kernCode: Mach.kernReturnT; [mappedAddress: mappedAddress, kernCode: kernCode] _ Mach.vmAllocateWithPager[targetTask: Mach.taskSelf[], address: 0, size: YggFile.bytesPerPage, anywhere: TRUE, pagingObject: sl.first.port, offset: 0, raiseSignal: TRUE]; TRUSTED { page0 _ LOOPHOLE[mappedAddress]; segmentUse _ page0.segmentUse; DIDMapLogicalPage0 _ page0.DIDMapLogicalPage0; NextDIDLogicalPage _ page0.NextDIDLogicalPage; }; IF segmentUse = undefined THEN { smd _ LIST[[,sl.first.segment.segmentId, sl.first.port, sl.first.segment.lowSize, undefined, 0, 0, 0, [0], 0, 0, NIL, NIL]]; } ELSE { TRUSTED { IF page0.segmentId # sl.first.segment.segmentId THEN ERROR; IF segmentUse = systemMetaData OR segmentUse = normalAndSystemMetaData THEN gotMetaData _ TRUE; [mappedAddress: mappedAddress, kernCode: kernCode] _ Mach.vmAllocateWithPager[targetTask: Mach.taskSelf[], address: 0, size: page0.allocationMapSize*YggFile.bytesPerPage, anywhere: TRUE, pagingObject: sl.first.port, offset: page0.allocationMapStartPage*YggFile.bytesPerPage, raiseSignal: TRUE]; vmAddressForSegmentAllocMap _ LOOPHOLE[mappedAddress]; [mappedAddress: mappedAddress, kernCode: kernCode] _ Mach.vmAllocate[targetTask: Mach.taskSelf[], address: 0, size: page0.allocationMapSize*YggFile.bytesPerPage, anywhere: TRUE, raiseSignal: TRUE]; vmAddressForShadowAllocMap _ LOOPHOLE[mappedAddress]; Copy[from: vmAddressForSegmentAllocMap, nwords: page0.allocationMapSize*YggFile.bytesPerPage/PBasics.bytesPerWord, to: vmAddressForShadowAllocMap]; FOR word: CARD IN [0..page0.segmentMaximum/BITS[YggFileInternal.AllocWord]] DO SELECT vmAddressForSegmentAllocMap[word].card FROM LAST[CARD32] => {}; 0 => freePages _ freePages + 32; ENDCASE => { countBitsInByte: PROC [byte: BYTE] RETURNS [cnt: INT _ 0] ~ CHECKED { wd: WORD _ 0; wd _ byte; UNTIL wd = 0 DO IF Basics.BITAND[wd, 1] = 0 THEN cnt _ cnt + 1; wd _ Basics.BITSHIFT[wd, 1]; ENDLOOP; }; freePages _ countBitsInByte[vmAddressForSegmentAllocMap[word].hh] + countBitsInByte[vmAddressForSegmentAllocMap[word].hl] + countBitsInByte[vmAddressForSegmentAllocMap[word].lh] + countBitsInByte[vmAddressForSegmentAllocMap[word].ll]; }; ENDLOOP; smd _ LIST[[, sl.first.segment.segmentId, sl.first.port, sl.first.segment.lowSize, segmentUse, page0.segmentLogicalPage0, page0.segmentMaximum - page0.segmentLogicalPage0, freePages, [0], DIDMapLogicalPage0, NextDIDLogicalPage, vmAddressForSegmentAllocMap, vmAddressForShadowAllocMap]]; }; }; kernCode _ Mach.vmDeallocate[targetTask: Mach.taskSelf[], address: LOOPHOLE[page0], size: YggFile.bytesPerPage, raiseSignal: TRUE]; IF SegMetadataList = NIL THEN SegMetadataList _ lastSegmentMetadata _ smd ELSE {lastSegmentMetadata.rest _ smd; lastSegmentMetadata _ smd}; ENDLOOP; IF SegMetadataList.rest = NIL AND SegMetadataList.first.segmentUse = undefined THEN { firstTimeTID: Camelot.tidT; outcome: YggEnvironment.Outcome; status: INT _ -1; kernCode: Mach.kernReturnT; page0MappedAddress: Mach.vmAddressT; nextDIDMappedAddress: Mach.vmAddressT; didMapPage0MappedAddress: Mach.vmAddressT; nextDIDLogicalPage: CARD; didMapLogicalPage0: CARD; YggLogBasic.EstablishLogFile[]; YggLogBasic.OpenForPut[nextPage: 0, version: 1, nextRecord: [zero, 0, 0]]; firstTime _ TRUE; firstTimeTID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; [mappedAddress: page0MappedAddress, kernCode: kernCode] _ Mach.vmAllocateWithPager[targetTask: Mach.taskSelf[], address: 0, size: YggFile.bytesPerPage, anywhere: TRUE, pagingObject: SegMetadataList.first.port, offset: 0, raiseSignal: TRUE]; -- map page 0 TRUSTED { page0: LONG POINTER TO YggCamelotSegment.PageZero; word32: INT _ -1; mappedAddress: Mach.vmAddressT; page0 _ LOOPHOLE[page0MappedAddress]; page0.segmentId _ SegMetadataList.first.segmentId; page0.segmentUse _ normalAndSystemMetaData; SegMetadataList.first.segmentUse _ normalAndSystemMetaData; page0.metadataReplicated _ FALSE; page0.segmentMaximum _ SegMetadataList.first.lowSize/YggFile.bytesPerPage; page0.allocationMapSize _ 256; page0.NextDIDLogicalPage _ nextDIDLogicalPage _ 80; SegMetadataList.first.NextDIDLogicalPage _ nextDIDLogicalPage; page0.DIDMapLogicalPage0 _ didMapLogicalPage0 _ page0.NextDIDLogicalPage+1; -- one page for the Next DID SegMetadataList.first.DIDMapLogicalPage0 _ didMapLogicalPage0; page0.allocationMapStartPage _ 68+page0.DIDMapLogicalPage0; -- 66 pages (leader + dictionary + 64 + a few) for initial DID map page0.segmentLogicalPage0 _ page0.allocationMapStartPage+256; -- 256 pages for the allocation map (a megabyte) SegMetadataList.first.offsetToFirstAllocPage _ page0.segmentLogicalPage0; SegMetadataList.first.freePages _ SegMetadataList.first.numberOfPages _ page0.segmentMaximum - page0.segmentLogicalPage0; [mappedAddress: mappedAddress, kernCode: kernCode] _ Mach.vmAllocateWithPager[targetTask: Mach.taskSelf[], address: 0, size: page0.allocationMapSize*YggFile.bytesPerPage, anywhere: TRUE, pagingObject: SegMetadataList.first.port, offset: page0.allocationMapStartPage*YggFile.bytesPerPage, raiseSignal: TRUE]; SegMetadataList.first.vmAddressForSegmentAllocMap _ LOOPHOLE[mappedAddress]; [mappedAddress: mappedAddress, kernCode: kernCode] _ Mach.vmAllocate[targetTask: Mach.taskSelf[], address: 0, size: page0.allocationMapSize*YggFile.bytesPerPage, anywhere: TRUE, raiseSignal: TRUE]; SegMetadataList.first.vmAddressForShadowAllocMap _ LOOPHOLE[mappedAddress]; word32 _ SegMetadataList.first.offsetToFirstAllocPage/32; FOR w: INT IN [0..word32) DO SegMetadataList.first.vmAddressForSegmentAllocMap[w].card _ LAST[CARD]; ENDLOOP; FOR bitNo: INT IN [0..SegMetadataList.first.offsetToFirstAllocPage - 32 * word32) DO SegMetadataList.first.vmAddressForSegmentAllocMap[word32].bits[bitNo] _ TRUE; ENDLOOP; Copy[from: SegMetadataList.first.vmAddressForSegmentAllocMap, nwords: page0.allocationMapSize*YggFile.bytesPerPage/PBasics.bytesPerWord, to: SegMetadataList.first.vmAddressForShadowAllocMap]; }; kernCode _ Camelot.DSPinObject[dsPort: YggEnvironment.dsPort, tid: firstTimeTID, optr: [segmentId: SegMetadataList.first.segmentId, highOffset: 0, lowOffset: 0], size: YggFile.bytesPerPage, raiseSignal: TRUE]; kernCode _ Camelot.DSLogNewValue[dsPort: YggEnvironment.dsPort, tid: firstTimeTID, optr: [segmentId: SegMetadataList.first.segmentId, highOffset: 0, lowOffset: 0], newValue: page0MappedAddress, newValueCnt: YggFile.bytesPerPage, raiseSignal: TRUE]; [mappedAddress: nextDIDMappedAddress, kernCode: kernCode] _ Mach.vmAllocateWithPager[targetTask: Mach.taskSelf[], address: 0, size: YggFile.bytesPerPage, anywhere: TRUE, pagingObject: SegMetadataList.first.port, offset: YggFile.bytesPerPage*nextDIDLogicalPage, raiseSignal: TRUE]; -- map NextDID page TRUSTED { nextDIDPage: LONG POINTER TO YggCamelotSegment.NextDIDPage; nextDIDPage _ LOOPHOLE[nextDIDMappedAddress]; nextDIDPage.sealDIDMapLeaderPage _ nextDID; nextDIDPage.didLow _ 0; nextDIDPage.didHigh _ YggDIDPrivate.HighDIDFirstClient; }; kernCode _ Camelot.DSPinObject[dsPort: YggEnvironment.dsPort, tid: firstTimeTID, optr: [segmentId: SegMetadataList.first.segmentId, highOffset: 0, lowOffset: YggFile.bytesPerPage*nextDIDLogicalPage], size: YggFile.bytesPerPage, raiseSignal: TRUE]; kernCode _ Camelot.DSLogNewValue[dsPort: YggEnvironment.dsPort, tid: firstTimeTID, optr: [segmentId: SegMetadataList.first.segmentId, highOffset: 0, lowOffset: YggFile.bytesPerPage*nextDIDLogicalPage], newValue: nextDIDMappedAddress, newValueCnt: YggFile.bytesPerPage, raiseSignal: TRUE]; [mappedAddress: didMapPage0MappedAddress, kernCode: kernCode] _ Mach.vmAllocateWithPager[targetTask: Mach.taskSelf[], address: 0, size: YggFile.bytesPerPage, anywhere: TRUE, pagingObject: SegMetadataList.first.port, offset: YggFile.bytesPerPage*didMapLogicalPage0, raiseSignal: TRUE]; -- map DIDMap page 0 TRUSTED { didMapPage0: LONG POINTER TO YggCamelotSegment.DIDMapLeaderPage; didMapPage0 _ LOOPHOLE[didMapPage0MappedAddress]; didMapPage0.sealDIDMapLeaderPage _ didLeader; didMapPage0.runSize _ 66; didMapPage0.nextRun _ 0; -- no more runs }; kernCode _ Camelot.DSPinObject[dsPort: YggEnvironment.dsPort, tid: firstTimeTID, optr: [segmentId: SegMetadataList.first.segmentId, highOffset: 0, lowOffset: YggFile.bytesPerPage*didMapLogicalPage0], size: YggFile.bytesPerPage, raiseSignal: TRUE]; kernCode _ Camelot.DSLogNewValue[dsPort: YggEnvironment.dsPort, tid: firstTimeTID, optr: [segmentId: SegMetadataList.first.segmentId, highOffset: 0, lowOffset: YggFile.bytesPerPage*didMapLogicalPage0], newValue: nextDIDMappedAddress, newValueCnt: YggFile.bytesPerPage, raiseSignal: TRUE]; [outcome: outcome] _ YggTransaction.Finish[transID: firstTimeTID, requestedOutcome: commit]; IF outcome # commit THEN ERROR; kernCode _ Mach.vmDeallocate[targetTask: Mach.taskSelf[], address: page0MappedAddress, size: YggFile.bytesPerPage, raiseSignal: TRUE]; kernCode _ Mach.vmDeallocate[targetTask: Mach.taskSelf[], address: nextDIDMappedAddress, size: YggFile.bytesPerPage, raiseSignal: TRUE]; kernCode _ Mach.vmDeallocate[targetTask: Mach.taskSelf[], address: didMapPage0MappedAddress, size: YggFile.bytesPerPage, raiseSignal: TRUE]; CamelotRecoverable.CamelotRecoveryComplete[]; } ELSE { -- look for new segments IF ~gotMetaData THEN ERROR; -- missing metadata segment (the segment with the did map and all that) FOR sml: SegmentMetadataList _ SegMetadataList, sml.rest UNTIL sml = NIL DO IF sml.first.segmentUse = undefined THEN { -- a new segment ERROR; -- should init the new segment and fix up the list }; ENDLOOP; }; }; OpenDIDMapFile: PUBLIC PROC RETURNS [file: YggInternal.FileHandle _ NIL] ~ { FOR sml: SegmentMetadataList _ SegMetadataList, sml.rest UNTIL sml = NIL DO IF sml.first.segmentUse = systemMetaData OR sml.first.segmentUse = normalAndSystemMetaData THEN { kernCode: Mach.kernReturnT; didMapdid: DID _ NEW[DIDRep _ [0, 1]]; mappedAddress: Mach.vmAddressT; didLeader: LONG POINTER TO YggCamelotSegment.DIDMapLeaderPage; DIDMapLogicalPage0: CARD32; runList: YggDIDMap.RunList; DIDMapLogicalPage0 _ sml.first.DIDMapLogicalPage0; IF DIDMapLogicalPage0 = 0 THEN ERROR; [mappedAddress: mappedAddress, kernCode: kernCode] _ Mach.vmAllocateWithPager[targetTask: Mach.taskSelf[], address: 0, size: YggFile.bytesPerPage, anywhere: TRUE, pagingObject: sml.first.port, offset: DIDMapLogicalPage0*YggFile.bytesPerPage, raiseSignal: TRUE]; TRUSTED { didLeader _ LOOPHOLE[mappedAddress]; IF didLeader.sealDIDMapLeaderPage # didLeader THEN ERROR; IF didLeader.nextRun # 0 THEN ERROR; runList _ LIST[[sml.first.segmentId, DIDMapLogicalPage0+1, 0, didLeader.runSize, FALSE]]; }; kernCode _ Mach.vmDeallocate[targetTask: Mach.taskSelf[], address: mappedAddress, size: YggFile.bytesPerPage, raiseSignal: TRUE]; file _ Open[runList: runList, byteSize: 0, did: didMapdid, fileUse: $DIDMap, verifyLeaderNow: FALSE]; file.sizeInBytes _ BytesForPages[fileSize[file, YggEnvironment.nullTransID].size]; EXIT; }; ENDLOOP; }; GetMaxDIDAddress: PUBLIC PROC RETURNS [optr: Camelot.optrT, address: Mach.pointerT] ~ { FOR sml: SegmentMetadataList _ SegMetadataList, sml.rest UNTIL sml = NIL DO IF sml.first.segmentUse = systemMetaData OR sml.first.segmentUse = normalAndSystemMetaData THEN { kernCode: Mach.kernReturnT; mappedAddress: Mach.vmAddressT; IF sml.first.NextDIDLogicalPage = 0 THEN ERROR; [mappedAddress: mappedAddress, kernCode: kernCode] _ Mach.vmAllocateWithPager[targetTask: Mach.taskSelf[], address: 0, size: YggFile.bytesPerPage, anywhere: TRUE, pagingObject: sml.first.port, offset: sml.first.NextDIDLogicalPage*YggFile.bytesPerPage, raiseSignal: TRUE]; TRUSTED { nextDIDPage: LONG POINTER TO YggCamelotSegment.NextDIDPage; address _ LOOPHOLE[mappedAddress + UNITS[YggCamelotSegment.PageUse]]; nextDIDPage _ LOOPHOLE[mappedAddress]; IF nextDIDPage.sealDIDMapLeaderPage # nextDID THEN ERROR; }; optr _ [segmentId: sml.first.segmentId, highOffset: 0, lowOffset: sml.first.NextDIDLogicalPage*YggFile.bytesPerPage]; EXIT; }; ENDLOOP; }; Copy: UNSAFE PROC [from: LONG POINTER, nwords: CARD, to: LONG POINTER] = { nowFrom: LONG POINTER _ from; nowTo: LONG POINTER _ to; nWordsLeft: CARD _ nwords; WHILE nWordsLeft > 0 DO copyThisTime: CARD _ MIN[nWordsLeft, 10000]; TRUSTED {PBasics.Copy[from: nowFrom, nwords: copyThisTime, to: nowTo];}; nowFrom _ nowFrom + copyThisTime * UNITS[PBasics.Word]; nowTo _ nowTo + copyThisTime * UNITS[PBasics.Word]; nWordsLeft _ nWordsLeft - copyThisTime; ENDLOOP; }; Init: PROC ~ { transactionToFileMap _ RedBlackTree.Create[getKey: GetKeyProc, compare: CompareProc]; }; Init[]; END. èYggFileImpl.mesa Copyright Ó 1988, 1989 by Xerox Corporation. All rights reserved. Bob Hagmann June 5, 1989 4:50:23 pm PDT Handle all operations on FileHandles's. Data types and variables exported to YggFile Exported procedures for files Open the file with this run list. If verifyLeaderNow is true, verify that the leader matches the runList now, instead of delaying the access. Info about the server Internal procedures Compute the file size as seen by the given tid. This is usually easy since most files are not concurrently undergoing modification. This code assumes that the file is locked so that nothing is changing while it runs. process committed run list first Now look in modification lists to fix up for this tid We assume that higher level software has already acquired the locks needed (for existing files) or will acquire the locks needed upon return (for create). Unmonitored is OK since we are only looking for a good segment. IF nearToDid = NIL THEN { } ELSE { }; extend the file in the range given Loop for each allocated disk run Fix this Exported procedures for transactions All changes for a segment are presented at one time for a transaction. YggFileInternal.AllocAndFree must be called in ascending segment order by a transaction. All latches are set for a transaction at one time and they are done in a determined order => no deadlock. seg: LIST OF SegmentMetadata _ NIL; seg _ segMetaFromSegID[mods.first.run.segmentId]; IF seg = NIL THEN ERROR; YggFileInternal.StableAlloc[seg, mods.first.run, tid]; seg: LIST OF SegmentMetadata _ NIL; seg _ segMetaFromSegID[mods.first.run.segmentId]; IF seg = NIL THEN ERROR; YggFileInternal.StableFree[seg, mods.first.run, tid]; YggFileInternal.Free[seg, mods.first.run]; seg: LIST OF SegmentMetadata _ NIL; seg _ segMetaFromSegID[rl.first.segmentId]; YggFileInternal.StableFree[seg, rl.first, tid]; YggFileInternal.Free[seg, rl.first]; Internal procedures for transactions make sure that there are no nested transactions lurking in the data structure Internal red black procs PROC [data: UserData] RETURNS [Key] PROC [k: Key, data: UserData] RETURNS [Basics.Comparison] Initialization from Camelot and DID Map Process list of segments and build SegMetadataList page79: LONG POINTER TO YggCamelotSegment.PageZero; first time startup for this server: only one segment and it is uninitialized Must init page 0 and the NextDID page. The DIDMap will self initialize, but we have to init its page 0. The allocaion map is already all zeros, so it is initialized. [newTid: firstTimeTID, kernCode: kernCode] _ Camelot.TABegin[taPort: YggEnvironment.taPort, parentTid: YggEnvironment.nullTransID, transType: ttNvServerBased, raiseSignal: TRUE]; init page 0 init NextDID page init DIDMap page 0 try to commit [status: status, kernCode: kernCode] _ Camelot.TAEnd[taPort: YggEnvironment.taPort, tid: firstTimeTID, protocolType: ptTwoPhased, raiseSignal: TRUE]; IF kernCode = Mach.KernSuccess AND status = Camelot.ErSuccess THEN ERROR; Utility Initialization of module Ê&‘˜code•Mark outsideHeaderšœ™KšœB™BKšœ'™'—K™K™'K™šÏk ˜ Kšœœ ˜,Kšœœe˜rKšœœ˜3Kšœœq˜{Kšœœ(˜5Jšœ œJ˜\Kšœœœ˜KšœœM˜dKšœœœ˜Kšœœ˜1Kšœ œ;˜JKšœœ0˜DKšœœf˜sKšœœÄ˜ÙKšœ œ ˜Kšœ œ˜0KšœœF˜Z—K˜KšÑbln œœ˜Kšœ“˜šKšœ.˜5šœ˜K˜—head™Kšœœœ˜Kšœœœœ˜Kšœœœ˜+K˜š Ïnœœœ œœ˜?Kšœ™—K˜KšÏb œœ˜+Kš  œœœ!˜;K˜Kš œœ#˜8Kšœœ'˜@KšŸœœœ˜2K˜Kšœ œ˜Kšœ)˜)K˜Kšœ œ˜—šœ™šŸœœœ(œœ œœœœ˜™KšœŽ™Žš  œœœ˜šœ˜Kšœ˜K˜K˜—K˜—Kšœ5˜5Kšœ˜K˜ K˜K™—šŸœœœ'œ œœœœ˜œš  œœœ˜Kšœ˜K˜K˜—Kšœ)Ïc)˜RK˜ Kšœ˜Kšœ˜Kšœ˜Kšœœ˜/Kšœ˜K˜K™—šŸœœœ*˜=Kšœ˜Kšœœœ˜'Kšœ0˜0Kšœ ˜ Kšœ˜K˜K™—Kšœ˜Kšœ˜K˜š Ÿœœœ'œœ%œ!˜™Kšœ!œ˜BKšœ˜Kšœ˜Kšœ&œœ˜0Kšœ'˜'Kšœ˜K˜K™—šŸœœœC˜WKšœ˜Kšœ˜Kšœ'˜'Jšœœœ˜6Jšœœœ!˜RKšœ!œ)˜PKšœœ˜/Kšœ˜K˜K™—šŸ œœœG˜_Kšœ˜Kšœ\˜\Kšœ ˜ Kšœ˜K˜K˜—š Ÿœœœ\œœ˜Kšœ˜Kšœ/˜/K˜K˜K˜—šŸ œ œ œœ˜OKš œ œœœœH˜pK˜K˜—š Ÿ œœœœ œ˜OKšœ˜#K˜K˜—šŸ œ œ œœ˜OKš œ œœœœH˜pK˜K˜—šŸ œ œœ œ˜OKšœ˜#K˜K˜—šŸ œœ œœœœœ˜§K™Kšœ!˜!šœFœœ˜[Kšœ<˜Kšœœ˜!Kšœœ+˜5Kšœœ˜Kšœœ˜šœœœ¡˜>Kšœ ˜ Kšœ˜K˜—Kšœ œ˜Kšœœ˜Kšœœœ˜šœœœ¡˜;Kšœ œœ˜+Kšœœ˜Kšœ¡˜!K˜—šœœ¡˜KšœU˜UKšœ˜Kšœ$˜$K˜—Kšœ ˜ Kšœ˜—K˜—šœ ˜ Kšœœ œ˜$K˜—Kšœ˜—Kšœ˜—Kšœ˜K˜—Kšœ'œœ˜3Kšœ@˜@Kšœ œœ˜Kšœ˜—Kšœ˜—K˜K™—šŸ œœ5œ'œ˜zK™šJšœ)¡˜GJšœœœ ¡˜;Kšœœ ˜Kšœœœ˜'Kšœ˜Kšœ;˜;šœ@˜GKšœœ˜ Kšœ˜šœœ˜šœ˜Kšœœ˜&KšœI˜IKšœ œ˜K˜—šœ˜Kšœ˜Kšœ¡Ñbcl&˜/K˜—Kšœœ˜—Kšœ)œ ˜5Kšœ˜—K˜K˜—šŸ œœœ œœœœ5˜ŠKšœœ.™?šœ œœ™Kšœ ˜ š œœœ2œœ˜TKšœ5œ˜PKšœ˜—Kšœ¡¢)˜?K™—šœœ™Kšœ¡¢˜K™—K˜K˜—šŸœœy˜…Kšœ!œ˜BKš œœœœœ˜cšœœ˜Kšœ œœ˜0šœ3œ œ˜Nšœ5œ˜=Kšœœœ˜&Kš œ(œ œœœ˜LKšœ œ"˜2Kšœ˜K˜—šœœ˜Kšœœœ0˜WK˜—Kšœ˜—K˜—K˜K˜—šŸœœ œ œ$œœ2œœœ œœœœ˜ðKšœ#˜#Kšœœ ˜š  œœLœœœœœ˜K™"šœ ˜Kšœ ™ Kšœ˜Kšœ…˜…Kšœœœœ˜&Kšœ ˜ Kšœ,¡!˜MKšœ%˜%Kšœ.˜.Kšœ˜Kšœ¡&œ˜/—Kšœœ˜K˜—Kšœ+˜+Kš œ<œœœœ˜YKšœ ˜ K˜K˜—šŸœœ*˜8KšÐbl™K˜K˜——šœ$™$Kšœ œœœ ˜#šœ œœ˜Kšœ˜Kšœ"˜"K˜K˜—šŸ œœœ˜.šœ œ¡˜AKšœ˜Kšœ,˜,Kšœ˜Kšœ=˜=šœœœ˜Kšœ‹™‹K˜š œœT˜_Kšœœ˜šœ œœ˜4šœ œ˜(Kšœœ˜3Kšœ˜K˜—šœ œ˜(šœœœ˜Kšœ œ œ˜-Kšœ˜Kšœ˜—šœœ˜Kšœœ˜Kšœ œ œ˜,Kšœ˜Kšœ˜K˜—Kšœ˜K˜—Kšœ˜šœœ˜Kš œ œœ œ œ ˜BKšœœœ œ˜K˜—šœ ˜ Kšœœ˜!Kšœ*œœœ˜:Kšœ1˜1šœ*œœ˜>Kšœœœœ™#Kšœ+™+Kšœ/™/Kšœ$™$Kšœ;˜;Kšœ˜—K˜—šœ˜Kšœf˜fK˜—Kšœ˜—Kšœ˜—Kšœ˜—šœœœ˜)KšœG˜GKšœ3œœœ˜CK˜—Kšœœ˜ Kšœ˜—šœ œœ˜Kšœ˜šœ œœ˜4Kšœs˜sKšœ˜—K˜—Kšœœ˜K˜—Kšœ˜K˜—šœœ¡˜š œœœ˜#Kšœœ˜Kšœ˜Kšœ˜KšœA˜Ašœœœ˜Kšœ˜Kšœœ˜š œœœ œœœ¡˜aKšœ œœ œ˜5š œ œœIœ œœ¡0˜§šœ4œ¡$˜bš œ œœJœ œ˜xšœ:œ¡m˜±Kšœ œœ/˜?Kšœœœ?œœœœ˜lKšœ#˜#Kšœ œœ*˜@Kšœœ$˜)K˜—šœœ¡-˜6Kšœ˜K˜—Kšœ˜—Kšœ¡&˜,K˜—Kšœ˜Kšœ˜—Kšœ˜—K˜—K˜—Kšœ˜Kšœ œ ˜Kšœ8˜8Kšœ˜K˜K˜—K˜K™—šŸœœœ˜+šœ œ¡˜AKšœ˜Kšœ,˜,Kšœ˜KšœD˜DKšœ˜šœœœ˜Kšœ˜Kšœœ˜šœ#œœ˜7KšœV˜VKšœ˜—š œœœ œœœ¡˜aKšœ˜ Kšœ˜—K˜—K˜—K˜K˜—šŸœœœ˜*šœ œ¡˜AKšœ˜Kšœ,˜,Kšœ˜Kšœ=˜=šœœœ˜Kšœ˜Kšœœ˜š œœœ œœœ¡˜aš œ œœIœ œœ¡0˜§Kšœ œœ˜/š œœœ:œœ˜^šœ˜šœ ˜ Kšœœœ˜#Kšœ1˜1Kšœœœœ˜Kšœ*˜*K˜—šœ˜K˜—šœ ˜ K˜—Kšœ˜—Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—šœ#œœ˜7KšœV˜VKšœ˜—K˜—Kšœ;˜;Kšœ˜K˜—šœœ¡˜š œœœ˜(Kšœœ˜Kšœ˜Kšœ˜KšœA˜Ašœœœ˜Kšœ˜Kšœœ˜š œœœ œœœ¡˜aKšœ œœ œ˜5š œ œœIœ œœ¡0˜§šœ4œ¡'˜eš œœœ:œœ˜^šœ˜šœ ˜ Kšœœœ˜#Kšœœœ˜1Kšœ˜Kšœ*˜*K˜—šœ˜K˜—šœ ˜ K˜—Kšœ˜—Kšœ˜—Kšœ œœ*˜@Kšœœ$˜)Kšœ¡&˜,K˜—Kšœ˜Kšœ˜—Kšœ˜—K˜—K˜—Kšœ˜K˜—K˜——šœ$™$Kšœœœ ˜Kšœ œœ˜0Kšœœ¡*˜SK˜Kšœ œœ˜#šœœœ˜Kšœ˜Kšœœœ˜Kšœœœ ˜Kšœ˜Kšœ˜—K˜š œœœ˜=K™MKšœœ˜Kšœ˜Kšœ˜KšœA˜Ašœœœ˜Kšœ˜Kšœœ˜š œœœ œœ˜@š œ œœIœ œ˜uKšœœœ˜'Kšœ˜—Kšœ˜—K˜—K˜—š œœœ*˜JKšœœ˜Kšœ˜Kšœ˜KšœA˜Ašœœœ˜Kšœœ˜"Kšœœ˜*Kšœ œ˜Kšœ=˜=K˜—šœ˜Kšœ˜Kšœœ˜š œœœ œœ˜@Kšœœœ˜'Kšœ˜—Kšœ œ˜ K˜—K˜K™——šœ™šŸ œ˜&Jšœœ™#Jšœœ˜$Jšœ˜J˜J˜—•StartOfExpansion[]šŸ œ˜%Jšœœ™9Jšœœ˜(Jšœœ˜šœ˜Jšœ(œ ˜9Jšœ(œ˜6šœ˜ šœ˜Jšœ'œ ˜8Jšœ'œ˜5Jšœœ ˜—J˜——J˜—Icode2šœœ˜šŸœœœœ˜HMš œœœœ œ"œ˜ZMšœ˜Mšœ˜—šŸ œœœ˜)Mšœ˜Mšœ˜—J˜—™'šŸœœœœœœ œ˜tKšœ2™2Kšœ+œ˜/Kšœ œœ˜š œœœ2œœ˜RKšœ˜Kšœ˜Kšœ-˜-Kšœœ˜Kšœœ˜Kšœ œœ˜2Kšœ œœ™3Kšœ œ˜Kšœ;˜;Kšœ:˜:Kšœ˜Kšœœ7œ˜Þšœ˜ Kšœœ˜ Kšœ˜Kšœ.˜.Kšœ.˜.Kšœ˜—šœœ˜ Kš œœbœœœ˜|K˜—šœœ˜šœ˜ Kšœ.œœ˜;Jšœœ&œœ˜_Kšœµœgœ˜¦Kšœœ˜6Kšœ¬œœ˜ÅKšœœ˜5Kšœ“˜“š œœœœ˜Nšœ(˜2Kšœœ˜Kšœ ˜ šœ˜ š œœœœœœ˜EKšœœ˜ Kšœ ˜ šœ ˜Kšœœ œ˜/Kšœ œ˜Kšœ˜—K˜—Kšœê˜êK˜——Kšœ˜—Kšœœ”˜žK˜—K˜—KšœCœ2œ˜ƒKšœœœ,˜IKšœœ=˜BKšœ˜—šœœœ.œ˜UKš¡L™LK™§Kšœ˜Kšœ ˜ Kšœœ˜Kšœ˜Kšœ$˜$Kšœ&˜&Kšœ*˜*Kšœ˜Kšœ˜Jšœ˜JšœJ˜JKšœ œ˜KšœF˜FKšœ¬œ™²K™ Kšœ¢œDœ¡ ˜ÿšœ˜ Kšœ œœ˜2Kšœ˜Kšœ˜Kšœœ˜%Kšœ2˜2Kšœ+˜+Kšœ;˜;Kšœ!˜!KšœJ˜JKšœ˜K˜Kšœ3˜3Kšœ>˜>Kšœh˜hKšœ>˜>Kšœ<¡B˜~Kšœo˜oKšœI˜IKšœy˜yKšœµœtœ˜³Kšœ4œ˜LKšœ¬œœ˜ÅKšœ3œ˜KKšœ9˜9šœ˜KšœG˜GKšœ˜—šœT˜TKšœM˜MK˜—Kšœ¿˜¿Kšœ˜—Kšœœ œ,œ˜ÑKšœ’œ œQœ˜øKšœ™Kšœ¤œjœ¡œ¡˜­šœ˜ Kšœ œœ˜;Kšœœ˜-Kšœ+˜+Kšœ˜Kšœ7˜7Kšœ˜—Kšœœ`œ˜÷Kšœ’œ‡œ˜ Kšœ™Kšœ¨œjœ¡œ¡˜²šœ˜ Kšœ œœ$˜@Kšœœ˜1Kšœ-˜-Kšœ˜Kšœ)˜)Kšœ˜—Kšœœ`œ˜÷Kšœ’œ‡œ˜ K˜J™ Kšœœ™•Kšœœœœ™IKšœ\˜\Kšœœœ˜Kšœ€œ˜†Kšœ‚œ˜ˆKšœ†œ˜ŒKšœ-˜-K˜—šœœ¡˜!Kšœœœ¡G˜cšœ6œœ˜Kšœ"œ¡˜Kšœœ˜Kšœ˜Kšœ2˜2Kšœœœ˜%Kšœœ^œ˜…šœ˜ Kšœ œ˜$Kšœ,œœ˜9Kšœœœ˜$Kšœ œCœ˜YK˜—Kšœ{œ˜Kšœ^œ˜eKšœR˜RKšœ˜K˜—Kšœ˜—K˜K˜—šŸœœœœ2˜Wšœ6œœ˜Kšœ'œ0œ˜aKšœ˜Kšœ˜Kšœ"œœ˜/Kšœœhœ˜šœ˜ Kšœ œœ˜;Kšœ œ3˜EKšœœ˜&Kšœ,œœ˜9K˜—Kšœu˜uKšœ˜K˜—Kšœ˜—K˜K˜—K˜—™šŸœœœœœ œ œ˜JKšœ œœ˜Kšœœœ˜Kšœ œ ˜šœ˜Kšœœœ˜,KšœH˜HKšœ#œ˜7Kšœœ˜3Kšœ'˜'Kšœ˜—K˜K˜—K˜—™šŸœœ˜KšœU˜UK˜—K˜K˜—Kšœ˜—…—‘|Ãõ