Multithreading — The Delphi Way · © 2000 Martin Harvey
Source example 18
unit BoundedBuf;
interface
uses Windows, SysUtils;
const
DefaultWaitTime = 5000;
type
TBufferEntries = ^Pointer;
TBoundedBuffer = class
private
FBufInit: boolean;
FBufSize: integer;
FBuf: TBufferEntries;
FReadPtr,
FWritePtr: integer;
FEntriesFree, FEntriesUsed: THandle;
FCriticalMutex: THandle;
protected
procedure SetSize(NewSize: integer);
public
procedure ResetState;
destructor Destroy; override;
function PutItem(NewItem: Pointer): boolean;
function GetItem: Pointer;
published
property Size: integer read FBufSize write SetSize;
end;
implementation
const
FailMsg1 = 'Flow control failed, or buffer not initialised';
FailMsg2 = 'Critical section failed, or buffer not initialised';
procedure TBoundedBuffer.SetSize(NewSize: integer);
begin
if FBufInit then ResetState;
if NewSize < 2 then NewSize := 2;
FBufSize := NewSize;
GetMem(FBuf, Sizeof(Pointer) * FBufSize);
FillMemory(FBuf, Sizeof(Pointer) * FBufSize, 0);
FBufInit := true;
FCriticalMutex := CreateMutex(nil, false, nil);
FEntriesFree := CreateSemaphore(nil, FBufSize - 1, FBufSize, nil);
FEntriesUsed := CreateSemaphore(nil, 0, FBufSize, nil);
if (FCriticalMutex = 0)
or (FEntriesFree = 0)
or (FEntriesUsed = 0) then ResetState
end;
procedure TBoundedBuffer.ResetState;
begin
if FBufInit then
begin
WaitForSingleObject(FCriticalMutex, DefaultWaitTime);
FBufInit := false;
FBufSize := 0;
FreeMem(FBuf);
ReleaseSemaphore(FEntriesFree, 1, nil);
ReleaseSemaphore(FEntriesUsed, 1, nil);
CloseHandle(FEntriesFree);
CloseHandle(FEntriesUsed);
CloseHandle(FCriticalMutex);
end;
end;
function TBoundedBuffer.PutItem(NewItem: Pointer): boolean;
var
NthItem: TBufferEntries;
begin
result := false;
if WaitForSingleObject(FEntriesFree, INFINITE) <> WAIT_OBJECT_0 then
exit;
if (WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0)
or not FBufInit then
exit;
NthItem := FBuf;
Inc(NthItem, FWritePtr);
NthItem^ := NewItem;
FWritePtr := (FWritePtr + 1) mod FBufSize;
ReleaseMutex(FCriticalMutex);
ReleaseSemaphore(FEntriesUsed, 1, nil);
result := true;
end;
function TBoundedBuffer.GetItem: Pointer;
var
NthItem: TBufferEntries;
begin
result := nil;
if WaitForSingleObject(FEntriesUsed, INFINITE) <> WAIT_OBJECT_0 then
exit;
if (WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0)
or not FBufInit then
exit;
NthItem := FBuf;
Inc(NthItem, FReadPtr);
Result := NthItem^;
FReadPtr := (FReadPtr + 1) mod FBufSize;
ReleaseMutex(FCriticalMutex);
ReleaseSemaphore(FEntriesFree, 1, nil);
end;
destructor TBoundedBuffer.Destroy;
begin
ResetState;
inherited Destroy;
end;
end.