-- Log.mesa
-- Last edited by
--   MBrown on January 30, 1984 10:10:04 am PST
--   Taft on 16-Feb-82 18:29:44


  DIRECTORY
    AlpineEnvironment,
    AlpineInternal,
    Basics;

Log: DEFINITIONS =
  BEGIN
  TransID: TYPE = AlpineEnvironment.TransID;
  Comparison: TYPE = Basics.Comparison;


  RecordType: TYPE = AlpineInternal.LogRecordType;
    -- A tag of type RecordType is associated with each log record as it is written.  This is
    --used to identify the recovery procedure responsible for the record.

  RecordID: TYPE = AlpineInternal.LogRecordID;
    -- Uniquely identifies a record within a log.  The 48 bit address space allows for
    --several hundred years of log writing.
  nullRecordID: RecordID = AlpineInternal.nullLogRecordID;
    -- No log record is identified by this value.

  Compare: PROC [a, b: RecordID] RETURNS [Comparison];
    -- The record with the smaller RecordID was written earlier in time.
    -- An inline version of Log.Compare is LogInline.Compare.
  firstRecordID: RecordID = AlpineInternal.firstRecordID;
    -- for all r, Compare[firstRecordID, r] # greater
  lastRecordID: RecordID = AlpineInternal.lastRecordID;
    -- for all r, Compare[lastRecordID, r] # less

  Block: TYPE = RECORD [
    base: LONG POINTER,
    length: CARDINAL,
    rest: BlockPtr ← NIL ];
  BlockPtr: TYPE = LONG POINTER TO Block;
  nullBlock: Block = [base: NIL, length: 0, rest: NIL];
    -- Essentially a list of descriptor for array of word.
    -- Used both for gather writing (e.g. system puts a header onto each log record)
    --and for scatter reading (e.g. pages that are written contiguously in the log may
    --be buffered in discontiguous virtual memory.)
  minBlockLen: INT = 1;
  maxBlockLen: INT = LAST[CARDINAL] - 512;
    -- an underestimate of the true upper bound; no reasonable application should approach this.

  RecoveryProc: TYPE = PROC [
    record: RecordID, type: RecordType, trans: AlpineInternal.TransHandle,
    outcome: TransState];
    -- A recovery proc should call ReadForRecovery, not Read, to access the log
    --record whose ID is passed to it.  Attempting to read any other log record
    --with ReadForRecovery will result in an error.

  TransState: TYPE = AlpineInternal.TransState;
    -- {committed, aborted, ready}

  RegisterRecoveryProc: PROC [recordType: RecordType, recoveryProc: RecoveryProc];
    -- Any program that writes log records of a particular type is responsible for recovery
    --of those records.  As part of its PROGRAM proc (called by STARTing the module),
    --the program should call RegisterRecoveryProc once for each type of record it
    --writes.  It is an error to call RegisterRecoveryProc after the update pass of recovery
    --begins.

  ReadForRecovery: ReadProc;
    -- Should be called only from a RecoveryProc, as explained above.
    -- See below for definition of type ReadProc.

  -- Note: Perform recovery before calling any of the procedures below.

  -- Log writing

  Write: PROC [
    trans: AlpineInternal.TransHandle, recordType: RecordType,
    recordData: Block, force: BOOL ← FALSE]
    RETURNS [thisRecord, followingRecord: RecordID];
    -- ! WriteFailed (online log is full - server should restart).

  CoordinatorWrite: PROC [
    trans: AlpineInternal.CoordinatorHandle, recordType: RecordType,
    recordData: Block, force: BOOL ← FALSE]
    RETURNS [thisRecord, followingRecord: RecordID];
    -- ! WriteFailed (online log is full - server should restart).

  WriteFailed: ERROR;

  Force: PROC [followingRecord: RecordID];
    -- Force all records prior to (not including) followingRecord to be recorded
    --stably in the online log.


  -- Log reading

  ReadProc: TYPE = PROC [thisRecord: RecordID, wordsToSkip: CARDINAL ← 0, to: Block]
    RETURNS [status: ReadProcStatus, wordsRead: CARDINAL];

  ReadProcStatus: TYPE = {normal, destinationFull, sourceExhausted};
    -- normal means destinationFull and sourceExhausted (normal termination for reading
    --a fixed-length log record).

  Read: ReadProc;
    -- Extracts all or part of the contents of a log record whose type and transaction are
    --known, placing it in "to".  Should be called during normal operation of the server
    --to carry out intentions.

  END.


CHANGE LOG

Created by MBrown on  2-Dec-81

Changed by MBrown on 16-Dec-81 15:06:20
-- Initialize takes a File.Capability, not the name of a server.

Changed by MBrown on  1-Feb-82 15:10:32
-- Define interface that is decoupled from handles defined in other parts of Alpine.
--Change from stream to stateless style reading.  Use 48 bit log record ids throughout.

Changed by MBrown on  2-Feb-82 11:09:06
-- Add workerComplete to RecordType enumeration.

Changed by MBrown on June 23, 1982 4:16 pm
-- Use AlpineEnvironment.FileID, not File.Capability; introduce Error and LogFailure.

Changed by MBrown on September 14, 1982 12:34 pm
-- Move "Initialization and recovery" section to LogBasic.

Changed by MBrown on September 15, 1982 8:39 pm
-- Added RegisterRecoveryProc, merged various forms of invalidRecordID, eliminated Release
--(still in LogBasic).

Changed by MBrown on October 2, 1982 8:25 pm
-- Moved Format and Recover to LogControl.  next ← NIL in Log.Block.

Changed by MBrown on October 3, 1982 10:42 pm
-- Error -> LogFull (all other errors are really caller programming errors).
--recordExhausted: BOOL -> status: ReadProcStatus.

Changed by MBrown on October 7, 1982 5:09 pm
-- ReadForRecovery introduced, and the readProc parm to RecoveryProc dropped.

Changed by MBrown on October 11, 1982 3:21 pm
-- Added largestRecordID.

Changed by MBrown on November 3, 1982 11:59 am
-- Changed trans arg to Write from TransID to TransHandle; added CoordinatorWrite.

Changed by MBrown on January 30, 1984 10:10:13 am PST
-- Changed Environment -> Basics for Cedar 5.0.