-- File: VMPageOps.mesa
-- Last edited by Levin:  30-Oct-81 16:21:03

DIRECTORY
  VMDefs USING [Page],
  VMPrivate USING [
    AcquirePage, AddressToHandle, FileObject, PageHandle, ReleasePage,
    ValidatePageAddress, WaitUntilStable, Writable, WritePageToFS];

VMPageOps: PROGRAM
  IMPORTS VMPrivate
  EXPORTS VMDefs =

  BEGIN OPEN VMPrivate;


  -- This monitor synchronizes access to PageObjects by main memory address, and
  -- therefore is implicitly concerned with pages for which useCount > 0.  For this
  -- reason, the AddressToHandle mapping is assumed to be stable and can be safely used
  -- outside the cache monitor.


  -- Miscellaneous Declarations --

  BadPage: ERROR = CODE;
  FileReadOnly: ERROR = CODE;


  -- Procedures and Types Exported to VMDefs --

  FileObject: PUBLIC TYPE = VMPrivate.FileObject;

  Mark: PUBLIC PROCEDURE [page: VMDefs.Page] =
    BEGIN
    p: PageHandle;
    [] ← ValidatePageAddress[AddressToHandle[page]];
    p ← AcquireValidatedPage[page, FALSE];
    CheckReadOnly[p];
    p.dirty ← TRUE;
    ReleasePage[p];
    END;

  Start: PUBLIC PROCEDURE [page: VMDefs.Page] =
    BEGIN
    p: PageHandle ← AcquireValidatedPage[page];
    IF p.dirty THEN WritePageToFS[page: p, wait: FALSE] ELSE ReleasePage[p];
    END;

  Wait: PUBLIC PROCEDURE [page: VMDefs.Page] =
    BEGIN
    p: PageHandle ← AddressToHandle[page];
    IF p.useCount = 0 THEN ERROR BadPage;
    IF p.file = NIL THEN RETURN;
    IF WaitUntilStable[p, writing] = unstable THEN
      BEGIN -- write was never started; retry it synchronously
      ReleasePage[p];
      StartWait[page];
      END;
    END;

  MarkStart: PUBLIC PROCEDURE [page: VMDefs.Page] =
    BEGIN
    p: PageHandle ← AcquireValidatedPage[page];
    CheckReadOnly[p];
    p.dirty ← TRUE;
    WritePageToFS[page: p, wait: FALSE];
    END;

  StartWait: PUBLIC PROCEDURE [page: VMDefs.Page] =
    BEGIN
    p: PageHandle ← AcquireValidatedPage[page];
    IF p.dirty THEN WritePageToFS[page: p, wait: TRUE] ELSE ReleasePage[p];
    END;

  MarkStartWait: PUBLIC PROCEDURE [page: VMDefs.Page] =
    BEGIN
    p: PageHandle ← AcquireValidatedPage[page];
    CheckReadOnly[p];
    p.dirty ← TRUE;
    WritePageToFS[page: p, wait: TRUE];
    END;


  -- Internal Procedures --

  AcquireValidatedPage: PROCEDURE [page: VMDefs.Page, mustHaveFile: BOOLEAN ← TRUE]
    RETURNS [p: PageHandle] =
    -- does its best to check that 'page' is a valid address at this instant and
    -- returns the associated page object, marked unstable.
    BEGIN
    AcquirePage[p ← AddressToHandle[page]];
    IF p.useCount > 0 AND (~mustHaveFile OR p.file ~= NIL) THEN RETURN;
    ERROR BadPage;
    END;

  CheckReadOnly: PROCEDURE [p: PageHandle] =
    -- ensures that 'p' comes from a file that permits writing.
    BEGIN
    IF p.file = NIL OR Writable[p.file] THEN RETURN;
    ERROR FileReadOnly;
    END;


  END.