FSFileSpaceImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Russ Atkinson, November 7, 1984 11:13:05 am PST
DIRECTORY
BasicTime USING [GMT, Period],
BTree USING [Error],
File USING [Delete, Error, FP, Handle, Open, SetFlusher, Volume],
FS USING [Error],
FSBackdoor USING [EntryPtr, EntryType, MakeFName, Version],
FSDir USING [AcquireOldGName, DeleteEntry, EnumerateEntries],
FSFileOps USING [GetNameBodyAndVersion, VolumeDesc],
FSLock USING [ActiveFile, ReleaseRecord],
FSReport USING [ReportRemote],
Rope USING [ROPE, Text];
FSFileSpaceImpl: CEDAR MONITOR
IMPORTS BasicTime, BTree, File, FS, FSBackdoor, FSDir, FSFileOps, FSLock, FSReport
EXPORTS FSFileOps
= {
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
Space management variables and types
UseRecord: TYPE = RECORD[next: REF UseRecord, fp: File.FP, used: GMT];
LRU: TYPE = RECORD[
vDesc: FSFileOps.VolumeDesc ← NIL,
first: REF UseRecord ← NIL,
killed, -- times killed
sorts, -- number of lru lists made since last killed
count, -- initial length of last lru list made
cumCount, flushed, error, deleted, superseded, open: INT ← 0 -- since killed
];
lru: LRU;
Exported to FSFileOps
RegisterVolumeFlusher: PUBLIC ENTRY PROC [svDesc: FSFileOps.VolumeDesc] = {
ENABLE UNWIND => NULL;
IF svDesc = NIL
THEN { -- kill any existing volume flusher
IF lru.vDesc # NIL
THEN KillFlusher[]; -- already is a volume flusher
}
ELSE { -- establish a new volume flusher
lru.vDesc ← svDesc;
File.SetFlusher[lru.vDesc.vol, MakePagesAvailable, NIL];
};
};
RecordUsage: PUBLIC PROC [fp: File.FP, time: GMT] = {};
Internal procedures
MakePagesAvailable: ENTRY PROC [volume: File.Volume, nPages: INT, clientInfo: REF] RETURNS [ok: BOOL] = {
ENABLE UNWIND => NULL;
ok ← FALSE;
IF lru.vDesc # NIL THEN {
flusher is turned on
ENABLE BTree.Error, File.Error => { KillFlusher[]; CONTINUE };
ok ← FlushAnother[];
IF NOT ok THEN ok ← FlushAnother[]; -- make another lru list and try again
};
};
KillFlusher: INTERNAL PROC = {
File.SetFlusher[lru.vDesc.vol, NIL, NIL];
lru ← [NIL, NIL, lru.killed+1, 0, 0, 0, 0, 0, 0, 0, 0];
};
FlushAnother: INTERNAL PROC RETURNS [BOOL] = {
IF lru.first = NIL THEN {
build a new LRU list
GetFPandUsed: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL] = UNCHECKED {
WITH e: entry^ SELECT FROM
cached => {
lru.first ← NEW [ UseRecord ← [lru.first, e.fp, e.used] ];
lru.count ← lru.count+1;
};
ENDCASE => ERROR;
RETURN [accept: FALSE, stop: FALSE];
};
lru.count ← 0;
FSDir.EnumerateEntries[lru.vDesc, "[", all, GetFPandUsed, NIL];
lru.first ← LRUOrder[lru.first];
lru.cumCount ← lru.cumCount + lru.count;
lru.sorts ← lru.sorts+1;
};
DO
victim: REF UseRecord = lru.first;
IF victim = NIL THEN EXIT; -- time to stop
lru.first ← victim.next;
victim.next ← NIL;
IF TryVictim[victim] THEN RETURN[TRUE];
ENDLOOP;
RETURN [FALSE];
};
TryVictim: INTERNAL PROC [v: REF UseRecord] RETURNS [BOOL] = {
fName: ROPE;
t: FSBackdoor.EntryType;
dirUsed: GMT;
aF: FSLock.ActiveFile;
entryFP: File.FP;
h: File.Handle;
nameBody: Rope.Text;
version: FSBackdoor.Version;
h ← File.Open[lru.vDesc.vol, v.fp
! File.Error => {
IF why = unknownFile
THEN lru.deleted ← lru.deleted+1
ELSE lru.error ← lru.error+1;
GOTO failed;
} ];
[nameBody, version] ← FSFileOps.GetNameBodyAndVersion[h
! FS.Error => {
IF error.code = $badFP
THEN lru.deleted ← lru.deleted+1
ELSE lru.error ← lru.error+1;
GOTO failed;
} ];
[t, dirUsed, entryFP, aF] ← FSDir.AcquireOldGName[lru.vDesc, nameBody, version];
IF t = notFound THEN { lru.deleted ← lru.deleted+1; GOTO failed };
IF aF = NIL OR aF.fileLock # none THEN {
FSLock.ReleaseRecord[aF];
lru.open ← lru.open+1;
GOTO failed;
};
IF entryFP # v.fp THEN {
wierd case, but I think it can happen
FSLock.ReleaseRecord[aF];
lru.deleted ← lru.deleted+1;
GOTO failed;
};
IF BasicTime.Period[from: dirUsed, to: v.used ] < 0 THEN {
FSLock.ReleaseRecord[aF];
lru.superseded ← lru.superseded+1;
GOTO failed;
};
fName ← FSBackdoor.MakeFName[nameBody, version];
FSReport.ReportRemote[startFlushing, fName];
FSDir.DeleteEntry[lru.vDesc, nameBody, version
! BTree.Error, File.Error => {
FSLock.ReleaseRecord[aF]; FSReport.ReportRemote[endFlushing, fName] }
];
FSLock.ReleaseRecord[aF];
File.Delete[ h
! File.Error => {
lru.error ← lru.error+1; FSReport.ReportRemote[endFlushing, fName]; GOTO failed }
];
FSReport.ReportRemote[endFlushing, fName];
lru.flushed ← lru.flushed+1;
RETURN [TRUE];
EXITS failed => RETURN [FALSE];
};
LRUOrder: PROC [list: REF UseRecord] RETURNS [first: REF UseRecord] = {
Merge: PROC [list1, list2: REF UseRecord] RETURNS [mergeStart: REF UseRecord] = {
end: REF UseRecord;
IF list1 = NIL THEN {mergeStart ← list2; RETURN};
IF list2 = NIL THEN {mergeStart ← list1; RETURN};
IF BasicTime.Period[from: list1.used, to: list2.used] > 0
THEN {mergeStart ← list1; list1 ← list1.next}
ELSE {mergeStart ← list2; list2 ← list2.next};
end ← mergeStart;
DO
IF list1 = NIL THEN {end.next ← list2; EXIT};
IF list2 = NIL THEN {end.next ← list1; EXIT};
IF BasicTime.Period[from: list1.used, to: list2.used] > 0
THEN {end.next ← list1; list1 ← list1.next}
ELSE {end.next ← list2; list2 ← list2.next};
end ← end.next;
ENDLOOP;
};
maxLists: CARDINAL = 22; -- bits in word-address - 2
lists: ARRAY [0 .. maxLists] OF REF UseRecord ← ALL [NIL];
UNTIL list = NIL DO
i: CARDINAL ← 0;
first ← list;
list ← list.next;
first.next ← NIL;
UNTIL lists[i] = NIL DO
first ← Merge[first, lists[i]];
lists[i] ← NIL;
i ← i+1;
ENDLOOP;
lists[i] ← first;
ENDLOOP;
first ← NIL;
FOR i: CARDINAL IN [0 .. maxLists] DO
first ← Merge[first, lists[i]];
lists[i] ← NIL;
ENDLOOP;
};
}.