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 = {
FileBackdoor.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;
};