123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- 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 ',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 ',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.
|