-- File: DiskIODefs.mesa
-- Last edited by Levin:  24-Nov-80 13:30:15

DIRECTORY
  AltoDefs USING [MaxFilePage, PageNumber],
  AltoFileDefs USING [DISK, eofDA, fillinDA, SN, vDA, vDC];

DiskIODefs: DEFINITIONS =

  BEGIN

  -- A brief discourse on the design and intended use of this interface:
  
  -- This interface is intended to be a functional replacement for the DiskDefs
  -- interface of Alto/Mesa, which in turn is a version of the low-level parts of
  -- the original BCPL BFS package.  The major difference is that DiskIODefs, as
  -- distinct from Mesa's DiskDefs, offers asynchronous disk transfers.  The package
  -- is capable of queuing multi-page transfer requests at once, and can take
  -- advantage of the chaining tricks used in the BFS.  However, this interface does
  -- not force its clients to understand how disk control blocks are constructed and
  -- manipulated.  Thus, the BFS concepts of 'CBZone' and a 'CB' are not needed here.
  -- All disk transfer requests that this interface supports are expressed in a
  -- 'DiskRequest', defined below.  The implicit assumptions in the design of this
  -- structure are the following (it may be helpful to refer to the definition of
  -- 'DiskRequest' while reading this list):
  
  -- 1)	 Each DiskRequest transfers a sequence of consecutive pages within a single
  --     file.  (Exception:  in the case of operations used for file extension, some
  --     pages may not (yet) be part of the file.)
  -- 2)	 A file is uniquely identified by its file ID (FID), and a page within the
  --     file is uniquely identified by a page number.  These are the standard
  --     conventions of the Alto file system.
  -- 3)	 Pages are transfered strictly in the order that they appear in the 'xfers'
  --     array, that is, in order of increasing page number within the file.
  -- 4)	 A CompletionProcedure will be invoked as each page transfer completes.  (For
  --     an exception, see item 6, below.)  If the transfer produces an error, the
  --     transfer will be retried an appropriate number of times.  Thus, the
  --     CompletionProcedure will be invoked only when the transfer has either completed
  --     successfully or when a permanent error has occurred.  (However, a permanent
  --     error causes all subsequently queued transfers to be inhibited.  These
  --     inhibited requests will cause the completion procedure to be invoked with
  --     status = neverStarted.)
  -- 5)	 A VerboseCompletionProcedure should be used only when the requestor wishes
  --     to obtain information stored in the header or label of the disk page.  Calling
  --     a VerboseCompletionProcedure requires substantially more computation than
  --     calling a NormalCompletionProcedure.
  -- 6)	 'xfers[i]' identifies a page to be transferred.  These pages are implicitly
  --     sequential in the file, the first being 'firstPage+pagesToSkip'.  Each
  --     XferSpec contains a pointer to the buffer for the page, the associated disk
  --     address, and an identifier supplied by the caller which will identify the page
  --     to the CompletionProcedure.  If the disk address is 'fillInvDA', the disk
  --     driver will attempt to deduce the address from the label of the previous page
  --     in the file.  This implies that if xfers[0].vDA is 'fillInvDA', the disk
  --     driver must read pages from the disk beginning at 'firstPage' and continuing
  --     for 'pagesToSkip-1' pages in order to determine the disk address.  If a
  --     VerboseCompletionProcedure has been specified, it will be called for each
  --     such read operation.  The 'id' parameter in this case will be the value
  --     'request.nonXferID', which the client should ensure is distinct from all
  --     'xfers[i].id'.  A NormalCompletionProcedure will not be invoked for such
  --     reads.  (The buffer used to hold the data for such reads is supplied
  --     by the disk driver.)
  -- 7)	 If 'pagesToSkip' is non-zero, the client must supply the disk address
  --     corresponding to 'fileID' and 'firstPage' in 'firstPagevDA'.  In this case,
  --     xfers[i].vDA should be 'fillInvDA', and the disk driver behaves as described
  --     in item 6 above.  If 'pagesToSkip' is zero, 'firstPagevDA' is ignored and
  --     xfers[0].vDA cannot be 'fillInvDA'.  Some initial subsequence of the 'xfers'
  --     must have valid addresses in their vDA fields.  Once xfers[k].vDA equals
  --     'fillInvDA', for some k, xfers[i].vDA should be 'fillInvDA' for all i>k.
  -- 8)	 DiskRequests that specify writing of labels (WriteHLD or WriteLD) must
  --     furnish additional information to complete the label field.  The 'next' and
  --     'prev' fields of the variant record are used by the disk driver to complete
  --     the labels at the ends of the consecutive page sequence described by 'xfers'.
  --     The 'lastByteCount' field permits the last page of the file to be partially
  --     used; it will be ignored unless 'next' is 'eofvDA'.  In addition, 'pagesToSkip'
  --     must be zero and all elements of 'xfers' must contain explicit disk addresses
  --     (NOT 'fillInvDA') when either WriteHLD or WriteLD is specified.  This
  --     requirement is imposed because the disk driver will overwrite the labels
  --     instead of reading them, and therefore cannot use the existing links in
  --     them for chaining purposes.  For related reasons, the same restrictions on
  --     'pagesToSkip' and vDA fields apply to ReadHLD.  (Clients that write labels
  --     should be careful not to violate the invariants of the Alto File System.)
  -- 9)	 The client of this interface can freely release the storage used to hold the
  --     DiskRequest or XferSpecs immediately after InitiateDiskIO returns.
  --     Specifically, these data structures need not be retained until
  --     CompletionProcedures are invoked.


  -- Types and Related Constants --

  vDA: TYPE = AltoFileDefs.vDA;
  vDC: TYPE = AltoFileDefs.vDC;
  SN: TYPE = AltoFileDefs.SN;
  DISK: TYPE = AltoFileDefs.DISK;

  FID: TYPE = MACHINE DEPENDENT RECORD [version: CARDINAL, serial: SN];

  PageCount: TYPE = [0..AltoDefs.MaxFilePage];

  eofvDA: vDA = AltoFileDefs.eofDA;
  fillInvDA: vDA = AltoFileDefs.fillinDA;

  DiskRequest: TYPE = RECORD [
    firstPage: AltoDefs.PageNumber,
    fileID: FID,
    firstPagevDA: vDA,
    pagesToSkip: PageCount,
    nonXferID: RequestID,
    xfers: DESCRIPTOR FOR ARRAY OF XferSpec,
    proc: CompletionProcedure,
    noRestore: BOOLEAN,
    command: SELECT action: vDC FROM
      ReadHLD, ReadLD, ReadD, WriteD, SeekOnly, DoNothing => NULL,
      WriteHLD, WriteLD => [next, prev: vDA, lastByteCount: CARDINAL],
      ENDCASE];

  XferSpec: TYPE = RECORD [buffer: POINTER, diskAddress: vDA, id: RequestID];

  CompletionProcedure: TYPE = RECORD [
    SELECT tag: * FROM
      normal => [proc: NormalCompletionProcedure],
      verbose => [proc: VerboseCompletionProcedure],
      ENDCASE];

  NormalCompletionProcedure: TYPE = PROCEDURE [id: RequestID, status: CompletionStatus];

  VerboseCompletionProcedure: TYPE = PROCEDURE [
    id: RequestID, status: CompletionStatus, header: POINTER TO vDH, label: POINTER TO vDL];

  CompletionStatus: TYPE = {
    noStatus, ok, neverStarted, checkError, checksumError, diskOffline, dataLate,
    seekFailure, badDiskAddress};

  RequestID: TYPE = UNSPECIFIED;

  vDH: TYPE = MACHINE DEPENDENT RECORD [packID: CARDINAL, diskAddress: vDA];

  vDL: TYPE = MACHINE DEPENDENT RECORD [
    next, prev: vDA,
    undefined1: UNSPECIFIED,
    bytes: CARDINAL,
    undefined2: [0..1],
    page: AltoDefs.PageNumber,
    fileID: FID];

  RequestError: TYPE = {badvDA, illegalvDASequence, tooManyTransfers, illegalTransfer};

  -- Procedures and Signals --

  InitializeDiskIO: PRIVATE PROCEDURE [minOps: CARDINAL] RETURNS [nOps: CARDINAL];
  -- initializes the disk IO package with a minimum of 'minOps' page transfers
  -- permitted simultaneously.  The actual number permitted is returned.  Note that this
  -- procedure is PRIVATE, and should therefore be called only by knowledgeable clients
  -- (i.e., the VM level).

  FinalizeDiskIO: PRIVATE PROCEDURE;
  -- finalizes the disk IO package.  Note that this is a private procedure, and should
  -- therefore be called only by knowledgeable clients (i.e., the VM level).

  ResetDiskShape: PRIVATE PROCEDURE [disk: DISK];
  -- resets the parameters that describe the shape of the disk.  Note that this
  -- procedure is PRIVATE, and should therefore be called only by knowledgeable clients
  -- (i.e., the VM level).

  InitiateDiskIO: PROCEDURE [operation: POINTER TO DiskRequest];
  -- initiates the requested disk transfers and returns to the caller.  The
  -- CompletionProcedure in the argument DiskRequest is invoked as each transfer
  -- terminates.  If 'operation' is somehow invalid or malformed, 'BogusDiskRequest'
  -- will be raised.

  BogusRequest: ERROR [reason: RequestError, operation: POINTER TO DiskRequest];
  -- raised by InitiateDiskIO because the disk request is invalid for some reason.
  -- The 'reason' parameter indicates why the request is unacceptable.

  DiskError: ERROR [status: CompletionStatus];
  -- a convenient signal for use by higher levels (strange as that may seem).

  END.