YggFileImpl.mesa
Copyright Ó 1988, 1989 by Xerox Corporation. All rights reserved.
Bob Hagmann May 15, 1989 3:45:45 pm PDT
Handle all operations on FileHandles's.
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
Data types and variables
ROPE: TYPE ~ Rope.ROPE;
DID: PUBLIC TYPE ~ REF DIDRep;
DIDRep: PUBLIC TYPE ~ YggDIDPrivate.DIDRep;
Error: PUBLIC ERROR[why: YggFile.Reason, diskPage: INT] = CODE;
exported to YggFile
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;
Exported procedures for files
Open: PUBLIC PROC [runList: YggDIDMap.RunList, byteSize: INT, did: YggDID.DID, fileUse: ATOM, verifyLeaderNow: BOOL] RETURNS [file: FileHandle ← NIL] ~ {
Open the file with this run list. If verifyLeaderNow is true, verify that the leader matches the runList now, instead of delaying the access.
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] ~ {
Info about the server
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;
};
Internal procedures
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] ~ {
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.
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: BOOLFALSE] RETURNS [runList: YggDIDMap.RunList ← NIL] ~ {
lastFrom: INT ← from + nPages - 1;
process committed run list first
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;
Now look in modification lists to fix up for this tid
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: BOOLFALSE;
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] ~ {
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).
uncommittedSize: YggFile.PageCount ← 0 ; -- smallest run we will accept
minRun: INTMIN[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] ~ {
Unmonitored is OK since we are only looking for a good segment.
IF nearToDid = NIL THEN {
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"
}
ELSE {
-- 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: BOOLTRUE, 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: BOOLFALSE] = TRUSTED {
extend the file in the range given
WHILE amount > 0 DO
Loop for each allocated disk run
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] = {
Fix this
};
Exported procedures for transactions
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 {
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.
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 => {
seg: LIST OF SegmentMetadata ← NIL;
seg ← segMetaFromSegID[mods.first.run.segmentId];
IF seg = NIL THEN ERROR;
YggFileInternal.StableAlloc[seg, mods.first.run, tid];
addOp[stableAlloc, mods.first.run.segmentId, mods.first.run];
YggDIDMap.AddRuns[fH.first.did, tid, fH.first.fileUse, LIST[mods.first.run]];
};
removePages => {
seg: LIST OF SegmentMetadata ← NIL;
segsegMetaFromSegID[mods.first.run.segmentId];
IF seg = NIL THEN ERROR;
YggFileInternal.StableFree[seg, mods.first.run, tid];
YggFileInternal.Free[seg, mods.first.run];
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
seg: LIST OF SegmentMetadata ← NIL;
seg ← segMetaFromSegID[rl.first.segmentId];
YggFileInternal.StableFree[seg, rl.first, tid];
YggFileInternal.Free[seg, rl.first];
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;
segsegMetaFromSegID[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[];
};
};
Internal procedures for transactions
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: BOOLFALSE,
files: LIST OF FileHandle,
opsToDo: OpsList ← NIL
];
checkForOnlyTopLevelTrans: ENTRY PROC [tid: Camelot.tidT] ~ {
make sure that there are no nested transactions lurking in the data structure
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];
};
};
Internal red black procs
GetKeyProc: RedBlackTree.GetKey = {
PROC [data: UserData] RETURNS [Key]
fileTrans: FileTrans ← NARROW[data];
RETURN[ fileTrans.btidForFT ];
};
CompareProc: RedBlackTree.Compare = {
PROC [k: Key, data: UserData] RETURNS [Basics.Comparison]
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;
};
Initialization from Camelot and DID Map
InitializeFile: PUBLIC PROC [segmentList: LIST OF YggCamelotSegment.SegPort] RETURNS [firstTime : BOOL ← FALSE ]~ {
Process list of segments and build SegMetadataList
lastSegmentMetadata: SegmentMetadataList ← NIL;
gotMetaData: BOOLFALSE;
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;
page79: 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 {
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.
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: 1, version: 1, nextRecord: [zero, 4096, 0]];
firstTime ← TRUE;
firstTimeTID ← YggTransaction.CreateTrans[YggEnvironment.nullTransID];
[newTid: firstTimeTID, kernCode: kernCode] ← Camelot.TABegin[taPort: YggEnvironment.taPort, parentTid: YggEnvironment.nullTransID, transType: ttNvServerBased, raiseSignal: TRUE];
init page 0
[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];
init NextDID page
[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];
init DIDMap page 0
[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];
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;
[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: DIDNEW[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;
};
Utility
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: CARDMIN[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;
};
Initialization of module
Init: PROC ~ {
transactionToFileMap ← RedBlackTree.Create[getKey: GetKeyProc, compare: CompareProc];
};
Init[];
END.