fpc/packages/rtl-objpas/tests/utcfpmonitor.pas

368 lines
9.1 KiB
ObjectPascal

unit utcfpmonitor;
{$mode ObjFPC}{$H+}
{ $DEFINE DEBUG_MONITOR}
interface
uses
Classes, SysUtils, fpcunit, testregistry,
{$ifdef windows}
fpwinmonitor
{$else}
fpmonitor
{$endif};
const
WaitPeriod = 10;
WaitTimeout = 2000;
MaxObjCount = 2;
MaxThrdCount = 5;
Type
TThreadOperation = (toNone,toEnter,toTryEnter,toExit,toPulse,toWait,toPulseAll);
TOperationResult = Record
Op : TThreadOperation;
Tick : Int64;
Res : Boolean;
end;
TTestObject = Class(TObject)
// Operation/Timestamp when a thread performed a task
Res : Array[1..MaxThrdCount] of TOperationResult;
end;
{ TTestThread }
TTestThread = Class(TThread)
Private
FObj : TTestObject;
FOperation : TThreadOperation;
FTimeout : Integer;
FID : Integer;
Constructor Create(aObj : TTestObject; aOperation : TThreadOperation; aId,aTimeout : Integer; aOnFree : TNotifyEvent);
Public
Procedure Execute; override;
end;
{ TTestMonitorSupport }
TTestMonitorSupport = Class(TTestCase)
private
FThrdCount : Integer;
FObj : Array[1..MaxObjCount] of TTestObject;
FThrd : Array[1..MaxThrdCount] of TThread;
function DoCreateThread(aObj: TTestObject; aOperation: TThreadOperation; aId, aTimeout: Integer): TTestThread;
class procedure AssertEquals(Msg: String; aExpected, aActual: TThreadOperation); overload;
function GetObj(AIndex: Integer): TTestObject;
procedure ThreadDone(Sender : TObject);
procedure WaitForAllThreads(aTimeOut: Integer=0);
public
Procedure Setup; override;
Procedure TearDown; override;
Property Obj1 : TTestObject Index 1 Read GetObj;
Property Obj2 : TTestObject Index 2 Read GetObj;
Published
Procedure TestHookup;
Procedure TestLock;
Procedure TestLockMulti;
Procedure TestTryLock;
Procedure TestPulse;
Procedure TestPulseAll;
procedure TestWait;
end;
implementation
Uses TypInfo;
{ TTestThread }
constructor TTestThread.Create(aObj: TTestObject; aOperation: TThreadOperation; aId,aTimeout: Integer; aOnFree : TNotifyEvent);
begin
FObj:=aObj;
FOperation:=aOperation;
FTimeout:=aTimeout;
FID:=aID;
FreeOnTerminate:=True;
OnTerminate:=aOnfree;
Inherited Create(false);
end;
procedure TTestThread.Execute;
var
OpRes : TOperationResult;
begin
{$IFDEF DEBUG_MONITOR} Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' Begin executing operation ',FOperation);{$ENDIF}
try
OpRes.Res:=True;
Case FOperation of
toEnter : TMonitor.Enter(Fobj);
toTryEnter : OpRes.Res:=TMonitor.TryEnter(Fobj);
toExit : TMonitor.Exit(Fobj);
toPulse : begin
Sleep(WaitPeriod * 2);
TMonitor.Pulse(Fobj);
end;
toPulseAll :
begin
TMonitor.Enter(Fobj);
OpRes.Res:=TMonitor.Wait(FObj,FTimeout);
end;
toWait :
begin
TMonitor.Enter(Fobj);
OpRes.Res:=TMonitor.Wait(FObj,FTimeout);
end;
end;
OpRes.Tick:=GetTickCount64;
OpRes.Op:=FOperation;
FObj.Res[FID]:=OpRes;
// We need to clean up !
Case FOperation of
toEnter,
toWait,
toPulseAll,
toTryEnter:
begin
if OpRes.Res then
begin
TMonitor.Exit(Fobj);
{$IFDEF DEBUG_MONITOR} Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' Unlocking previously locked object ',FOperation);{$ENDIF}
end;
end;
else
//
end;
except
On E : Exception do
Writeln(StdErr,GetTickCount64,': Thread ',ptruint(GetCurrentThreadID),' exception ',E.ClassName,' during operation ',FOperation,' : ',E.Message);
end;
{$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' End executing operation ',FOperation);{$ENDIF}
end;
{ TTestMonitorSupport }
function TTestMonitorSupport.GetObj(AIndex: Integer): TTestObject;
begin
Result:=FObj[aIndex];
end;
procedure TTestMonitorSupport.ThreadDone(Sender: TObject);
var
aCount,I : Integer;
begin
aCount:=0;
{$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' Begin done executing');{$ENDIF}
For I:=1 to MaxThrdCount do
begin
if FThrd[i]=Sender then
begin
{$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' Done executing: found thread at ',I){$ENDIF};
FThrd[i]:=Nil;
end
else if assigned(FThrd[I]) then
inc(aCount);
end;
{$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' End done executing. Threads still active: ',aCount);{$ENDIF}
end;
procedure TTestMonitorSupport.WaitForAllThreads(aTimeOut : Integer = 0);
var
I : Integer;
Last,Start : Int64;
TimeOut,OK : Boolean;
begin
If aTimeOut=0 then
aTimeout:=WaitTimeout;
Start:=GetTickCount64;
{$IFDEF DEBUG_MONITOR} Writeln(StdErr,Start,': Thread ',GetCurrentThreadID,' Waiting for ', FThrdCount,' threads to stop');{$ENDIF}
Timeout:=False;
Repeat
OK:=True;
CheckSynchronize(5);
For I:=1 to MaxThrdCount do
OK:=OK and (FThrd[i]=Nil);
if not Ok then
begin
sleep(10);
Last:=GetTickCount64;
TimeOut:=(Last-Start)>aTimeout;
end;
Until OK or TimeOut;
{$IFDEF DEBUG_MONITOR}
if not OK then
Writeln(StdErr,Last,': Thread ',GetCurrentThreadId,' Not all threads stopped');
{$ENDIF}
end;
procedure TTestMonitorSupport.Setup;
var
I : Integer;
begin
inherited Setup;
FThrdCount:=0;
For I:=1 to MaxObjCount do
FObj[i]:=TTestObject.Create;
For I:=1 to MaxThrdCount do
FThrd[i]:=Nil;
end;
procedure TTestMonitorSupport.TearDown;
var
I : Integer;
begin
FThrdCount:=0;
For I:=1 to MaxObjCount do
FreeAndNil(FObj[i]);
For I:=1 to MaxThrdCount do
FThrd[i]:=Nil;
inherited TearDown;
end;
procedure TTestMonitorSupport.TestHookup;
var
I : integer;
begin
For I:=1 to MaxObjCount do
AssertNotNull('Obj '+IntToStr(i),FObj[I]);
For I:=1 to MaxThrdCount do
AssertNull('Thrd '+IntToStr(i),FThrd[I]);
end;
function TTestMonitorSupport.DoCreateThread(aObj: TTestObject; aOperation: TThreadOperation; aId, aTimeout: Integer): TTestThread;
begin
Inc(FThrdCount);
FThrd[FThrdCount]:=TTestThread.Create(aObj,aOperation,Aid,aTimeout,@ThreadDone);
Result:=TTestThread(FThrd[FThrdCount]);
end;
class procedure TTestMonitorSupport.AssertEquals(Msg: String; aExpected, aActual: TThreadOperation);
begin
AssertEquals(Msg,GetEnumName(TypeInfo(TOperationResult),Ord(aExpected)),
GetEnumName(TypeInfo(TOperationResult),Ord(aActual)));
end;
procedure TTestMonitorSupport.TestLock;
var
N : Int64;
begin
TMonitor.Enter(Obj1);
DoCreateThread(Obj1,toEnter,1,0);
Sleep(WaitPeriod);
N:=GetTickCount64;
TMonitor.Exit(Obj1);
WaitForAllThreads;
AssertTrue('Thread lock timestamp ',N<=FObj[1].Res[1].Tick);
AssertEquals('Thread did a lock ',toEnter,Obj1.Res[1].Op);
end;
procedure TTestMonitorSupport.TestLockMulti;
var
N : Int64;
I : integer;
begin
TMonitor.Enter(Obj1);
For I:=1 to MaxThrdCount do
DoCreateThread(Obj1,toEnter,I,0);
Sleep(WaitPeriod);
N:=GetTickCount64;
TMonitor.Exit(Obj1);
WaitForAllThreads;
AssertTrue('Thread lock timestamp ',N<=FObj[1].Res[1].Tick);
AssertEquals('Thread did a lock ',toEnter,Obj1.Res[1].Op);
end;
procedure TTestMonitorSupport.TestTryLock;
begin
TMonitor.Enter(Obj1);
DoCreateThread(Obj1,toTryEnter,1,0);
Sleep(WaitPeriod);
TMonitor.Exit(Obj1);
Writeln(GetTickCount64,': Thread ',ptruint(GetCurrentThreadID),' Released lock');
WaitForAllThreads;
AssertEquals('Thread tried a lock ',toTryEnter,Obj1.Res[1].Op);
AssertFalse('Thread lock failed ',Obj1.Res[1].Res);
end;
procedure TTestMonitorSupport.TestPulse;
var
N : Int64;
begin
// Acquire the lock
TMonitor.Enter(Obj1);
DoCreateThread(Obj1,toPulse,1,INFINITE);
Sleep(WaitPeriod);
N:=GetTickCount64;
TMonitor.Wait(Obj1,500);
TMonitor.Exit(Obj1);
WaitForAllThreads;
AssertTrue('Thread pulse timestamp ',N<=FObj[1].Res[1].Tick);
AssertEquals('Thread did a pulse',toPulse,Obj1.Res[1].Op);
AssertTrue('Thread Wait was successful',Obj1.Res[1].Res);
end;
procedure TTestMonitorSupport.TestPulseAll;
var
N : Int64;
i : integer;
begin
// Acquire the lock
For I:=1 to MaxThrdCount do
DoCreateThread(Obj1,toPulseAll,I,INFINITE);
Sleep(WaitPeriod*MaxThrdCount);
N:=GetTickCount64;
TMonitor.PulseAll(Obj1);
WaitForAllThreads(WaitTimeOut*MaxThrdCount);
For I:=0 to MaxThrdCount do
begin
AssertEquals('Thread '+IntToStr(i)+' did a Wait',toPulseAll,Obj1.Res[1].Op);
AssertTrue('Thread '+IntToStr(i)+' Wait was successful',Obj1.Res[1].Res);
AssertTrue('Thread '+IntToStr(i)+' pulse timestamp ',N<=FObj[1].Res[1].Tick);
end;
end;
procedure TTestMonitorSupport.TestWait;
var
N : Int64;
begin
// Acquire the lock
DoCreateThread(Obj1,toWait,1,INFINITE);
Sleep(WaitPeriod*4);
N:=GetTickCount64;
TMonitor.Pulse(Obj1);
WaitForAllThreads;
AssertEquals('Thread did a Wait',toWait,Obj1.Res[1].Op);
AssertTrue('Thread Wait was successful',Obj1.Res[1].Res);
AssertTrue('Thread pulse timestamp ',N<=FObj[1].Res[1].Tick);
end;
initialization
RegisterTest(TTestMonitorSupport);
end.