-- LogBasic.mesa
-- The LogBasic interface is lower-level than the Log interface.  The Log interface "knows"
--about transactions, while LogBasic does not.  The LogBasic interface therefore allows
--logging an event that is not associated with a single transaction (e.g. a log checkpoint).
-- Last edited by
--   MBrown on June 27, 1983 10:04 am

  DIRECTORY
    AlpineEnvironment,
    Log,
    LogRep;

LogBasic: DEFINITIONS =
  BEGIN
  VolumeID: TYPE = AlpineEnvironment.VolumeID;
  FileID: TYPE = AlpineEnvironment.FileID;
  PageNumber: TYPE = AlpineEnvironment.PageNumber;
  PageCount: TYPE = AlpineEnvironment.PageNumber;
  Block: TYPE = Log.Block;
  RecordID: TYPE = Log.RecordID;
  WordNumber: TYPE = INT;
    -- This represents a word index (0-origin) into the online log file.  This representation
    --limits us to a 4 billion byte online log, which may be a problem eventually but
    --not today.
  WordCount: TYPE = INT;
    -- This represents a difference of two WordNumbers, the length of an online log file,
    --etc.


  -- Initialization.

  EstablishLogFile: PROC [logVolume: VolumeID, logFile: FileID];
    -- Must be called before any of the procs below.

  LogFileSize: PROC [] RETURNS [PageCount];

  LocateFirstRecord: PROC [] RETURNS [WordNumber];
    -- Reads the log file determined by EstablishLogFile.  Returns the WordNumber of a
    --record near the start of the log.
    -- The intended use of this proc is in recovery from the loss of the restart file.  This
    --proc takes a long time, but is seldom used.


  -- Sequential reading.

  -- There is a single stream for reading the log.  This stream is used only during recovery.
  --There is no monitor protection, so the client is responsible for serializing stream calls.

  -- Sequential reading of the log is more primitive than random access.  It knows how to deal
  --gracefully with the raggedness that defines the end of log.  It can be performed even if the
  --logical addressing structure of the log has not been established (as on the first step of
  --recovery.)

  OpenRecordStreamFromWord: PROC [firstWord: WordNumber]
    RETURNS [notStartOfRecord: BOOL, currentRecord: RecordID];
    -- ! InvalidPage
    -- Creates a stream using physical log addressing.  RecordIDs returned by Advance will be
    --based on the assumption that the RecordID of record found at firstWord is firstWord.

  OpenRecordStreamFromCheckpoint: PROC [
    checkpointWord: WordNumber, checkpointRecord, firstRecord: RecordID]
    RETURNS [notStartOfRecord: BOOL, currentRecord: RecordID];
    -- ! InvalidPage
    -- Creates a stream using logical log addressing.  The logical-physical correspondence is
    --defined by checkpointWord and checkpointRecord.  firstRecord must be less
    --than checkpointRecord.

  InvalidPage: ERROR;
    -- OpenRecordStreamFromXXX was pointed to a page with random garbage in it.
    -- After this error, the caller should CloseRecordStream[] immediately. 

  CheckCurrentRecord: PROC [] RETURNS [truncated: BOOL];
    -- ! Log.Error{ invalidRecordID, invalidRecord }
    -- This proc may be called before calling GetCurrentRecord, in order to detect errors
    --that would otherwise be raised by the GetCurrentRecord call.

  GetCurrentRecord: PROC [currentRecord: RecordID, to: Block]
    RETURNS [status: Log.ReadProcStatus, wordsRead: CARDINAL];
    -- This proc has the same type as Get below.
    -- This call does not change the state of the stream.

  GetCurrentPageAndVersion: PROC []
    RETURNS [PageNumber, LogRep.PageVersion];
    -- ! (none)
    -- A somewhat peculiar operation, provided for purposes of recovery.

  AdvanceRecordStream: PROC []
    RETURNS [endOfLog, truncatedRecord: BOOL, currentRecord: RecordID];
    -- ! (none)
    -- Positions to next log record, returns its ID.
    -- If endOfLog, then "currentRecord" is past the end of log.
    -- If truncatedRecord, then the record before "currentRecord" was incomplete.
    -- There is no guarantee that "currentRecord" exists in its entirety, only that some prefix
    --exists.

  CloseRecordStream: PROC [];
    -- Shuts down the stream.  Must be called before opening the stream again.


  -- Writing.

  -- There is a single output stream, created by the call to OpenForPut.  The stream is
  --protected by monitors so it is ok for several processes to call Put without other
  --synchronization.
  -- (Note that OpenForPut is done implicitly by EraseLogFile.)
  -- No non-fatal errors are possible in the basic log writing primitives.  An attempt to
  --write when the online log is (nearly) full causes a wait; another process must free some
  --online log pages.

  OpenForPut: PROC [nextPage: PageNumber, version: LogRep.PageVersion,
    nextRecord: RecordID];
    -- Sets up the output stream for writing to file page nextPage, naming the first
    --record that it writes there nextRecord.

  AssertNormalOperation: PROC [];
    -- If normalOperation, then perform more stringent checking for log full.

  Put: PROC [from: Log.Block, force: BOOL ← FALSE, writeID: BOOL ← FALSE]
    RETURNS [thisRecord, followingRecord: RecordID];
    -- ! Log.Error {writeID, totalLen, logFull}
    -- Caller need not ensure that all pages of "from" are in main store; Put will touch each
    --page before entering the log tail monitor.
    -- If writeID and a log record ID won't fit in the first block, Error[writeID].
    -- If total lenth of write request is not IN [minBlockLen .. maxBlockLen], Error[totalLen].
    -- Error[logFull] is a server crash; it does not release the log tail monitor or log core monitor.
  minBlockLen: INT = 1;
  maxBlockLen: INT = LAST[CARDINAL];

  WordNumberFromRecordID: PROC [thisRecord: RecordID] RETURNS [WordNumber];
    -- ! Log.Error {recordNonexistent, recordNotOnline} (thisRecord is not valid.)
    -- If thisRecord is represented in the online log, returns its location.
    -- Intended for use by the proc that writes checkpointComplete records.

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

  RecordIDOfNextPut: PROC [] RETURNS [RecordID];
    -- ! (none)
    -- Returns the RecordID that would have been assigned if this call had been a Put.

  CloseForPut: PROC [];
    -- ! (none)
    -- Flushes all log buffers to disk, and returns log writing component to initial state.

  maxInvalidLogPages: PageCount = 16;
    -- This many pages past end of log do not have well-defined values.  On restart, these
    --pages need to be reinitialized.


  -- Purging.

  Release: PROC [beforeRecord: RecordID];
    -- ! Log.Error {recordNonexistent, recordNotOnline} (beforeRecord is not valid to release.)
    -- The caller certifies that all records prior to (not including) beforeRecord may
    --be purged from the online log.


  -- Random reading.

  -- Intended to be used only for carrying out intentions, not during recovery.

  Get: PROC [thisRecord: RecordID, to: Log.Block]
    RETURNS [status: Log.ReadProcStatus, wordsRead: CARDINAL];
    -- Extracts all or part of the contents of a log record.  wordsRead is the number of
    --words of log record touched by the Get call (bounded above by the total length
    --of the block "to").

  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 August 10, 1982 11:26 pm
-- Move Put here from LogPrivate; define realistic Get.

Changed by MBrown on August 16, 1982 3:29 pm
-- Added sequential reading back in, and simplified random reading correspondingly.

Changed by MBrown on September 16, 1982 12:42 pm
-- Changes to make recovery implementable.

Changed by MBrown on September 23, 1982 1:52 pm
-- Added WordNumberFromRecordID.

Changed by MBrown on June 27, 1983 9:48 am
-- Added AssertNormalOperation.