-- File: VMPrivate.mesa
-- Last edited by Levin:  12-Jul-82 14:57:02

  AltoDefs USING [MaxMDSPage],
  FileDefs USING [FileHandle, FSInstance, Operations, PageNumber],
  VMDefs USING [
    CacheIndex, FileHandle, FileSystemType, OpenOptions, Page, PageAddress,
    PageByteIndex, PageNumber, Position, Problem];



  -- Types and Related Constants --

  FileSystem: TYPE = POINTER TO FSInstance;

  FSInstance: TYPE = RECORD [ops: FileDefs.Operations, instance: FileDefs.FSInstance];

  ObjectType: TYPE = {page, file};

  Object: TYPE = RECORD [
    body: SELECT tag: ObjectType FROM
      file => [
	seal: VMFileSeal,
	useCount: CacheIndex,
	link: FileHandle,
	fh: FileDefs.FileHandle,
	fs: FileSystem,
	cachePages: CacheIndex,
	options: VMDefs.OpenOptions,
	altoFile: BOOLEAN,
	openCount: [0..37B]],  -- pick a convenient size based on size of other fields
      page => [
	state: PageState,
	age: LRUInfo,
	dirty: BOOLEAN,
	recordNextVda: BOOLEAN,
	errorStatus: VMDefs.Problem,
	buffer: MDSPageNumber,
	pageStable: CONDITION,
	file: FileHandle,
	beingTaken: BOOLEAN,
	page: FileDefs.PageNumber,
	hashLink: CacheIndex,
	useCount: [0..377B]  -- pick a convenient size based on size of other fields

  FileObject: TYPE = file Object;
  PageObject: TYPE = page Object;

  ObjectHandle: TYPE = POINTER TO Object;
  FileHandle: TYPE = POINTER TO FileObject;
  PageHandle: TYPE = POINTER TO PageObject;

  -- File Operations --

  fsOps: ARRAY VMDefs.FileSystemType OF FileDefs.Operations;

  Writable: PROCEDURE [file: FileHandle] RETURNS [BOOLEAN] = INLINE
    {RETURN[file.options ~= oldReadOnly]};

  -- Cache Management --

  -- Types and Related Constants --

  CacheIndex: TYPE = VMDefs.CacheIndex;

  nilCacheIndex: CacheIndex = 255;

  HandleTable: TYPE = RECORD [SEQUENCE nHandles: CacheIndex OF PageHandle];

  LRUInfo: TYPE = {old, new};

  PageByteCount: TYPE =
    [FIRST[VMDefs.PageByteIndex] + 1..LAST[VMDefs.PageByteIndex] + 1];

  -- Procedures and Signals --

  InitializeVMCache: PROCEDURE [min, max: CacheIndex];
  -- establishes initial invariant for VMCache.

  FinalizeVMCache: PROCEDURE;
  -- cleans up VMCache.

  InitializeVMCacheMgr: PROCEDURE [h: POINTER TO HandleTable, min, max: CacheIndex];
  -- establishes initial invariant for VMCacheMon.

  FinalizeVMCacheMgr: PROCEDURE;
  -- cleans up VMCacheMon.

  AcquireCache: PROCEDURE;
  -- waits for the cache to become stable, marks it unstable, and returns.

  ReleaseCache: PROCEDURE;
  -- resets the cache to a stable state (and awakens anyone waiting for it).

  AllocateCacheIndex: PROCEDURE RETURNS [CacheIndex];
  -- allocates a slot in the cache and returns its CacheIndex.  This index will
  -- correspond to an allocated page object with page.state = unstable,
  -- page.useCount = 0, page.buffer ~= nilMDSPage, and page.pageStable initialized,
  -- but whose remaining fields are undefined.

  IndexToHandle: PROCEDURE [index: CacheIndex] RETURNS [PageHandle];
  -- maps a CacheIndex to the PageHandle for the corresponding object.

  LookupInHashTable: PROCEDURE [addr: VMDefs.PageAddress]
    RETURNS [index: CacheIndex];
  -- looks up the given address to discover whether there exists a corresponding
  -- page already in the cache.  If so, its CacheIndex is returned, if not,
  -- nilCacheIndex is returned.

  EnterInHashTable: PROCEDURE [page: PageHandle];
  -- enters the given page into the hash table.

  EnterInPageTable: PROCEDURE [page: MDSPageNumber, index: CacheIndex];
  -- establishes a correspondance between the (MDS) 'page' and slot 'index' in
  -- the cache.

  RemovePageFromTables: PROCEDURE [page: PageHandle];
  -- eliminates 'page' from the mapping tables, and disassociates it from its file.

  EnumerateCachePagesInFile: PROCEDURE [
    file: VMDefs.FileHandle,
    proc: PROCEDURE [page: PageHandle] RETURNS [found, unmap: BOOLEAN]]
  -- enumerates all cache slots containing pages belonging to 'file'.  If 'proc'
  -- returns 'unmap' = TRUE, RemovePageFromTables will be called (providing that
  -- there are no other users of the page.  If 'found' = TRUE, the enumeration
  -- terminates.  Note: This procedure supplies its own cache mutual exclusion.

  -- Page Management --

  -- Types and Related Constants --

  MDSPageNumber: TYPE = [0..AltoDefs.MaxMDSPage];

  nilMDSPage: MDSPageNumber = 0;

  PageState: TYPE = {stable, unstable};

  PageByte: TYPE = MACHINE DEPENDENT RECORD [p: MDSPageNumber, b: [0..255]];

  WaitReason: TYPE = {reading, writing};

  -- Procedures and Signals --

  InitializeVMPageMgr: PROCEDURE;
  -- establishes initial invariant for VMPageMon.

  FinalizeVMPageMgr: PROCEDURE;
  -- cleans up VMPageMon.

  AcquirePage: PROCEDURE [page: PageHandle];
  -- waits for 'page' to become stable, marks it unstable, and returns.

  ReleasePage: PROCEDURE [page: PageHandle];
  -- resets 'page' to a stable state (and awakens anyone waiting for it).

  WaitUntilStable: PROCEDURE [page: PageHandle, why: WaitReason]
    RETURNS [PageState];
  -- waits for the argument page to become stable, potentially reporting transmission
  -- errors.  If the return value is 'stable', 'page' is stable and the previous
  -- transfer completed successfully.  If the return value is 'unstable', 'page' is
  -- unstable (i.e., an implicit AcquirePage has been done) and the previous transfer
  -- was never started (i.e., the DiskIODefs.CompletionStatus was 'neverStarted').  If
  -- the previous transfer had a permanent transmission error, either
  -- VMDefs.CantReadBackingStore (if why = reading) or VMDefs.CantWriteBackingStore
  -- (if why = writing) is raised.

  ValidatePageAddress: PROCEDURE [page: PageHandle]
    RETURNS [extending: BOOLEAN, newLength: VMDefs.Position];
  -- ensures that page.addr is reasonable for page.file.

  -- I/O Management --

  InitializeVMIO: PROCEDURE;
  -- initializes the I/O handling routines.

  -- finalizes the I/O handling routines.

  WritePageToFS: PROCEDURE [page: PageHandle, wait: BOOLEAN];
  -- initiates a write to the file system of 'page'.  page.state is assumed to be
  -- unstable and remains so while the transfer is underway.  If 'wait' is TRUE,
  -- WritePageToFS will wait until the write operation completes before returning,
  -- at which time the page will be placed in the 'stable' state.  If 'wait' if FALSE,
  -- WritePageToFS returns immediately after initiating the operation.  The page is
  -- marked clean (i.e., page.dirty becomes FALSE) when the write completes
  -- successfully.

  -- File Manipulation --

  -- Types and Related Constants --

  VMFileSeal: TYPE = [0..77B];

  openSeal: VMFileSeal = 51B;
  underwaySeal: VMFileSeal = 35B;
  closedSeal: VMFileSeal = 26B;

  -- Procedures and Signals --

  InitializeVMFile: PROCEDURE [maxCachePages: CacheIndex];
  -- establishes initial invariant for VMFile.

  FinalizeVMFile: PROCEDURE;
  -- cleans up VMFile.

  ValidateFile: PROCEDURE [file: VMDefs.FileHandle] = INLINE
    -- ensures that 'file' is a plausible file handle.
    {IF LOOPHOLE[file, FileHandle].seal ~= openSeal THEN ERROR InvalidFile};

  ValidatePageNumber: PROCEDURE [page: VMDefs.PageNumber] = INLINE
    -- ensures that 'page' is a plausible page number.
    {IF CARDINAL[page] > 77777B THEN ERROR InvalidPageNumber};

  InvalidFile: ERROR;
  -- raised by ValidateFile.

  InvalidPageNumber: ERROR;
  -- raised by ValidatePageNumber 

  -- Miscellaneous Procedures and Signals --

  AddressToHandle: PROCEDURE [address: VMDefs.Page] RETURNS [PageHandle];
  -- maps an (MDS) address to the PageHandle for the corresponding object.

  HandleToAddress: PROCEDURE [page: PageHandle] RETURNS [VMDefs.Page];
  -- maps a PageHandle to the (MDS) address of its corresponding buffer.

  AddressToMDSPage: PROCEDURE [address: POINTER] RETURNS [MDSPageNumber] = INLINE
    -- maps an (MDS) address to the corresponding MDSPageNumber.
    {RETURN[LOOPHOLE[address, PageByte].p]};

    -- maps a MDSPageNumber to the corresponding (MDS) address.
    {RETURN[LOOPHOLE[PageByte[p: p, b: 0]]]};

  loggingEnabled: BOOLEAN;
  -- indicates whether statistics logging is enabled.

  clobberCatcherEnabled: BOOLEAN;
  -- indicates whether hardware write-protect is to be used to detect clobbers.

  WriteEnable: PROCEDURE [page: MDSPageNumber];
  -- clears write-protection on the indicated page.

  WriteProtect: PROCEDURE [page: MDSPageNumber];
  -- sets write-protection on the indicated page.