-- CedarSnapshotRegistry.mesa
-- last edited by Levin:  September 7, 1982 1:00 pm

DIRECTORY
  CedarSnapshot USING [CheckpointProc, RollbackProc],
  CedarSnapshotPrivate USING [],
  Heap USING [systemZone],
  Inline USING [LongCOPY];

CedarSnapshotRegistry: MONITOR
  IMPORTS Heap, Inline
  EXPORTS CedarSnapshot, CedarSnapshotPrivate =

BEGIN

Registry: TYPE = LONG POINTER TO Procs;
Procs: TYPE = RECORD [
  nProcs: CARDINAL ← 0,
  procs: SEQUENCE nSlots: CARDINAL OF RegisteredProc ← NULL];
RegisteredProc: TYPE = PROC;

initialProcs: CARDINAL = 10;
incrementProcs: CARDINAL = 5;

checkpointRegistry, rollbackRegistry: Registry ← NIL;

ProcAlreadyRegistered: ERROR = CODE;
ProcNotRegistered: ERROR = CODE;


-- Procedures exported to CedarSnapshot

Register: PUBLIC ENTRY PROC [
  c: CedarSnapshot.CheckpointProc ← NIL, r: CedarSnapshot.RollbackProc ← NIL] =
  {AddProc[@checkpointRegistry, LOOPHOLE[c]]; AddProc[@rollbackRegistry, LOOPHOLE[r]]};

Deregister: PUBLIC ENTRY PROC [
  c: CedarSnapshot.CheckpointProc ← NIL, r: CedarSnapshot.RollbackProc ← NIL] =
  {RemoveProc[@checkpointRegistry, LOOPHOLE[c]]; RemoveProc[@rollbackRegistry, LOOPHOLE[r]]};


-- Procedures exported to CedarSnapshotPrivate

-- The enumerators call the caller-supplied procedure outside the monitor, permitting
-- Register/Deregister to be called from within the enumeration.

EnumerateCheckpointProcs: PUBLIC PROC [proc: PROC [CedarSnapshot.CheckpointProc]] = {
  i: CARDINAL ← LAST[CARDINAL];
  p: CedarSnapshot.CheckpointProc;
  GetNext: ENTRY PROC RETURNS [p: CedarSnapshot.CheckpointProc] = INLINE {
    p ← NIL;
    i ← MIN[i, checkpointRegistry.nProcs];
    IF i > 0 THEN {
      i ← i - 1;
      p ← LOOPHOLE[checkpointRegistry.procs[i]];
      };
    };
  IF checkpointRegistry = NIL THEN RETURN;
  UNTIL (p ← GetNext[]) = NIL DO proc[p]; ENDLOOP;
  };

EnumerateRollbackProcs: PUBLIC PROC [proc: PROC [CedarSnapshot.RollbackProc]] = {
  i: CARDINAL ← 0;
  p: CedarSnapshot.RollbackProc;
  GetNext: ENTRY PROC RETURNS [p: CedarSnapshot.RollbackProc] = INLINE {
    p ← NIL;
    IF i < rollbackRegistry.nProcs THEN {
      p ← LOOPHOLE[rollbackRegistry.procs[i]];
      i ← i + 1;
      };
    };
  IF rollbackRegistry = NIL THEN RETURN;
  UNTIL (p ← GetNext[]) = NIL DO proc[p]; ENDLOOP;
  };


-- Private Procedures

AddProc: INTERNAL PROC [registry: LONG POINTER TO Registry, proc: RegisteredProc] = {
  IF proc = NIL THEN RETURN;
  IF registry↑ = NIL THEN registry↑ ← Heap.systemZone.NEW[Procs[initialProcs] ← []];
  FOR i: CARDINAL IN [0..registry↑.nProcs) DO
    IF registry↑.procs[i] = proc THEN ERROR ProcAlreadyRegistered;
    ENDLOOP;
  IF registry↑.nProcs = registry↑.nSlots THEN {
    newRegistry: Registry ←
      Heap.systemZone.NEW[Procs[registry↑.nSlots+incrementProcs] ← [nProcs: registry↑.nProcs]];
    Inline.LongCOPY[from: @registry↑.procs[0], to: @newRegistry.procs[0],
                    nwords: registry↑.nSlots*SIZE[RegisteredProc]];
    Heap.systemZone.FREE[registry];
    registry↑ ← newRegistry;
    };
  registry↑.procs[registry↑.nProcs] ← proc;
  registry↑.nProcs ← registry↑.nProcs + 1;
  };

RemoveProc: INTERNAL PROC [registry: LONG POINTER TO Registry, proc: RegisteredProc] = {
  state: {looking, compressing} ← looking;
  FOR i: CARDINAL IN [0..registry↑.nProcs) DO
    SELECT state FROM
      looking => IF registry↑.procs[i] = proc THEN state ← compressing;
      compressing => registry↑.procs[i - 1] ← registry↑.procs[i];
      ENDCASE;
    ENDLOOP;
  IF state = looking THEN ERROR ProcNotRegistered;
  registry↑.nProcs ← registry↑.nProcs - 1;
  };

END.