// IfsLeafVMemClose.bcpl - Leaf VMem Close - SWAPPABLE
// Copyright Xerox Corporation 1979, 1980, 1982

// Last modified September 17, 1982  8:55 AM by Taft

get ecBadFileMap, ecBrokenLeafQueue, ecBrokenLeafVMemQueue,
 ecCantPurgeVFile from "IfsLeafErrors.decl";
get FD, oldPageNumber from "IfsDirs.decl";
get fillInDA, eofDA from "Disks.d";
get "IfsLeaf.decl";
get "IfsLeafVMem.decl";
get "IfsIsf.d";

external
[
//outgoing procedures
FreeLeafVMem; LeafPurgeVFile;

//incoming procedures
Allocate; CloseIFSFile; Dismiss; FMapChecksum; Free; FreeFmap;
IFSError; IndexedPageIO; InsertAfter; LookupFmap;
Min; MoveBlock; ReadCalendar; SnarfBuffers; UnsnarfBuffers; TransferLeaderPage;
Unqueue;

//incoming statics
leafVMI; sysZone;
]

static fmapTruncations = 0;

//----------------------------------------------------------------------------
let FreeLeafVMem(fd, lvmd) = valof
//----------------------------------------------------------------------------
[
let refs = lvmd>>LVMD.refCount - 1;
if refs ne 0 then
   [
   lvmd>>LVMD.refCount = refs;
   CloseIFSFile(fd); resultis false;
   ]

// There is a potential problem here if multiple Leaf contexts
// are used and somebody tries to grab the lvmd being flushed.
LeafPurgeVFile(lvmd);
unless Unqueue(lv leafVMI>>LeafVMI.lvmdQueue, lvmd) then
 IFSError(ecBrokenLeafVMemQueue);

let buffer = Allocate(sysZone, wordsPerPage);
let lastPage = lvmd>>LVMD.lastAddress.high lshift logPagesPerWord +
 lvmd>>LVMD.lastAddress.low rshift logBytesPerPage + 1;
let fmap = lv lvmd>>LVMD.fmap;
let lastPageDA = nil;
   [
   lastPageDA = LookupFmap(fmap, lastPage);
   switchon lastPageDA into
      [
      case fillInDA:
         IndexedPageIO(fmap, lastPage, buffer, 1, 0); endcase;
      case eofDA:
         IFSError(ecBadFileMap); endcase;
      default: break;
      ]
   ] repeat
TransferLeaderPage(fd, buffer);
buffer>>ILD.hintLastPageFa.da = lastPageDA;
buffer>>ILD.hintLastPageFa.pageNumber = lastPage;
buffer>>ILD.hintLastPageFa.charPos =
 lvmd>>LVMD.lastAddress.low & bytesPerPage-1;

// Write the filemap...
let ildMap = lv buffer>>LeafILD.fmap;
let last = Min(fmap>>FM.last-mapoffset, maxLenOldFMap-lenMapEntry);
MoveBlock(ildMap, lv (fmap>>FM.fmap)>>FM.map, last+lenMapEntry);
if last ne fmap>>FM.last-mapoffset then
   [
   fmapTruncations = fmapTruncations + 1;
   // make sure map ends with fillInDA
   ildMap!(last+1) = fillInDA;
   ]
// Write the filemap info...
buffer>>LeafILD.fmapLast = last;
buffer>>LeafILD.fmapChecksum = FMapChecksum(ildMap, last);
ReadCalendar(lv buffer>>LeafILD.fmapWritten);
MoveBlock(lv buffer>>LeafILD.fmapFP, lv fmap>>FM.fp, lFP);
buffer>>LeafILD.fmapSeal = version;

TransferLeaderPage(fd, buffer, true);
Free(sysZone, buffer); FreeFmap(fmap); Free(sysZone, lvmd);
// This is a bit kludgy, but since this is called from places other than the
//  Leaf Ctx, CtxRunning>>RSCtx.userInfo is not always valid.  However,
//  upon examination it seems that CloseIFSFile only refers to CtxRunning
//  inside CreateFD.  But this only happens when it is creating an FD to a
//  directory in which case CtxRunning is not used after all.
CloseIFSFile(fd, lastPage - fd>>FD.oldPageNumber);
resultis true;
]

//----------------------------------------------------------------------------
and LeafPurgeVFile(lvmd) be
//----------------------------------------------------------------------------
[
for leafPage = lv leafVMI>>LeafVMI.pages↑1 by lenLeafPage to
 lv leafVMI>>LeafVMI.pages↑lastLeafPage do
   [
   let tries = 0;
   while leafPage>>LeafPage.probe & lvmd eq leafPage>>LeafPage.lvmd &
    leafPage>>LeafPage.address ne 0 do
      [
      let cPage = leafPage>>LeafPage.address rshift logVMPageLength;
      // Snarf the entire page group so as to keep all VMem pages of the group
      // together on the available list.
      test SnarfBuffers(cPage, 1 lshift logVPagesPerLeafPage, 0) eq 0
         ifso
            [
            // Page is locked, perhaps because it is being swapped out already.
            // Allow up to 10 seconds for this complete.
            // Note: after waiting, be sure to re-evaluate whether or not
            // we are still interested in flushing this LeafPage, since it may
            // have gotten flushed by some other VMem activity in the meantime.
            tries = tries + 1;
            if tries gr 100 then IFSError(ecCantPurgeVFile);
            Dismiss(10);
            ]
         ifnot
            [ UnsnarfBuffers(cPage, 1 lshift logVPagesPerLeafPage); break ]
      ]
   ]

if lvmd>>LVMD.written then
   [
   // All this can be optimized so that "written" only applies to last page.
   let lastPage = lvmd>>LVMD.lastAddress.high lshift logPagesPerWord +
    lvmd>>LVMD.lastAddress.low rshift logBytesPerPage + 1;
   let lastByte = lvmd>>LVMD.lastAddress.low & bytesPerPage-1;
   let fmap = lv lvmd>>LVMD.fmap;
   let buffer = Allocate(sysZone, wordsPerPage);
   if lastByte ne IndexedPageIO(fmap, lastPage, buffer, 1, 0) then
    IndexedPageIO(fmap, lastPage, buffer, 1, -1, lastByte);
   lvmd>>LVMD.written = false;
   Free(sysZone, buffer);
   ]
]