Sample 1
unit test;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('Hello World!');
end;
end.
Sample 2
unit PrimeForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TPrimeFrm = class(TForm)
NumEdit: TEdit;
SpawnButton: TButton;
procedure SpawnButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
PrimeFrm: TPrimeFrm;
implementation
uses PrimeThread;
{$R *.DFM}
procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
var
NewThread: TPrimeThrd;
begin
NewThread := TPrimeThrd.Create(True);
NewThread.FreeOnTerminate := True;
try
NewThread.TestNumber := StrToInt(NumEdit.Text);
NewThread.Resume;
except on EConvertError do
begin
NewThread.Free;
ShowMessage('That is not a valid number!');
end;
end;
end;
end.
Sample 3
unit PrimeThread;
interface
uses
Classes;
type
TPrimeThrd = class(TThread)
private
FTestNumber: integer;
protected
function IsPrime: boolean;
procedure Execute; override;
public
property TestNumber: integer write FTestNumber;
end;
implementation
uses SysUtils, Dialogs;
function TPrimeThrd.IsPrime: boolean;
var
iter: integer;
begin
result := true;
if FTestNumber < 0 then
begin
result := false;
exit;
end;
if FTestNumber <= 2 then
exit;
for iter := 2 to FTestNumber - 1 do
begin
if (FTestNumber mod iter) = 0 then
begin
result := false;
{exit;}
end;
end;
end;
procedure TPrimeThrd.Execute;
begin
if IsPrime then
ShowMessage(IntToStr(FTestNumber) + 'is prime.')
else
ShowMessage(IntToStr(FTestNumber) + 'is not prime.');
end;
end.
Sample 4
var
a: integer; {a is global}
begin
a := a + 1;
end;
Sample 5
type
TPrimeFrm = class(TForm)
NumEdit: TEdit;
SpawnButton: TButton;
ResultsMemo: TMemo;
procedure SpawnButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Sample 6
unit PrimeThread;
interface
uses
Classes;
type
TPrimeThrd = class(TThread)
private
FTestNumber: integer;
FResultString: string;
protected
function IsPrime: boolean;
procedure UpdateResults;
procedure Execute; override;
public
property TestNumber: integer write FTestNumber;
end;
implementation
uses SysUtils, Dialogs, PrimeForm;
procedure TPrimeThrd.UpdateResults;
begin
PrimeFrm.ResultsMemo.Lines.Add(FResultString);
end;
function TPrimeThrd.IsPrime: boolean; {omitted for brevity}
procedure TPrimeThrd.Execute;
begin
if IsPrime then
FResultString := IntToStr(FTestNumber) + ' is prime.'
else
FResultString := IntToStr(FTestNumber) + ' is not prime.';
Synchronize(UpdateResults);
end;
end.
Sample 7
function TPrimeThrd.IsPrime: boolean;
var
iter: integer;
begin
result := true;
if FTestNumber < 0 then
begin
result := false;
exit;
end;
if FTestNumber <= 2 then
exit;
iter := 2;
while (iter < FTestNumber) and (not terminated) do
begin
if (FTestNumber mod iter) = 0 then
begin
result := false;
{exit;}
end;
Inc(iter);
end;
end;
Sample 8
unit PrimeForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TPrimeFrm = class(TForm)
NumEdit: TEdit;
SpawnButton: TButton;
ResultsMemo: TMemo;
procedure SpawnButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
private
{ Private declarations }
FThreadRefCount: integer;
procedure HandleTerminate(Sender: TObject);
public
{ Public declarations }
end;
var
PrimeFrm: TPrimeFrm;
implementation
uses PrimeThread;
{$R *.DFM}
procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
var
NewThread: TPrimeThrd;
begin
NewThread := TPrimeThrd.Create(True);
NewThread.FreeOnTerminate := True;
try
with NewThread do
begin
TestNumber := StrToInt(NumEdit.Text);
Inc(FThreadRefCount);
OnTerminate := HandleTerminate;
Resume;
end;
except on EConvertError do
begin
NewThread.Free;
ShowMessage('That is not a valid number!');
end;
end;
end;
procedure TPrimeFrm.FormCreate(Sender: TObject);
begin
FThreadRefCount := 0;
end;
procedure TPrimeFrm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose := true;
if FThreadRefCount > 0 then
begin
if MessageDlg('Threads active. Do you still want to quit?',
mtWarning, [mbYes, mbNo], 0) = mrNo then
CanClose := false;
end;
end;
procedure TPrimeFrm.HandleTerminate(Sender: TObject);
begin
Dec(FThreadRefCount);
end;
end.
Sample 9
unit PrimeForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, PrimeThread;
const
WM_THREAD_COMPLETE = WM_APP + 5437; { Just a magic number }
type
TPrimeFrm = class(TForm)
NumEdit: TEdit;
SpawnButton: TButton;
ResultsMemo: TMemo;
procedure SpawnButtonClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
private
{ Private declarations }
FThread: TPrimeThrd;
procedure HandleThreadCompletion(var Message: TMessage); message WM_THREAD_COMPLETE;
public
{ Public declarations }
end;
var
PrimeFrm: TPrimeFrm;
implementation
{$R *.DFM}
procedure TPrimeFrm.HandleThreadCompletion(var Message: TMessage);
begin
if Assigned(FThread) then
begin
FThread.WaitFor;
FThread.Free;
FThread := nil;
end;
end;
procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
begin
if not Assigned(FThread) then
begin
FThread := TPrimeThrd.Create(True);
FThread.FreeOnTerminate := false;
try
with FThread do
begin
TestNumber := StrToInt(NumEdit.Text);
Resume;
end;
except on EConvertError do
begin
FThread.Free;
FThread := nil;
ShowMessage('That is not a valid number!');
end;
end;
end;
end;
procedure TPrimeFrm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose := true;
if Assigned(FThread) then
begin
if MessageDlg('Threads active. Do you still want to quit?',
mtWarning, [mbYes, mbNo], 0) = mrNo then
CanClose := false;
end;
{Sleep(50000);}{Line C}
if CanClose then
begin
if Assigned(FThread) then
begin
FThread.Terminate;
FThread.WaitFor;
FThread.Free;
FThread := nil;
end;
end;
end;
end.
Sample 10
unit PrimeThread;
interface
uses
Classes;
type
TPrimeThrd = class(TThread)
private
FTestNumber: integer;
FResultString: string;
protected
function IsPrime: boolean;
procedure UpdateResults;
procedure Execute; override;
public
property TestNumber: integer write FTestNumber;
end;
implementation
uses SysUtils, Dialogs, PrimeForm, Windows;
procedure TPrimeThrd.UpdateResults;
begin
PrimeFrm.ResultsMemo.Lines.Add(FResultString);
end;
function TPrimeThrd.IsPrime: boolean;
var
iter: integer;
begin
result := true;
if FTestNumber < 0 then
begin
result := false;
exit;
end;
if FTestNumber <= 2 then
exit;
iter := 2;
while (iter < FTestNumber) and (not terminated) do {Line A}
begin
if (FTestNumber mod iter) = 0 then
begin
result := false;
{exit;}
end;
Inc(iter);
end;
end;
procedure TPrimeThrd.Execute;
begin
if IsPrime then
FResultString := IntToStr(FTestNumber) + ' is prime.'
else
FResultString := IntToStr(FTestNumber) + ' is not prime.';
if not Terminated then {Line B}
begin
Synchronize(UpdateResults);
PostMessage(PrimeFrm.Handle, WM_THREAD_COMPLETE, 0, 0);
end;
end;
end.
Sample 11
{ Unit PrimeThread }
type
TPrimeThrd = class(TThread)
private
FTestNumber: integer;
FResultString: string;
protected
function IsPrime: boolean;
procedure Execute; override;
public
property TestNumber: integer write FTestNumber;
property ResultString: string read FResultString;
end;
procedure TPrimeThrd.Execute;
begin
if IsPrime then
FResultString := IntToStr(FTestNumber) + ' is prime.'
else
FResultString := IntToStr(FTestNumber) + ' is not prime.';
if not Terminated then {Line B}
PostMessage(PrimeFrm.Handle, WM_THREAD_COMPLETE, 0, 0);
end;
{ Unit PrimeForm }
procedure TPrimeFrm.HandleThreadCompletion(var Message: TMessage);
begin
if Assigned(FThread) then
begin
FThread.WaitFor;
ResultsMemo.Lines.Add(FThread.ResultString);
FThread.Free;
FThread := nil;
end;
end;
Sample 12
unit PrimeForm2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, PrimeThread;
const
WM_DATA_IN_BUF = WM_APP + 1000;
MaxMemoLines = 20;
type
TPrimeFrm = class(TForm)
ResultMemo: TMemo;
StartBtn: TButton;
StartNumEdit: TEdit;
StopBtn: TButton;
procedure StartBtnClick(Sender: TObject);
procedure StopBtnClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
FStringSectInit: boolean;
FPrimeThread: TPrimeThrd2;
FStringBuf: TStringList;
procedure UpdateButtons;
procedure HandleNewData(var Message: TMessage); message WM_DATA_IN_BUF;
public
{ Public declarations }
StringSection: TRTLCriticalSection;
property StringBuf: TStringList read FStringBuf write FStringBuf;
end;
var
PrimeFrm: TPrimeFrm;
implementation
{$R *.DFM}
procedure TPrimeFrm.UpdateButtons;
begin
StopBtn.Enabled := FStringSectInit;
StartBtn.Enabled := not FStringSectInit;
end;
procedure TPrimeFrm.StartBtnClick(Sender: TObject);
begin
if not FStringSectInit then
begin
InitializeCriticalSection(StringSection);
FStringBuf := TStringList.Create;
FStringSectInit := true;
FPrimeThread := TPrimeThrd2.Create(true);
SetThreadPriority(FPrimeThread.Handle, THREAD_PRIORITY_BELOW_NORMAL);
try
FPrimeThread.StartNum := StrToInt(StartNumEdit.Text);
except
on EConvertError do FPrimeThread.StartNum := 2;
end;
FPrimeThread.Resume;
end;
UpdateButtons;
end;
procedure TPrimeFrm.StopBtnClick(Sender: TObject);
begin
if FStringSectInit then
begin
with FPrimeThread do
begin
Terminate;
WaitFor;
Free;
end;
FPrimeThread := nil;
FStringBuf.Free;
FStringBuf := nil;
DeleteCriticalSection(StringSection);
FStringSectInit := false;
end;
UpdateButtons;
end;
procedure TPrimeFrm.HandleNewData(var Message: TMessage);
begin
if FStringSectInit then {Not necessarily the case!}
begin
EnterCriticalSection(StringSection);
ResultMemo.Lines.Add(FStringBuf.Strings[0]);
FStringBuf.Delete(0);
LeaveCriticalSection(StringSection);
{Now trim the Result Memo.}
if ResultMemo.Lines.Count > MaxMemoLines then
ResultMemo.Lines.Delete(0);
end;
end;
procedure TPrimeFrm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
StopBtnClick(Self);
end;
end.
Sample 13
unit PrimeThread;
interface
uses
Classes, Windows;
type
TPrimeThrd2 = class(TThread)
private
{ Private declarations }
FStartNum: integer;
function IsPrime(TestNo: integer): boolean;
protected
procedure Execute; override;
public
property StartNum: integer read FStartNum write FStartNum;
end;
implementation
uses PrimeForm2, SysUtils;
function TPrimeThrd2.IsPrime(TestNo: integer): boolean;
var
iter: integer;
begin
result := true;
if TestNo < 0 then
result := false;
if TestNo <= 2 then
exit;
iter := 2;
while (iter < TestNo) and (not terminated) do
begin
if (TestNo mod iter) = 0 then
begin
result := false;
exit;
end;
Inc(iter);
end;
end;
procedure TPrimeThrd2.Execute;
var
CurrentNum: integer;
begin
CurrentNum := FStartNum;
while not Terminated do
begin
if IsPrime(CurrentNum) then
begin
EnterCriticalSection(PrimeFrm.StringSection);
PrimeFrm.StringBuf.Add(IntToStr(CurrentNum) + ' is prime.');
LeaveCriticalSection(PrimeFrm.StringSection);
PostMessage(PrimeFrm.Handle, WM_DATA_IN_BUF, 0, 0);
end;
Inc(CurrentNum);
end;
end;
end.
Sample 14
function Read(L: TList; Index: integer): integer;
begin
if (Index > 0) and (L.Count > Index) then
begin
with L.Items[Index] do
begin
Lock;
Result := Value;
Unlock;
end;
end
else
raise ENotFound;
end;
procedure Write(L: TList; Index: integer; NewVal: integer);
begin
if (Index > 0) and (L.Count > Index) then
begin
with L.Items[Index] do
begin
Lock;
Value := NewVal;
Unlock;
end;
end
else
raise ENotFound;
end;
function Compare(L: TList; Ind1, Ind2: integer): integer;
begin
if (Ind1 > 0) and (Ind2 > 0) and (L.Count > Ind1) and (L.Count > Ind2) then
begin
L.Items[Ind1].Lock;
L.Items[Ind2}.Lock;
Result := L.Items[Ind2].Value - L.Items[Ind1].Value;
L.Items[Ind2].Unlock;
L.Items[Ind1].Unlock;
end
else
raise ENotFound;
end;
Sample 15
function CompareIndirect(L: TList; Ind1: integer): integer;
var
Ind2: integer;
begin
if (Ind1 > 0) and (L.Count > Ind1) then
begin
L.Items[Ind1].Lock;
Ind2 := L.Items[Ind1];
Assert(Ind2 <> Ind1); {I'm not even going to consider this nasty case in any more detail!}
if Ind2 > Ind1 then
L.Items[Ind2].Lock
else
begin
L.Items[Ind1].Unlock;
L.Items[Ind2].Lock;
L.Items[Ind1].Lock;
end;
Result := L.Items[Ind2].Value - L.Items[Ind1].Value;
L.Items[Ind1].Unlock;
L.Items[Ind2].Unlock;
end
else
raise ENotFound;
end;
Sample 16
function CompareIndirect(L: TList; Ind1: integer): integer;
var
Ind2: integer;
TempValue: integer;
begin
if (Ind1 > 0) and (L.Count > Ind1) then
begin
L.Items[Ind1].Lock;
Ind2 := L.Items[Ind1];
Assert(Ind2 <> Ind1); {I'm not even going to consider this nasty case in any more detail!}
if Ind2 > Ind1 then
L.Items[Ind2].Lock
else
begin
TempValue := L.Items[Ind1].Value;
L.Items[Ind1].Unlock;
L.Items[Ind2].Lock;
L.Items[Ind1].Lock;
end;
if TempValue := L.Items[Ind1].Value then
Result := L.Items[Ind2].Value - L.Items[Ind1].Value
else
{Perhaps some retry mechanism?};
L.Items[Ind1].Unlock;
L.Items[Ind2].Unlock;
end
else
raise ENotFound;
end;
Sample 17
type
TPrimeFrm = class(TForm)
{ No change here until public declarations }
public
{ Public declarations }
StringSemaphore: THandle; { Now a semaphore instead of a critical section }
property StringBuf: TStringList read FStringBuf write FStringBuf;
end;
procedure TPrimeFrm.StartBtnClick(Sender: TObject);
begin
if not FStringSectInit then
begin
StringSemaphore := CreateSemaphore(nil, 1, 1, SemName); { Now creating a semaphore instead of a critical section }
FStringBuf := TStringList.Create;
FStringSectInit := true;
FPrimeThread := TPrimeThrd2.Create(true);
SetThreadPriority(FPrimeThread.Handle, THREAD_PRIORITY_BELOW_NORMAL);
try
FPrimeThread.StartNum := StrToInt(StartNumEdit.Text);
except
on EConvertError do FPrimeThread.StartNum := 2;
end;
FPrimeThread.Resume;
end;
UpdateButtons;
end;
procedure TPrimeFrm.StopBtnClick(Sender: TObject);
begin
if FStringSectInit then
begin
with FPrimeThread do
begin
Terminate;
WaitFor;
Free;
end;
FPrimeThread := nil;
FStringBuf.Free;
FStringBuf := nil;
CloseHandle(StringSemaphore); { Deleting semaphore }
FStringSectInit := false;
end;
UpdateButtons;
end;
procedure TPrimeFrm.HandleNewData(var Message: TMessage);
begin
if FStringSectInit then {Not necessarily the case!}
begin
WaitForSingleObject(StringSemaphore, INFINITE); { New wait call }
ResultMemo.Lines.Add(FStringBuf.Strings[0]);
FStringBuf.Delete(0);
ReleaseSemaphore(StringSemaphore, 1, nil); { New release call }
{Now trim the Result Memo.}
if ResultMemo.Lines.Count > MaxMemoLines then
ResultMemo.Lines.Delete(0);
end;
end;
procedure TPrimeThrd2.Execute;
var
CurrentNum: integer;
begin
CurrentNum := FStartNum;
while not Terminated do
begin
if IsPrime(CurrentNum) then
begin
WaitForSingleObject(PrimeFrm.StringSemaphore, INFINITE); { New wait call }
PrimeFrm.StringBuf.Add(IntToStr(CurrentNum) + ' is prime.');
ReleaseSemaphore(PrimeFrm.StringSemaphore, 1, nil); { New release call }
PostMessage(PrimeFrm.Handle, WM_DATA_IN_BUF, 0, 0);
end;
Inc(CurrentNum);
end;
end;
Sample 18
unit BoundedBuf;
{Martin Harvey 24/4/2000}
interface
uses Windows, SysUtils;
const
DefaultWaitTime = 5000; { Five second wait on mutexes }
type
{ I don't particularly like dynamic arrays, so I'm going to do things
the "C" way here, explicitly allocating memory
Think of TBufferEntries as ^(array of pointer) }
TBufferEntries = ^Pointer;
TBoundedBuffer = class
private
FBufInit: boolean;
FBufSize: integer;
FBuf: TBufferEntries;
FReadPtr, { ReadPtr points to next used entry in buffer}
FWritePtr: integer; { WritePtr points to next free entry in buffer}
FEntriesFree, FEntriesUsed: THandle; { Flow control semaphores }
FCriticalMutex: THandle; { Critical section mutex }
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;
{ No constructor required because default values of 0, false etc acceptable }
implementation
const
FailMsg1 = 'Flow control failed, or buffer not initialised';
FailMsg2 = 'Critical section failed, or buffer not initialised';
procedure TBoundedBuffer.SetSize(NewSize: integer);
{ Initialises handles and allocates memory.
If the buffer size has previously been set, then this may invoke a buffer
reset }
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); { note lack of name }
{ The initial count on the semaphores requires some thought,
The maximum count requires more thought.
Again, all synchronisation objects are anonymous }
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;
{ Closes handles and deallocates memory.
Note that this must unblock threads in such a manner that they quit cleanly }
begin
if FBufInit then
begin
WaitForSingleObject(FCriticalMutex, DefaultWaitTime);
FBufInit := false;
FBufSize := 0;
FreeMem(FBuf);
{ Now wake up all threads currently waiting.
Currently assumes only 1 producer and 1 consumer.
Plenty of ordering subtleties and pitfalls to be discussed here }
ReleaseSemaphore(FEntriesFree, 1, nil);
ReleaseSemaphore(FEntriesUsed, 1, nil);
CloseHandle(FEntriesFree);
CloseHandle(FEntriesUsed);
{ If reader or writer threads are waiting,
then they will be waiting on the mutex.
We will close the handle and let them time out }
CloseHandle(FCriticalMutex);
end;
end;
function TBoundedBuffer.PutItem(NewItem: Pointer): boolean;
{ Called by producer thread }
var
NthItem: TBufferEntries;
begin
result := false;
{ WAIT(EntriesFree) }
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);
{ SIGNAL(EntriesUsed) }
ReleaseSemaphore(FEntriesUsed, 1, nil);
result := true;
end;
function TBoundedBuffer.GetItem: Pointer;
{ Called by consumer thread }
var
NthItem: TBufferEntries;
begin
result := nil;
{ WAIT(EntriesUsed) }
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);
{ SIGNAL(EntriesFree) }
ReleaseSemaphore(FEntriesFree, 1, nil);
end;
destructor TBoundedBuffer.Destroy;
begin
ResetState;
inherited Destroy;
end;
end.
Sample 19
procedure TBoundedBuffer.ResetState;
{ Closes handles and deallocates memory.
Note that this must unblock threads in such a manner that they quit cleanly }
var
SemCount: integer;
begin
if FBufInit then
begin
WaitForSingleObject(FCriticalMutex, DefaultWaitTime);
FBufInit := false;
FBufSize := 0;
FreeMem(FBuf);
repeat
ReleaseSemaphore(FEntriesFree, 1, @SemCount);
until SemCount = 0;
repeat
ReleaseSemaphore(FEntriesUsed, 1, @SemCount);
until SemCount = 0;
CloseHandle(FEntriesFree);
CloseHandle(FEntriesUsed);
CloseHandle(FCriticalMutex);
end;
end;
Sample 20
procedure TBoundedBuffer.ResetState;
{ Closes handles and deallocates memory.
Note that this must unblock threads in such a manner that they quit cleanly }
var
SemCount: integer;
LocalHandle: THandle;
begin
if FBufInit then
begin
WaitForSingleObject(FCriticalMutex, DefaultWaitTime);
FBufInit := false;
FBufSize := 0;
FreeMem(FBuf);
LocalHandle := FEntriesFree;
FEntriesFree := 0;
repeat
ReleaseSemaphore(LocalHandle, 1, @SemCount);
until SemCount = 0;
CloseHandle(LocalHandle);
LocalHandle := FEntriesUsed;
FEntriesUsed := 0;
repeat
ReleaseSemaphore(LocalHandle, 1, @SemCount);
until SemCount = 0;
CloseHandle(LocalHandle);
CloseHandle(FCriticalMutex);
end;
end;
Sample 21
unit HandleForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
THandleFrm = class(TForm)
CreateBtn: TButton;
CloseOwnerBtn: TButton;
CloseNonOwnerBtn: TButton;
procedure CreateBtnClick(Sender: TObject);
procedure CloseOwnerBtnClick(Sender: TObject);
procedure CloseNonOwnerBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Mutex: THandle;
end;
var
HandleFrm: THandleFrm;
implementation
uses HandleThreads;
{$R *.DFM}
procedure THandleFrm.CreateBtnClick(Sender: TObject);
var
NewThread: THandleThread;
begin
Mutex := CreateMutex(nil, false, nil);
WaitForSingleObject(Mutex, INFINITE);
NewThread := THandleThread.Create(false);
NewThread := THandleThread.Create(false);
ShowMessage('Threads Created.');
end;
procedure THandleFrm.CloseOwnerBtnClick(Sender: TObject);
begin
CloseHandle(Mutex);
end;
procedure THandleFrm.CloseNonOwnerBtnClick(Sender: TObject);
begin
ReleaseMutex(Mutex);
CloseHandle(Mutex);
end;
end.
unit HandleThreads;
interface
uses
Classes, Windows, SysUtils, Dialogs;
type
THandleThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;
implementation
uses HandleForm;
procedure THandleThread.Execute;
var
RetVal: integer;
begin
RetVal := WaitForSingleObject(HandleFrm.Mutex, INFINITE);
case RetVal of
WAIT_OBJECT_0: ShowMessage('Unblocked: WAIT_OBJECT_0');
WAIT_ABANDONED: ShowMessage('Unblocked: WAIT_ABANDONED');
WAIT_TIMEOUT: ShowMessage('Unblocked: WAIT_TIMEOUT');
else
ShowMessage('Unblocked. Unknown return code.');
end;
end;
end.
Sample 22
unit HandleForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
THandleFrm = class(TForm)
CreateBtn: TButton;
CloseOwnerBtn: TButton;
CloseNonOwnerBtn: TButton;
RelBtn: TButton;
procedure CreateBtnClick(Sender: TObject);
procedure CloseOwnerBtnClick(Sender: TObject);
procedure CloseNonOwnerBtnClick(Sender: TObject);
procedure RelBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Semaphore: THandle;
end;
var
HandleFrm: THandleFrm;
implementation
uses HandleThreads;
{$R *.DFM}
procedure THandleFrm.CreateBtnClick(Sender: TObject);
begin
Semaphore := CreateSemaphore(nil, 1, 1, nil);
WaitForSingleObject(Semaphore, INFINITE);
THandleThread.Create(false);
THandleThread.Create(false);
ShowMessage('Threads Created.');
end;
procedure THandleFrm.CloseOwnerBtnClick(Sender: TObject);
begin
CloseHandle(Semaphore);
end;
procedure THandleFrm.CloseNonOwnerBtnClick(Sender: TObject);
begin
ReleaseSemaphore(Semaphore, 1, nil);
CloseHandle(Semaphore);
end;
procedure THandleFrm.RelBtnClick(Sender: TObject);
begin
ReleaseSemaphore(Semaphore, 1, nil);
end;
end.
unit HandleThreads;
interface
uses
Classes, Windows, SysUtils, Dialogs;
type
THandleThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;
implementation
uses HandleForm;
procedure THandleThread.Execute;
var
RetVal: integer;
begin
RetVal := WaitForSingleObject(HandleFrm.Semaphore, 10000);
case RetVal of
WAIT_OBJECT_0: ShowMessage('Unblocked: WAIT_OBJECT_0');
WAIT_ABANDONED: ShowMessage('Unblocked: WAIT_ABANDONED');
WAIT_TIMEOUT: ShowMessage('Unblocked: WAIT_TIMEOUT');
else
ShowMessage('Unblocked. Unknown return code.');
end;
end;
end.
Sample 23
unit BoundedBuf;
{Martin Harvey 24/4/2000}
interface
uses Windows, SysUtils;
const
DefaultWaitTime = 1000; { One second wait on all synchronisation primitives }
type
{ I don't particularly like dynamic arrays, so I'm going to do things
the "C" way here, explicitly allocating memory
Think of TBufferEntries as ^(array of pointer) }
TBufferEntries = ^Pointer;
TBoundedBuffer = class
private
FBufInit: boolean;
FBufSize: integer;
FBuf: TBufferEntries;
FReadPtr, { ReadPtr points to next used entry in buffer}
FWritePtr: integer; { WritePtr points to next free entry in buffer}
FEntriesFree, FEntriesUsed: THandle; { Flow control semaphores }
FCriticalMutex: THandle; { Critical section mutex }
protected
procedure SetSize(NewSize: integer);
function ControlledWait(Semaphore: THandle): boolean;
{ Returns whether wait returned OK, or an error occurred }
public
procedure ResetState;
destructor Destroy; override;
function PutItem(NewItem: Pointer): boolean;
function GetItem: Pointer;
published
property Size: integer read FBufSize write SetSize;
end;
{ No constructor required because default values of 0, false etc acceptable }
implementation
procedure TBoundedBuffer.SetSize(NewSize: integer);
{ Initialises handles and allocates memory.
If the buffer size has previously been set, then this may invoke a buffer
reset }
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); { note lack of name }
{ The initial count on the semaphores requires some thought,
The maximum count requires more thought.
Again, all synchronisation objects are anonymous }
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;
{ Closes handles and deallocates memory.
Note that this must unblock threads in such a manner that they quit cleanly }
begin
if FBufInit then
begin
WaitForSingleObject(FCriticalMutex, DefaultWaitTime);
FBufInit := false;
FBufSize := 0;
FreeMem(FBuf);
ReleaseSemaphore(FEntriesUsed, 1, nil);
ReleaseSemaphore(FEntriesFree, 1, nil);
CloseHandle(FEntriesFree);
CloseHandle(FEntriesUsed);
ReleaseMutex(FCriticalMutex);
CloseHandle(FCriticalMutex);
end;
end;
function TBoundedBuffer.ControlledWait(Semaphore: THandle): boolean;
var
ErrCode: integer;
begin
repeat
ErrCode := WaitForSingleObject(Semaphore, DefaultWaitTime);
if (ErrCode = WAIT_OBJECT_0) or (ErrCode = WAIT_ABANDONED) then
begin
{ If wait abandoned, return failure. Buffer not properly cleaned up }
result := ErrCode = WAIT_OBJECT_0;
exit;
end;
{ Wait timed out. Check whether buffer state initialised }
if WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0 then
begin
result := false;
exit;
end
else
begin
result := FBufInit;
ReleaseMutex(FCriticalMutex);
end;
until not Result;
end;
function TBoundedBuffer.PutItem(NewItem: Pointer): boolean;
{ Called by producer thread }
var
NthItem: TBufferEntries;
begin
result := false;
{ WAIT(EntriesFree) }
if not ControlledWait(FEntriesFree) then
exit;
if (WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0)
or not FBufInit then { NB.This condition depends on L -> R lazy evaluation }
exit;
NthItem := FBuf;
Inc(NthItem, FWritePtr);
NthItem^ := NewItem;
FWritePtr := (FWritePtr + 1) mod FBufSize;
ReleaseMutex(FCriticalMutex);
{ SIGNAL(EntriesUsed) }
ReleaseSemaphore(FEntriesUsed, 1, nil);
result := true;
end;
function TBoundedBuffer.GetItem: Pointer;
{ Called by consumer thread }
var
NthItem: TBufferEntries;
begin
result := nil;
{ WAIT(EntriesUsed) }
if not ControlledWait(FEntriesUsed) then
exit;
if (WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0)
or not FBufInit then { NB.This condition depends on L -> R lazy evaluation }
exit;
NthItem := FBuf;
Inc(NthItem, FReadPtr);
Result := NthItem^;
FReadPtr := (FReadPtr + 1) mod FBufSize;
ReleaseMutex(FCriticalMutex);
{ SIGNAL(EntriesFree) }
ReleaseSemaphore(FEntriesFree, 1, nil);
end;
destructor TBoundedBuffer.Destroy;
begin
ResetState;
inherited Destroy;
end;
end.
Sample 24
unit PrimeThreads;
interface
uses
Windows, Classes, SysUtils, BoundedBuf, Forms;
type
TIntRec = record
Num: integer;
end;
PIntRec = ^TIntRec;
TPrimeThread = class(TThread)
private
FBuffer: TBoundedBuffer;
protected
function IsPrime(TestNum: integer): boolean;
public
property Buffer: TBoundedBuffer read FBuffer write FBuffer;
end;
TForwardPrimeThread = class(TPrimeThread)
private
protected
procedure SendToBackThread(TestNum: integer);
procedure Execute; override;
end;
TBackwardPrimeThread = class(TPrimeThread)
private
FDestSection: PRTLCriticalSection;
FDestMsgNum: integer;
FDestForm: TForm;
FDestList: TStrings;
protected
function ReverseNumber(Input: integer): integer;
function RecieveFromForwardThread(var TestNum: integer): boolean;
procedure SendToVCLThread(CurrentNumber, ReversedNumber: integer);
procedure Execute; override;
public
property DestSection: PRTLCriticalSection read FDestSection write FDestSection;
property DestMsgNum: integer read FDestMsgNum write FDestMsgNum;
property DestForm: TForm read FDestForm write FDestForm;
property DestList: TStrings read FDestList write FDestList;
end;
var
ForwardThread: TForwardPrimeThread;
BackwardThread: TBackwardPrimeThread;
Buffer: TBoundedBuffer;
procedure StartThreads(Form: TForm;
Section: PRTLCriticalSection;
MsgNum: integer;
List: TStrings);
procedure StopThreads;
implementation
const
DefBufSize = 16;
{ Ancillary procedures }
procedure StartThreads(Form: TForm;
Section: PRTLCriticalSection;
MsgNum: integer;
List: TStrings);
begin
ForwardThread := TForwardPrimeThread.Create(true);
BackwardThread := TBackwardPrimeThread.Create(true);
SetThreadPriority(ForwardThread.Handle, THREAD_PRIORITY_BELOW_NORMAL);
SetThreadPriority(BackwardThread.Handle, THREAD_PRIORITY_BELOW_NORMAL);
Buffer := TBoundedBuffer.Create;
Buffer.Size := DefBufSize;
ForwardThread.Buffer := Buffer;
BackwardThread.Buffer := Buffer;
with BackwardThread do
begin
DestForm := Form;
DestSection := Section;
DestMsgNum := MsgNum;
DestList := List;
end;
ForwardThread.Resume;
BackwardThread.Resume;
end;
procedure StopThreads;
begin
ForwardThread.Terminate;
BackwardThread.Terminate;
Buffer.ResetState;
ForwardThread.WaitFor;
BackwardThread.WaitFor;
Buffer.Free;
ForwardThread.Free;
BackwardThread.Free;
end;
{ TPrimeThread }
function TPrimeThread.IsPrime(TestNum: integer): boolean;
var
iter: integer;
begin
result := true;
if TestNum < 0 then
result := false;
if TestNum <= 2 then
exit;
iter := 2;
while (iter < TestNum) and (not terminated) do {Line A}
begin
if (TestNum mod iter) = 0 then
begin
result := false;
exit;
end;
Inc(iter);
end;
end;
{ TForwardPrimeThread }
procedure TForwardPrimeThread.SendToBackThread(TestNum: integer);
var
NewRec: PIntRec;
begin
New(NewRec);
NewRec.Num := TestNum;
if not Buffer.PutItem(NewRec) then Dispose(NewRec);
end;
procedure TForwardPrimeThread.Execute;
var
CurrentNumber: integer;
begin
CurrentNumber := 2;
while not Terminated do
begin
if IsPrime(CurrentNumber) then
SendToBackThread(CurrentNumber);
Inc(CurrentNumber);
end;
end;
{ TBackwardPrimeThread }
function TBackwardPrimeThread.RecieveFromForwardThread(var TestNum: integer): boolean;
var
NewRec: PIntRec;
begin
NewRec := Buffer.GetItem;
Result := Assigned(NewRec);
if Result then TestNum := NewRec^.Num;
end;
procedure TBackwardPrimeThread.SendToVCLThread(CurrentNumber, ReversedNumber: integer);
var
Msg: string;
begin
Msg := 'Palindromic primes: ' + IntToStr(CurrentNumber) + ' and '
+ IntToStr(ReversedNumber);
EnterCriticalSection(FDestSection^);
DestList.Add(Msg);
LeaveCriticalSection(FDestSection^);
PostMessage(DestForm.Handle, DestMsgNum, 0, 0);
end;
function TBackwardPrimeThread.ReverseNumber(Input: integer): integer;
var
InStr, OutStr: string;
Len, Iter: integer;
begin
Input := Abs(Input);
InStr := IntToStr(Input);
OutStr := '';
Len := Length(InStr);
for Iter := Len downto 1 do
OutStr := OutStr + InStr[Iter];
try
Result := StrToInt(OutStr);
except
on EConvertError do Result := Input;
end;
end;
procedure TBackwardPrimeThread.Execute;
var
CurrentNumber,
ReversedNumber: integer;
begin
while not Terminated do
begin
if RecieveFromForwardThread(CurrentNumber) then
begin
ReversedNumber := ReverseNumber(CurrentNumber);
if IsPrime(ReversedNumber) then
SendToVCLThread(CurrentNumber, ReversedNumber);
end;
end;
end;
end.
Sample 25
unit PalPrimeForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
const
WM_DATA_IN_BUF = WM_APP + 1000;
MaxMemoLines = 20;
type
TPalFrm = class(TForm)
ResultsMemo: TMemo;
StartButton: TButton;
StopButton: TButton;
procedure StartButtonClick(Sender: TObject);
procedure StopButtonClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
FStringSectInit: boolean;
FStringBuf: TStringList;
StringSection: TRTLCriticalSection;
procedure UpdateButtons;
procedure HandleNewData(var Message: TMessage); message WM_DATA_IN_BUF;
public
{ Public declarations }
end;
var
PalFrm: TPalFrm;
implementation
uses PrimeThreads;
{$R *.DFM}
procedure TPalFrm.UpdateButtons;
begin
StopButton.Enabled := FStringSectInit;
StartButton.Enabled := not FStringSectInit;
end;
procedure TPalFrm.StartButtonClick(Sender: TObject);
begin
if not FStringSectInit then
begin
InitializeCriticalSection(StringSection);
FStringBuf := TStringList.Create;
FStringSectInit := true;
StartThreads(Self, @StringSection, WM_DATA_IN_BUF, FStringBuf);
end;
UpdateButtons;
end;
procedure TPalFrm.StopButtonClick(Sender: TObject);
begin
if FStringSectInit then
begin
ResultsMemo.Lines.Add('Please wait...');
StopThreads;
ResultsMemo.Lines.Add('Done!');
FStringBuf.Free;
FStringBuf := nil;
DeleteCriticalSection(StringSection);
FStringSectInit := false;
end;
UpdateButtons;
end;
procedure TPalFrm.HandleNewData(var Message: TMessage);
begin
if FStringSectInit then
begin
EnterCriticalSection(StringSection);
ResultsMemo.Lines.Add(FStringBuf.Strings[0]);
FStringBuf.Delete(0);
LeaveCriticalSection(StringSection);
if ResultsMemo.Lines.Count > MaxMemoLines then
ResultsMemo.Lines.Delete(0);
end;
end;
procedure TPalFrm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
StopButtonClick(Self);
end;
end.
Sample 26
unit BoundedBuf;
{Martin Harvey 24/4/2000}
interface
uses Windows, SysUtils;
const
DefaultWaitTime = 1000; { One second wait on all synchronisation primitives }
type
{ I don't particularly like dynamic arrays, so I'm going to do things
the "C" way here, explicitly allocating memory
Think of TBufferEntries as ^(array of pointer) }
TBufferEntries = ^Pointer;
TBoundedBuffer = class
private
FBufInit: boolean;
FBufSize: integer;
FBuf: TBufferEntries;
FReadPtr, { ReadPtr points to next used entry in buffer}
FWritePtr: integer; { WritePtr points to next free entry in buffer}
FEntriesFree, FEntriesUsed: THandle; { Flow control semaphores }
FCriticalMutex: THandle; { Critical section mutex }
FEntryCountFree, FEntryCountUsed: integer; { Used for peeking operations }
protected
procedure SetSize(NewSize: integer);
function ControlledWait(Semaphore: THandle): boolean;
{ Returns whether wait returned OK, or an error occurred }
public
procedure ResetState;
destructor Destroy; override;
function PutItem(NewItem: Pointer): boolean;
function GetItem: Pointer;
{ New peeking operations. Note that we can't use simple properties, since
we have to communicate success or failure of the operation, in addition
to providing a result }
function GetEntriesFree(var Free: integer): boolean;
function GetEntriesUsed(var Used: integer): boolean;
published
property Size: integer read FBufSize write SetSize;
end;
{ No constructor required because default values of 0, false etc acceptable }
implementation
procedure TBoundedBuffer.SetSize(NewSize: integer);
{ Initialises handles and allocates memory.
If the buffer size has previously been set, then this may invoke a buffer
reset }
begin
if FBufInit then ResetState;
if NewSize < 2 then NewSize := 2;
FBufSize := NewSize;
GetMem(FBuf, Sizeof(Pointer) * FBufSize);
FillMemory(FBuf, Sizeof(Pointer) * FBufSize, 0);
FCriticalMutex := CreateMutex(nil, false, nil); { note lack of name }
WaitForSingleObject(FCriticalMutex, INFINITE);
FBufInit := true;
{ The initial count on the semaphores requires some thought,
The maximum count requires more thought.
Again, all synchronisation objects are anonymous }
FEntriesFree := CreateSemaphore(nil, FBufSize - 1, FBufSize, nil);
FEntriesUsed := CreateSemaphore(nil, 0, FBufSize, nil);
FEntryCountFree := FBufSize - 1;
FEntryCountUsed := 0;
ReleaseMutex(FCriticalMutex);
if (FCriticalMutex = 0)
or (FEntriesFree = 0)
or (FEntriesUsed = 0) then ResetState
end;
procedure TBoundedBuffer.ResetState;
{ Closes handles and deallocates memory.
Note that this must unblock threads in such a manner that they quit cleanly }
begin
if FBufInit then
begin
WaitForSingleObject(FCriticalMutex, DefaultWaitTime);
FBufInit := false;
FBufSize := 0;
FreeMem(FBuf);
ReleaseSemaphore(FEntriesUsed, 1, nil);
ReleaseSemaphore(FEntriesFree, 1, nil);
CloseHandle(FEntriesFree);
CloseHandle(FEntriesUsed);
ReleaseMutex(FCriticalMutex);
CloseHandle(FCriticalMutex);
end;
end;
function TBoundedBuffer.ControlledWait(Semaphore: THandle): boolean;
var
ErrCode: integer;
begin
repeat
ErrCode := WaitForSingleObject(Semaphore, DefaultWaitTime);
if (ErrCode = WAIT_OBJECT_0) or (ErrCode = WAIT_ABANDONED) then
begin
{ If wait abandoned, return failure. Buffer not properly cleaned up }
result := ErrCode = WAIT_OBJECT_0;
exit;
end;
{ Wait timed out. Check whether buffer state initialised }
if WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0 then
begin
result := false;
exit;
end
else
begin
result := FBufInit;
ReleaseMutex(FCriticalMutex);
end;
until not Result;
end;
function TBoundedBuffer.PutItem(NewItem: Pointer): boolean;
{ Called by producer thread }
var
NthItem: TBufferEntries;
begin
result := false;
{ WAIT(EntriesFree) }
if not ControlledWait(FEntriesFree) then
exit;
if (WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0)
or not FBufInit then { NB.This condition depends on L -> R lazy evaluation }
exit;
NthItem := FBuf;
Inc(NthItem, FWritePtr);
NthItem^ := NewItem;
FWritePtr := (FWritePtr + 1) mod FBufSize;
Inc(FEntryCountUsed);
Dec(FEntryCountFree);
ReleaseMutex(FCriticalMutex);
{ SIGNAL(EntriesUsed) }
ReleaseSemaphore(FEntriesUsed, 1, nil);
result := true;
end;
function TBoundedBuffer.GetItem: Pointer;
{ Called by consumer thread }
var
NthItem: TBufferEntries;
begin
result := nil;
{ WAIT(EntriesUsed) }
if not ControlledWait(FEntriesUsed) then
exit;
if (WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0)
or not FBufInit then { NB.This condition depends on L -> R lazy evaluation }
exit;
NthItem := FBuf;
Inc(NthItem, FReadPtr);
Result := NthItem^;
FReadPtr := (FReadPtr + 1) mod FBufSize;
Inc(FEntryCountFree);
Dec(FEntryCountUsed);
ReleaseMutex(FCriticalMutex);
{ SIGNAL(EntriesFree) }
ReleaseSemaphore(FEntriesFree, 1, nil);
end;
destructor TBoundedBuffer.Destroy;
begin
ResetState;
inherited Destroy;
end;
function TBoundedBuffer.GetEntriesFree(var Free: integer): boolean;
begin
result := false;
if (WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0)
or not FBufInit then
exit;
Free := FEntryCountFree;
result := true;
ReleaseMutex(FCriticalMutex);
end;
function TBoundedBuffer.GetEntriesUsed(var Used: integer): boolean;
begin
result := false;
if (WaitForSingleObject(FCriticalMutex, DefaultWaitTime) <> WAIT_OBJECT_0)
or not FBufInit then
exit;
Used := FEntryCountUsed;
result := true;
ReleaseMutex(FCriticalMutex);
end;
end.
Sample 27
unit BiDirBuf;
{Martin Harvey 7/5/2000}
interface
uses BoundedBuf;
type
TBufferSide = (bsSideA, bsSideB);
TBufferOp = (boWriting, boReading);
TBiDirBuf = class
private
FAtoBBuf, FBtoABuf: TBoundedBuffer;
protected
function GetBuf(Side: TBufferSide; Op: TBufferOp): TBoundedBuffer;
function GetSize: integer;
procedure SetSize(NewSize: integer);
public
constructor Create;
destructor Destroy; override;
procedure ResetState;
function PutItem(Side: TBufferSide; Item: pointer): boolean;
function GetItem(Side: TBufferSide): pointer;
{ Entries used function peeks buffer one is reading from, and
Entried free function peeks buffer one is writing to. It seems
a bit useless to allow the other two operations: why worry about
your neighbour when you have plenty else to worry about? }
function GetEntriesUsed(Side: TBufferSide; var Used: integer): boolean;
function GetEntriesFree(Side: TBufferSide; var Free: integer): boolean;
published
property Size: integer read GetSize write SetSize;
end;
implementation
{ TBiDirBuf }
constructor TBiDirBuf.Create;
begin
inherited Create;
FAToBBuf := TBoundedBuffer.Create;
FBToABuf := TBoundedBuffer.Create;
end;
destructor TBiDirBuf.Destroy;
begin
FAToBBuf.Free;
FBToABuf.Free;
inherited Destroy;
end;
procedure TBiDirBuf.ResetState;
begin
FAToBBuf.ResetState;
FBToABuf.ResetState;
end;
function TBiDirBuf.GetBuf(Side: TBufferSide; Op: TBufferOp): TBoundedBuffer;
begin
if ((Side = bsSideA) and (Op = boWriting))
or ((Side = bsSideB) and (Op = boReading)) then
result := FAToBBuf
else if ((Side = bsSideA) and (Op = boReading))
or ((Side = bsSideB) and (Op = boWriting)) then
result := FBToABuf
else
begin
result := FAToBBuf;
Assert(false);
end;
end;
function TBidirBuf.GetSize: integer;
begin
Assert(FAToBBuf.Size = FBToABuf.Size);
result := FAToBBuf.Size;
end;
procedure TBiDirBuf.SetSize(NewSize: integer);
begin
FAToBBuf.Size := NewSize;
FBToABuf.Size := NewSize;
Assert(FAToBBuf.Size = FBToABuf.Size);
end;
function TBiDirBuf.PutItem(Side: TBufferSide; Item: Pointer): boolean;
begin
result := GetBuf(Side, boWriting).PutItem(Item);
end;
function TBiDirBuf.GetItem(Side: TBufferSide): Pointer;
begin
result := GetBuf(Side, boReading).GetItem;
end;
function TBiDirBuf.GetEntriesUsed(Side: TBufferSide; var Used: integer): boolean;
begin
result := GetBuf(Side, boReading).GetEntriesUsed(Used);
end;
function TBiDirBuf.GetEntriesFree(Side: TBufferSide; var Free: integer): boolean;
begin
result := GetBuf(Side, boWriting).GetEntriesFree(Free);
end;
end.
Sample 28
unit BlockToAsyncBuf;
{ Martin Harvey 10/5/2000 }
interface
uses Classes, Forms, Messages, Windows, BiDirBuf;
const
InternalBufferSize = 4;
WM_BLOCK_ASYNC = WM_USER + 2876;
type
{ With this component, as with previous buffering schemes, one cannot read
or write nil pointers. }
TThreadNotify = (tnReaderDataFlow, tnWriterDataFlow);
TBlockAsyncThread = class(TThread)
private
FDataSection: TRTLCriticalSection;
FIdleSemaphore: THandle;
FInterimBuf: Pointer;
FOnDataFlow: TNotifyEvent;
FBuffer: TBiDirBuf;
protected
procedure DataFlow; virtual;
function GetItemsInTransit: integer;
public
constructor Create(CreateSuspended: boolean);
destructor Destroy; override;
published
property OnDataFlow: TNotifyEvent read FOnDataFlow write FOnDataFlow;
property Buffer: TBiDirBuf write FBuffer;
property ItemsInTransit: integer read GetItemsInTransit;
end;
TBAWriterThread = class(TBlockAsyncThread)
private
protected
procedure Execute; override;
public
function WriteItem(Item: Pointer): boolean;
published
end;
TBAReaderThread = class(TBlockAsyncThread)
private
protected
procedure Execute; override;
public
function ReadItem: pointer;
published
end;
TBlockToAsyncBuf = class(TComponent)
private
FHWND: THandle;
FBuffer: TBiDirBuf;
FReaderThread: TBAReaderThread;
FWriterThread: TBAWriterThread;
FOnRead, FOnWrite: TNotifyEvent;
protected
procedure MessageHandler(var Msg: TMessage);
procedure ReaderDataFlow(Sender: TObject);
procedure WriterDataFlow(Sender: TObject);
procedure Read; virtual;
procedure Write; virtual;
function GetItemsInTransit: integer;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function BlockingRead: pointer;
function BlockingWrite(Item: pointer): boolean;
function AsyncRead: pointer;
function AsyncWrite(Item: pointer): boolean;
procedure ResetState;
published
property OnRead: TNotifyEvent read FOnRead write FOnRead;
property OnWrite: TNotifyEvent read FOnWrite write FOnWrite;
property ItemsInTransit: integer read GetItemsInTransit;
end;
implementation
procedure TBlockAsyncThread.DataFlow;
begin
if Assigned(FOnDataFlow) then FOnDataFlow(Self);
end;
constructor TBlockAsyncThread.Create(CreateSuspended: boolean);
begin
inherited Create(CreateSuspended);
InitializeCriticalSection(FDataSection);
FIdleSemaphore := CreateSemaphore(nil, 0, High(Integer), nil);
end;
destructor TBlockAsyncThread.Destroy;
begin
ReleaseSemaphore(FIdleSemaphore, 1, nil);
WaitFor;
DeleteCriticalSection(FDataSection);
CloseHandle(FIdleSemaphore);
end;
function TBlockAsyncThread.GetItemsInTransit: integer;
begin
EnterCriticalSection(FDataSection);
if Assigned(FInterimBuf) then
result := 1
else
result := 0;
LeaveCriticalSection(FDataSection);
end;
{ Buffer error handling needs to be discussed }
procedure TBAWriterThread.Execute;
var
Temp: Pointer;
begin
while not Terminated do
begin
DataFlow;
WaitForSingleObject(FIdleSemaphore, INFINITE);
EnterCriticalSection(FDataSection);
Temp := FInterimBuf;
FInterimBuf := nil;
LeaveCriticalSection(FDataSection);
if not FBuffer.PutItem(bsSideA, Temp) then Terminate;
end;
end;
function TBAWriterThread.WriteItem(Item: Pointer): boolean;
begin
result := false;
EnterCriticalSection(FDataSection);
if not Assigned(FInterimBuf) then
begin
FInterimBuf := Item;
result := true;
end;
LeaveCriticalSection(FDataSection);
if Result then ReleaseSemaphore(FIdleSemaphore, 1, nil);
end;
procedure TBAReaderThread.Execute;
var
Temp: Pointer;
begin
while not Terminated do
begin
Temp := FBuffer.GetItem(bsSideA);
if Assigned(Temp) then
begin
EnterCriticalSection(FDataSection);
FInterimBuf := Temp;
LeaveCriticalSection(FDataSection);
DataFlow;
WaitForSingleObject(FIdleSemaphore, INFINITE);
end
else Terminate;
end;
end;
function TBAReaderThread.ReadItem: pointer;
begin
EnterCriticalSection(FDataSection);
result := FInterimBuf;
LeaveCriticalSection(FDataSection);
if Assigned(Result) then ReleaseSemaphore(FIdleSemaphore, 1, nil);
end;
procedure TBlockToAsyncBuf.MessageHandler(var Msg: TMessage);
begin
if (Msg.Msg = WM_BLOCK_ASYNC) then
begin
case TThreadNotify(Msg.LParam) of
tnReaderDataflow: Read;
tnWriterDataflow: Write;
else
Assert(false);
end;
end;
end;
procedure TBlockToAsyncBuf.ReaderDataFlow(Sender: TObject);
begin
PostMessage(FHWND, WM_BLOCK_ASYNC, 0, Integer(tnReaderDataflow));
end;
procedure TBlockToAsyncBuf.WriterDataFlow(Sender: TObject);
begin
PostMessage(FHWND, WM_BLOCK_ASYNC, 0, Integer(tnWriterDataflow));
end;
procedure TBlockToAsyncBuf.Read;
begin
if Assigned(FOnRead) then FOnRead(Self);
end;
procedure TBlockToAsyncBuf.Write;
begin
if Assigned(FOnWrite) then FOnWrite(Self);
end;
constructor TBlockToAsyncBuf.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FHWND := AllocateHWnd(MessageHandler);
FBuffer := TBiDirBuf.Create;
FBuffer.Size := InternalBufferSize;
FReaderThread := TBAReaderThread.Create(true);
FReaderThread.Buffer := Self.FBuffer;
FReaderThread.OnDataFlow := ReaderDataFlow;
FWriterThread := TBAWriterThread.Create(true);
FWriterThread.Buffer := Self.FBuffer;
FWriterThread.OnDataFlow := WriterDataFlow;
FReaderThread.Resume;
FWriterThread.Resume;
end;
procedure TBlockToAsyncBuf.ResetState;
begin
if Assigned(FReaderThread) then FReaderThread.Terminate;
if Assigned(FWriterThread) then FWriterThread.Terminate;
FBuffer.ResetState;
FReaderThread.Free;
FWriterThread.Free;
FReaderThread := nil;
FWriterThread := nil;
end;
destructor TBlockToAsyncBuf.Destroy;
begin
{ A few destruction subtleties here }
ResetState;
FBuffer.Free;
DeallocateHWnd(FHWND);
inherited Destroy;
end;
function TBlockToAsyncBuf.BlockingRead: pointer;
begin
result := FBuffer.GetItem(bsSideB);
end;
function TBlockToAsyncBuf.BlockingWrite(Item: pointer): boolean;
begin
result := FBuffer.PutItem(bsSideB, Item);
end;
function TBlockToAsyncBuf.AsyncRead: pointer;
begin
result := FReaderThread.ReadItem;
end;
function TBlockToAsyncBuf.AsyncWrite(Item: pointer): boolean;
begin
result := FWriterThread.WriteItem(Item);
end;
function TBlockToAsyncBuf.GetItemsInTransit: integer;
var
Entries: integer;
begin
result := FReaderThread.ItemsInTransit + FWriterThread.ItemsInTransit;
if FBuffer.GetEntriesUsed(bsSideA, Entries) then
Inc(result, Entries);
if FBuffer.GetEntriesUsed(bsSideB, Entries) then
Inc(result, Entries);
end;
end.
Sample 29
unit BlockAsyncForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, BlockToAsyncBuf, PrimeRangeThread;
const
MaxCount = 20;
type
TBlockAsyncFrm = class(TForm)
Label1: TLabel;
StartRangeEdit: TEdit;
Label2: TLabel;
EndRangeEdit: TEdit;
SubmitBtn: TButton;
ResultsMemo: TMemo;
procedure FormCreate(Sender: TObject);
procedure SubmitBtnClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FItemsInTransit: integer;
FBuf: TBlockToAsyncBuf;
FWorkerThread: TPrimeRangeThread;
procedure BufRead(Sender: TObject);
procedure BufWrite(Sender: TObject);
public
{ Public declarations }
end;
var
BlockAsyncFrm: TBlockAsyncFrm;
implementation
{$R *.DFM}
procedure TBlockAsyncFrm.FormCreate(Sender: TObject);
begin
FWorkerThread := TPrimeRangeThread.Create(true);
FBuf := TBlockToAsyncBuf.Create(Self);
with FBuf do
begin
{ Note that these changes will take effect before
events from this component could possibly occur }
OnRead := BufRead;
OnWrite := BufWrite;
end;
SetThreadPriority(FWorkerThread.Handle, THREAD_PRIORITY_BELOW_NORMAL);
with FWorkerThread do
begin
Buf := FBuf;
Resume;
end;
end;
procedure TBlockAsyncFrm.SubmitBtnClick(Sender: TObject);
var
Request: PRangeRequestType;
Temp: integer;
begin
New(Request);
try
Request.Low := StrToInt(StartRangeEdit.Text);
Request.High := StrToInt(EndRangeEdit.Text);
if Request.Low > Request.High then
begin
Temp := Request.Low;
Request.Low := Request.High;
Request.High := Temp;
end;
if FBuf.AsyncWrite(Request) then
begin
Request := nil;
SubmitBtn.Enabled := false;
Inc(FItemsInTransit);
end;
finally
if Assigned(Request) then Dispose(Request);
end;
end;
procedure TBlockAsyncFrm.BufWrite(Sender: TObject);
begin
{ Buffer has indicated that there is space for us to write }
SubmitBtn.Enabled := true;
end;
procedure TBlockAsyncFrm.BufRead(Sender: TObject);
var
Reply: TStringList;
begin
{ We have received a notification that we may read. }
Reply := TStringList(FBuf.AsyncRead);
if Assigned(Reply) then
begin
Dec(FItemsInTransit);
ResultsMemo.Lines.BeginUpdate;
ResultsMemo.Lines.AddStrings(Reply);
while ResultsMemo.Lines.Count > MaxCount do
ResultsMemo.Lines.Delete(0);
ResultsMemo.Lines.EndUpdate;
end;
end;
procedure TBlockAsyncFrm.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
begin
CanClose := true;
if FItemsInTransit > 0 then
if MessageDlg('Some requests in transit, close anyway?', mtWarning,
mbOKCancel, 0) <> mrOK then
CanClose := false;
end;
procedure TBlockAsyncFrm.FormDestroy(Sender: TObject);
begin
FWorkerThread.Terminate;
FBuf.ResetState;
FWorkerThread.WaitFor;
FBuf.Free;
FWorkerThread.Free;
end;
end.
Sample 30
unit PrimeRangeThread;
interface
uses
Classes, BlockToAsyncBuf;
type
TPrimeRangeThread = class(TThread)
private
{ Private declarations }
FBuf: TBlockToAsyncBuf;
protected
function IsPrime(TestNum: integer): boolean;
procedure Execute; override;
public
published
property Buf: TBlockToAsyncBuf read FBuf write FBuf;
end;
TRangeRequestType = record
Low, High: integer;
end;
PRangeRequestType = ^TRangeRequestType;
{ Results returned in a string list }
implementation
uses SysUtils;
{ TPrimeRangeThread }
function TPrimeRangeThread.IsPrime(TestNum: integer): boolean;
var
iter: integer;
begin
result := true;
if TestNum < 0 then
result := false;
if TestNum <= 2 then
exit;
iter := 2;
while (iter < TestNum) and (not terminated) do {Line A}
begin
if (TestNum mod iter) = 0 then
begin
result := false;
exit;
end;
Inc(iter);
end;
end;
procedure TPrimeRangeThread.Execute;
var
PRange: PRangeRequestType;
TestNum: integer;
Results: TStringList;
begin
while not Terminated do
begin
PRange := PRangeRequestType(FBuf.BlockingRead);
if Assigned(PRange) then
begin
Assert(PRange.Low <= PRange.High);
Results := TStringList.Create;
Results.Add('Primes from: ' + IntToStr(PRange.Low) +
' to: ' + IntToStr(PRange.High));
for TestNum := PRange.Low to PRange.High do
begin
if IsPrime(TestNum) then
Results.Add(IntToStr(TestNum) + ' is prime.');
end;
if not FBuf.BlockingWrite(Results) then
begin
Results.Free;
Terminate;
end;
end
else Terminate;
end;
end;
end.
Sample 31
unit SimpleSync;
{ Martin Harvey 27/5/2000 }
interface
uses Windows;
type
TSimpleSynchronizer = class(TObject)
private
FDataLock, FWriteLock: TRTLCriticalSection;
FActRead, FReadRead, FActWrite, FWriteWrite: integer;
FReaderSem, FWriterSem: THandle;
protected
public
constructor Create;
destructor Destroy; override;
procedure StartRead;
procedure StartWrite;
procedure EndRead;
procedure EndWrite;
published
end;
implementation
constructor TSimpleSynchronizer.Create;
begin
inherited Create;
InitializeCriticalSection(FDataLock);
InitializeCriticalSection(FWriteLock);
FReaderSem := CreateSemaphore(nil, 0, High(Integer), nil);
FWriterSem := CreateSemaphore(nil, 0, High(Integer), nil);
{ Initial values of 0 OK for all counts }
end;
destructor TSimpleSynchronizer.Destroy;
begin
DeleteCriticalSection(FDataLock);
DeleteCriticalSection(FWriteLock);
CloseHandle(FReaderSem);
CloseHandle(FWriterSem);
inherited Destroy;
end;
procedure TSimpleSynchronizer.StartRead;
begin
EnterCriticalSection(FDataLock);
Inc(FActRead);
if FActWrite = 0 then
begin
Inc(FReadRead);
ReleaseSemaphore(FReaderSem, 1, nil);
end;
LeaveCriticalSection(FDataLock);
WaitForSingleObject(FReaderSem, INFINITE);
end;
procedure TSimpleSynchronizer.StartWrite;
begin
EnterCriticalSection(FDataLock);
Inc(FActWrite);
if FReadRead = 0 then
begin
Inc(FWriteWrite);
ReleaseSemaphore(FWriterSem, 1, nil);
end;
LeaveCriticalSection(FDataLock);
WaitForSingleObject(FWriterSem, INFINITE);
EnterCriticalSection(FWriteLock);
end;
procedure TSimpleSynchronizer.EndRead;
begin
EnterCriticalSection(FDataLock);
Dec(FReadRead);
Dec(FActRead);
if FReadRead = 0 then
begin
while FWriteWrite < FActWrite do
begin
Inc(FWriteWrite);
ReleaseSemaphore(FWriterSem, 1, nil);
end;
end;
LeaveCriticalSection(FDataLock);
end;
procedure TSimpleSynchronizer.EndWrite;
begin
LeaveCriticalSection(FWriteLock);
EnterCriticalSection(FDataLock);
Dec(FWriteWrite);
Dec(FActWrite);
if FActWrite = 0 then
begin
while FReadRead < FActRead do
begin
Inc(FReadRead);
ReleaseSemaphore(FReaderSem, 1, nil);
end;
end;
LeaveCriticalSection(FDataLock);
end;
end.
Sample 32
unit ChecksumList;
{ Martin Harvey 29/5/2000 }
interface
uses SimpleSync, Classes, SysUtils;
type
TChecksumList = class
private
FCheckList: TList;
FSync: TSimpleSynchronizer;
protected
{ Find function returns -1 if not found }
function FindFileIndex(FileName: string): integer;
function NoLockGetFileList: TStringList;
function NoLockGetChecksum(FileName: string): integer;
public
constructor Create;
destructor Destroy; override;
procedure SetChecksum(FileName: string; Checksum: integer);
procedure RemoveChecksum(FileName: string);
function GetChecksum(FileName: string): integer;
function GetFileList: TStringList;
function GetChecksumList: TStringList;
end;
implementation
type
TCheckSum = record
FileName: string;
Checksum: integer;
end;
PCheckSum = ^TCheckSum;
constructor TChecksumList.Create;
begin
inherited Create;
FCheckList := TList.Create;
FSync := TSimpleSynchronizer.Create;
end;
destructor TCheckSumList.Destroy;
var
iter: integer;
CurSum: PCheckSum;
begin
if FCheckList.Count > 0 then
begin
for iter := 0 to FCheckList.Count - 1 do
begin
CurSum := PCheckSum(FCheckList.Items[iter]);
if Assigned(CurSum) then Dispose(CurSum);
end;
end;
FCheckList.Free;
FSync.Free;
inherited Destroy;
end;
function TCheckSumList.FindFileIndex(FileName: string): integer;
var
iter: integer;
CurSum: PCheckSum;
begin
result := -1;
if FCheckList.Count > 0 then
begin
for iter := 0 to FCheckList.Count - 1 do
begin
CurSum := PCheckSum(FCheckList.Items[iter]);
Assert(Assigned(CurSum));
if AnsiCompareText(FileName, CurSum.FileName) = 0 then
begin
result := iter;
exit;
end;
end;
end;
end;
procedure TCheckSumList.SetChecksum(FileName: string; Checksum: integer);
var
CurSum: PCheckSum;
CurIndex: integer;
begin
FSync.StartWrite;
CurIndex := FindFileIndex(FileName);
if CurIndex >= 0 then
CurSum := PCheckSum(FCheckList.Items[CurIndex])
else
begin
New(CurSum);
FCheckList.Add(CurSum);
end;
CurSum.FileName := FileName;
CurSum.Checksum := Checksum;
FSync.EndWrite;
end;
procedure TCheckSumList.RemoveChecksum(FileName: string);
var
CurIndex: integer;
begin
FSync.StartWrite;
CurIndex := FindFileIndex(FileName);
if CurIndex >= 0 then
begin
FCheckList.Delete(CurIndex);
FCheckList.Pack;
end;
FSync.EndWrite;
end;
function TCheckSumList.NoLockGetChecksum(FileName: string): integer;
var
CurIndex: integer;
CurSum: PCheckSum;
begin
result := 0;
CurIndex := FindFileIndex(FileName);
if CurIndex >= 0 then
begin
CurSum := PCheckSum(FCheckList.Items[CurIndex]);
Assert(Assigned(CurSum));
result := CurSum.Checksum;
end;
end;
function TCheckSumList.GetChecksum(FileName: string): integer;
begin
FSync.StartRead;
result := NoLockGetChecksum(FileName);
FSync.EndRead;
end;
function TCheckSumList.NoLockGetFileList: TStringList;
var
iter: integer;
CurSum: PCheckSum;
begin
result := TStringList.Create;
if FCheckList.Count > 0 then
begin
for iter := 0 to FCheckList.Count - 1 do
begin
CurSum := PCheckSum(FCheckList.Items[iter]);
Assert(Assigned(CurSum));
result.Add(CurSum.FileName);
end;
end;
result.Sort;
end;
function TCheckSumList.GetFileList: TStringList;
begin
FSync.StartRead;
result := NoLockGetFileList;
FSync.EndRead;
end;
function TCheckSumList.GetChecksumList: TStringList;
var
iter: integer;
begin
FSync.StartRead;
result := NoLockGetFileList;
if result.Count > 0 then
begin
for iter := 0 to result.Count - 1 do
begin
result.strings[iter] := result.strings[iter]
+ ' ' + IntToStr(NoLockGetChecksum(result.strings[iter]));
end;
end;
FSync.EndRead;
end;
end.
Sample 33
unit CheckThread;
{ Martin Harvey 30/5/2000 }
interface
uses
Classes, Windows, ChecksumList, SysUtils;
type
TState = (sGetCurrentCRCs,
sBuildFileList,
sRemoveCRCs,
sCheckFile,
sDone);
TStateReturn = (rvOK, rvFail1, rvFail2);
TActionFunc = function: TStateReturn of object;
TStateActions = array[TState] of TActionFunc;
TNextStates = array[TState, TStateReturn] of TState;
TCheckThread = class(TThread)
private
FStartDir: string;
FCurrentState: TState;
FActionFuncs: TStateActions;
FNextStates: TNextStates;
FInternalFileList: TStringList;
FExternalFileList: TStringList;
FExternalCRCList: TStringList;
FCheckList: TChecksumList;
FFileToProcess: integer;
protected
procedure InitActionFuncs;
procedure InitNextStates;
function GetCurrentCRCs: TStateReturn;
function BuildFileList: TStateReturn;
function RemoveCRCs: TStateReturn;
function CheckFile: TStateReturn;
procedure Execute; override;
public
constructor Create(CreateSuspended: boolean);
destructor Destroy; override;
property StartDir: string read FStartDir write FStartDir;
property CheckList: TChecksumList read FCheckList write FCheckList;
end;
implementation
{ TCheckThread }
{(*} {Prettyprinter auto-formatting off}
const
BaseStateTransitions:TNextStates = (
{rvOK} {rvFail1} {rvFail2}
{sGetCurrentCRCs } ( sBuildFileList, sDone, sDone ),
{sBuildFileList } ( sRemoveCRCs, sDone, sDone ),
{sRemoveCRCs } ( sCheckFile, sDone, sDone ),
{sCheckFile } ( sCheckFile, sGetCurrentCRCs, sDone ),
{sDone } ( sDone, sDone, sDone ));
{*)}{Prettyprinter auto-formatting on}
procedure TCheckThread.InitActionFuncs;
begin
FActionFuncs[sGetCurrentCRCs] := GetCurrentCRCs;
FActionFuncs[sBuildFileList] := BuildFileList;
FActionFuncs[sRemoveCRCs] := RemoveCRCs;
FActionFuncs[sCheckFile] := CheckFile;
end;
procedure TCheckThread.InitNextStates;
begin
FNextStates := BaseStateTransitions;
end;
function TCheckThread.GetCurrentCRCs: TStateReturn;
begin
FExternalFileList.Free;
FExternalFileList := nil;
FExternalCRCList.Free;
FExternalCRCList := nil;
FExternalFileList := FCheckList.GetFileList;
FExternalCRCList := FCheckList.GetChecksumList;
result := rvOK;
end;
function TCheckThread.BuildFileList: TStateReturn;
var
FindRet: integer;
SearchRec: TSearchRec;
begin
FInternalFileList.Clear;
FindRet := FindFirst(StartDir + '*.*', faAnyFile and not faDirectory, SearchRec);
if FindRet <> 0 then
result := rvFail1
else
begin
while FindRet = 0 do
begin
{ Found a file.}
FInternalFileList.Add(SearchRec.Name);
FindRet := FindNext(SearchRec);
end;
result := rvOK;
end;
FindClose(SearchRec);
FFileToProcess := 0;
end;
function TCheckThread.RemoveCRCs: TStateReturn;
var
iter: integer;
dummy: integer;
begin
FInternalFileList.Sort;
FExternalFileList.Sort;
if FExternalFileList.Count > 0 then
begin
for iter := 0 to FExternalFileList.Count - 1 do
begin
if not FInternalFileList.Find(FExternalFileList[iter], dummy) then
FCheckList.RemoveChecksum(FExternalFileList[iter]);
end;
end;
result := rvOK;
end;
function TCheckThread.CheckFile: TStateReturn;
var
FileData: TFileStream;
MemImage: TMemoryStream;
Data: byte;
Sum: integer;
iter: integer;
begin
if FFileToProcess >= FInternalFileList.Count then
begin
result := rvFail1;
exit;
end;
Sum := 0;
FileData := nil;
MemImage := nil;
try
FileData := TFileStream.Create(StartDir + FInternalFileList[FFileToProcess],
fmOpenRead or fmShareDenyWrite);
FileData.Seek(0, soFromBeginning);
MemImage := TMemoryStream.Create;
MemImage.CopyFrom(FileData, FileData.Size);
MemImage.Seek(0, soFromBeginning);
for iter := 1 to FileData.Size do
begin
MemImage.ReadBuffer(Data, sizeof(Data));
Inc(Sum, Data);
end;
FileData.Free;
MemImage.Free;
if (FCheckList.GetChecksum(FInternalFileList[FFileToProcess]) <> Sum) then
FCheckList.SetChecksum(FInternalFileList[FFileTOProcess], Sum);
except
on EStreamError do
begin
FileData.Free;
MemImage.Free;
end;
end;
Inc(FFileToProcess);
result := rvOK;
end;
procedure TCheckThread.Execute;
begin
SetThreadPriority(Handle, THREAD_PRIORITY_IDLE);
while not (Terminated or (FCurrentState = sDone)) do
FCurrentState := FNextStates[FCurrentState, FActionFuncs[FCurrentState]];
end;
constructor TCheckThread.Create(CreateSuspended: boolean);
begin
inherited Create(CreateSuspended);
InitActionFuncs;
InitNextStates;
FInternalFileList := TStringList.Create;
end;
destructor TCheckThread.Destroy;
begin
FInternalFileList.Free;
FExternalFileList.Free;
FExternalCRCList.Free;
inherited Destroy;
end;
end.
Sample 34
unit SyncForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, ChecksumList, CheckThread;
type
TForm1 = class(TForm)
FileMemo: TMemo;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
FChecksumList: TChecksumList;
FCheckThread: TCheckThread;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
FChecksumList := TChecksumList.Create;
FCheckThread := TCheckThread.Create(true);
with FCheckThread do
begin
StartDir := 'D:\Netscape Profiles\Martin\News\host-newsgroups.borland.com\';
CheckList := FChecksumList;
Resume;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
with FCheckThread do
begin
Terminate;
WaitFor;
Free;
end;
FChecksumList.Free;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
TempList: TStringList;
begin
TempList := FChecksumList.GetChecksumList;
with FileMemo do
begin
with Lines do
begin
BeginUpdate;
Assign(TempList);
EndUpdate;
end;
selstart := gettextlen;
perform(em_scrollcaret, 0, 0);
end;
TempList.Free;
end;
end.
Sample 35
unit SimulatedEvent;
{ Martin Harvey 4/6/2000 }
interface
uses Windows;
type
TSimulatedEvent = class
private
FBlockCount: integer;
FSignalled: boolean;
FDataSection: TRTLCriticalSection;
FBlockSem: THandle;
protected
public
constructor Create(CreateSignalled: boolean);
destructor Destroy; override;
procedure SetEvent;
procedure ResetEvent;
procedure PulseEvent;
procedure WaitFor;
published
end;
implementation
constructor TSimulatedEvent.Create(CreateSignalled: boolean);
begin
inherited Create;
FSignalled := CreateSignalled;
InitializeCriticalSection(FDataSection);
FBlockSem := CreateSemaphore(nil, 0, High(Integer), nil);
end;
destructor TSimulatedEvent.Destroy;
begin
DeleteCriticalSection(FDataSection);
CloseHandle(FBlockSem);
inherited Destroy;
end;
procedure TSimulatedEvent.SetEvent;
begin
EnterCriticalSection(FDataSection);
FSignalled := true;
while FBlockCount > 0 do
begin
ReleaseSemaphore(FBlockSem, 1, nil);
Dec(FBlockCount);
end;
LeaveCriticalSection(FDataSection);
end;
procedure TSimulatedEvent.ResetEvent;
begin
EnterCriticalSection(FDataSection);
FSignalled := false;
LeaveCriticalSection(FDataSection);
end;
procedure TSimulatedEvent.PulseEvent;
begin
EnterCriticalSection(FDataSection);
while FBlockCount > 0 do
begin
ReleaseSemaphore(FBlockSem, 1, nil);
Dec(FBlockCount);
end;
LeaveCriticalSection(FDataSection);
end;
procedure TSimulatedEvent.WaitFor;
begin
EnterCriticalSection(FDataSection);
if FSignalled then
begin
Dec(FBlockCOunt);
ReleaseSemaphore(FBlockSem, 1, nil);
end;
Inc(FBlockCount);
LeaveCriticalSection(FDataSection);
WaitForSingleObject(FBlockSem, INFINITE);
end;
end.
Sample 36
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.
Sample 37
var
Lock: integer;
{Lock Initialization}
Lock := -1;
{Entering the spin lock}
while InterlockedIncrement(Lock) > 0 do
begin
Dec(Lock);
Sleep(0);
end;
{Leaving the spin lock}
Dec(Lock);
Sample 38
procedure DoSomethingCritical(var Lock: integer);
var
Temp: integer;
begin
{ Initialize lock }
Lock := -1;
{ Enter Lock }
repeat
Inc(Lock);
Temp := Lock;
if Temp > 0 then
Dec(Lock);
until not (Temp > 0);
{ Perform operations }
{ Leave Lock }
Dec(Lock);
end;
procedure AsmDoSomethingCritical(var Lock: integer);
asm
{ Initialize lock }
lock mov dword ptr[eax],$FFFFFFFF
{ Enter Lock }
@spin:
lock inc dword ptr[eax]
mov edx,[eax]
test edx,edx
jng @skipdec
lock dec dword ptr[eax]
@skipdec:
test edx,edx
jg @spin
{ Perform operations }
{ Leave Lock }
lock dec dword ptr[eax]
end;
Sample 39
type
TEventCount = class
private
protected
public
constructor Create;
destructor Destroy; override;
function Advance: integer;
function Read: integer;
procedure Await(WaitCount: integer);
published
end;
TSequencer = class
private
protected
public
constructor Create;
destructor Destroy; override;
function Ticket: integer;
published
end;
Sample 40
{ Enforcing a mutual exclusion }
var
MyTurn: integer;
EC: TEventCount;
S: TSequencer; { assume already created appropriately }
begin
MyTurn := S.Ticket;
EC.Await(MyTurn);
{ Critical operations }
EC.Advance;
end;
Sample 41
{ Single producer consumer bounded buffer }
{ buffer has N slots }
var
InE, OutE: TEventCount; { Set up and initially 0 }
{ producer }
var
I: integer;
begin
while not terminated do
begin
OutE.Await(I - N);
{ insert item at I mod N }
InE.Advance;
Inc(I);
end;
end;
{ consumer }
var
I: integer;
begin
while not terminated do
begin
InE.Await(I);
{ remove item at i mod N }
OutE.Advance;
end;
end;
Sample 42
{ Bounded buffer with multiple producers and consumers }
var
InE, OutE: TEventCount; { Set up and initially 0 }
PrTk, CnTk: TSequencer; { Set up and initially 0 }
{ producer }
var
MyTurn: integer;
begin
while not terminated do
begin
MyTurn := PrTk.Ticket;
InE.Await(MyTurn);
OutE.Await(MyTurn - N + 1);
{ insert item at myturn mod N }
InE.Advance;
end;
end;
{ consumer }
var
MyTurn: integer;
begin
while not terminated do
begin
MyTurn := CnTk.Ticket;
OutE.Await(MyTurn);
InE.Await(MyTurn + 1);
{ remove item at MyTurn mod N }
OutE.Advance;
end;
end;