Более близкий к оригиналу Хоаровский Монитор с явно выделенными условиями ожидания (более эффективен, чем предыдущий в случае большого количества различных условий ожидания).
Сначала интерфейс:
Код:
DEFINITION Hoare;
  TYPE
    Monitor = POINTER TO LIMITED RECORD 
      (m: Monitor) Enter, NEW;
      (m: Monitor) Exit, NEW;
      (m: Monitor) FINALIZE-
    END;
    Condition = POINTER TO LIMITED RECORD 
      (c: Condition) Signal, NEW;
      (c: Condition) Wait, NEW;
      (c: Condition) FINALIZE-
    END;
  PROCEDURE NewMonitor (): Monitor;
  PROCEDURE NewCondition (m: Monitor): Condition;
END Hoare.
Пример использования:
Код:
  IMPORT Hoare;
  TYPE
    Element* = INTEGER;
    Queue* = POINTER TO LIMITED RECORD
      monitor: Hoare.Monitor;
      nonEmpty, nonFull: Hoare.Condition;
      buffer: ARRAY 3 OF Element;
      begin, end, count, enqueued, dequeued: INTEGER;
    END;
  PROCEDURE NewQueue* (): Queue;
    VAR q: Queue;
  BEGIN NEW(q);
    q.monitor := Hoare.NewMonitor();
    q.nonEmpty := Hoare.NewCondition(q.monitor);
    q.nonFull := Hoare.NewCondition(q.monitor);
    RETURN q
  END NewQueue;
  PROCEDURE (q: Queue) Enqueue* (e: Element), NEW;
  BEGIN
    q.monitor.Enter;
    WHILE q.count >= LEN(q.buffer) DO q.nonFull.Wait END;
    INC(q.count);
    INC(q.enqueued);
    q.buffer[q.end] := e;
    q.end := (q.end + 1) MOD LEN(q.buffer);
    q.nonEmpty.Signal;
    q.monitor.Exit
  END Enqueue;
  PROCEDURE (q: Queue) Dequeue* (OUT e: Element), NEW;
  BEGIN
    q.monitor.Enter;
    WHILE q.count < 1 DO q.nonEmpty.Wait END;
    DEC(q.count);
    INC(q.dequeued);
    e := q.buffer[q.begin];
    q.begin := (q.begin + 1) MOD LEN(q.buffer);
    q.nonFull.Signal;
    q.monitor.Exit
  END Dequeue;
Вообще, в оригинале Хоар предполагал при Wait использование не WHILE, а IF. Но в его мониторе было требование чтобы процессы ожидавшие по Wait впускались первыми и будились по очереди, здесь же немного не так (будятся все), так что нужен WHILE.
Реализация:
Код:
MODULE Hoare;
  IMPORT WinApi;
  
  TYPE
    Monitor* = POINTER TO LIMITED RECORD
      n: INTEGER;
      h: WinApi.HANDLE
    END;
    Condition* = POINTER TO LIMITED RECORD
      m: Monitor;
      h: WinApi.HANDLE
    END;
  PROCEDURE NewMonitor* (): Monitor;
    VAR m: Monitor;
  BEGIN NEW(m);
    m.h := WinApi.CreateEvent(NIL, WinApi.FALSE, WinApi.TRUE, NIL);
    RETURN m
  END NewMonitor;
  PROCEDURE NewCondition* (m: Monitor): Condition;
    VAR c: Condition;
  BEGIN ASSERT(m # NIL, 20);
    NEW(c);
    c.m := m;
    c.h := WinApi.CreateEvent(NIL, WinApi.TRUE, WinApi.FALSE, NIL);
    RETURN c
  END NewCondition;
  PROCEDURE (m: Monitor) Enter*, NEW;
    VAR res: INTEGER;
  BEGIN
    IF WinApi.InterlockedIncrement(m.n) # 1 THEN
      res := WinApi.WaitForSingleObject(m.h, WinApi.INFINITE)
    END
  END Enter;
  PROCEDURE (m: Monitor) Exit*, NEW;
    VAR ok: WinApi.BOOL;
  BEGIN
    IF WinApi.InterlockedDecrement(m.n) # 0 THEN
      ok := WinApi.SetEvent(m.h)
    END
  END Exit;
  PROCEDURE (c: Condition) Wait*, NEW;
    VAR res: INTEGER;
  BEGIN    
    res := WinApi.SignalObjectAndWait(c.m.h, c.h, WinApi.INFINITE, WinApi.FALSE);
    res := WinApi.InterlockedDecrement(c.m.n);
    c.m.Enter
  END Wait;
  PROCEDURE (c: Condition) Signal*, NEW;
    VAR ok: WinApi.BOOL;
  BEGIN ok := WinApi.PulseEvent(c.h)
  END Signal;
  PROCEDURE (m: Monitor) FINALIZE-;
    VAR ok: WinApi.BOOL;
  BEGIN
    (* ok := WinApi.CloseHandle(h.pulse); *)
    ok := WinApi.CloseHandle(m.h)
  END FINALIZE;
  PROCEDURE (c: Condition) FINALIZE-;
    VAR ok: WinApi.BOOL;
  BEGIN
    ok := WinApi.CloseHandle(c.h)
  END FINALIZE;
END Hoare.