unit EventSync;

{ Martin Harvey 5/6/2000 }

interface

uses Windows;

type
  TEventSynchronizer = class(TObject)
  private
    FDataLock, FWriteLock: TRTLCriticalSection;
    FReaders, FWriters: integer;
    FNoReaders, FNoWriters: THandle;
  protected
  public
    constructor Create;
    destructor Destroy; override;
    procedure StartRead;
    procedure StartWrite;
    procedure EndRead;
    procedure EndWrite;
  published
  end;

implementation

constructor TEventSynchronizer.Create;
begin
  inherited Create;
  InitializeCriticalSection(FDataLock);
  InitializeCriticalSection(FWriteLock);
  FNoReaders := CreateEvent(nil, true, true, nil);
  FNoWriters := CreateEvent(nil, true, true, nil);
end;

destructor TEventSynchronizer.Destroy;
begin
  DeleteCriticalSection(FDataLock);
  DeleteCriticalSection(FWriteLock);
  CloseHandle(FNoReaders);
  CloseHandle(FNoWriters);
  inherited Destroy;
end;

procedure TEventSynchronizer.StartRead;

var
  Block: boolean;

begin
  EnterCriticalSection(FDatalock);
  if FReaders = 0 then
    ResetEvent(FNoReaders);
  Inc(FReaders);
  Block := FWriters > 0;
  LeaveCriticalSection(FDataLock);
  if Block then
    WaitForSingleObject(FNoWriters, INFINITE);
end;

procedure TEventSynchronizer.StartWrite;

var
  Block: boolean;

begin
  EnterCriticalSection(FDataLock);
  if FWriters = 0 then
    ResetEvent(FNoWriters);
  Inc(FWriters);
  Block := FReaders > 0;
  LeaveCriticalSection(FDataLock);
  if Block then
    WaitForSingleObject(FNoReaders, INFINITE);
  EnterCriticalSection(FWriteLock);
end;

procedure TEventSynchronizer.EndRead;
begin
  EnterCriticalSection(FDataLock);
  Dec(FReaders);
  if FReaders = 0 then
    SetEvent(FNoReaders);
  LeaveCriticalSection(FDataLock);
end;

procedure TEventSynchronizer.EndWrite;
begin
  LeaveCriticalSection(FWriteLock);
  EnterCriticalSection(FDataLock);
  Dec(FWriters);
  if FWriters = 0 then
    SetEvent(FNoWriters);
  LeaveCriticalSection(FDataLock);
end;

end.