utcfpmonitor.pas 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. unit utcfpmonitor;
  2. {$mode ObjFPC}{$H+}
  3. { $DEFINE DEBUG_MONITOR}
  4. interface
  5. uses
  6. Classes, SysUtils, fpcunit, testregistry,
  7. {$ifdef windows}
  8. fpwinmonitor
  9. {$else}
  10. fpmonitor
  11. {$endif};
  12. const
  13. WaitPeriod = 10;
  14. WaitTimeout = 2000;
  15. MaxObjCount = 2;
  16. MaxThrdCount = 5;
  17. Type
  18. TThreadOperation = (toNone,toEnter,toTryEnter,toExit,toPulse,toWait,toPulseAll);
  19. TOperationResult = Record
  20. Op : TThreadOperation;
  21. Tick : Int64;
  22. Res : Boolean;
  23. end;
  24. TTestObject = Class(TObject)
  25. // Operation/Timestamp when a thread performed a task
  26. Res : Array[1..MaxThrdCount] of TOperationResult;
  27. end;
  28. { TTestThread }
  29. TTestThread = Class(TThread)
  30. Private
  31. FObj : TTestObject;
  32. FOperation : TThreadOperation;
  33. FTimeout : Integer;
  34. FID : Integer;
  35. Constructor Create(aObj : TTestObject; aOperation : TThreadOperation; aId,aTimeout : Integer; aOnFree : TNotifyEvent);
  36. Public
  37. Procedure Execute; override;
  38. end;
  39. { TTestMonitorSupport }
  40. TTestMonitorSupport = Class(TTestCase)
  41. private
  42. FThrdCount : Integer;
  43. FObj : Array[1..MaxObjCount] of TTestObject;
  44. FThrd : Array[1..MaxThrdCount] of TThread;
  45. function DoCreateThread(aObj: TTestObject; aOperation: TThreadOperation; aId, aTimeout: Integer): TTestThread;
  46. class procedure AssertEquals(Msg: String; aExpected, aActual: TThreadOperation); overload;
  47. function GetObj(AIndex: Integer): TTestObject;
  48. procedure ThreadDone(Sender : TObject);
  49. procedure WaitForAllThreads(aTimeOut: Integer=0);
  50. public
  51. Procedure Setup; override;
  52. Procedure TearDown; override;
  53. Property Obj1 : TTestObject Index 1 Read GetObj;
  54. Property Obj2 : TTestObject Index 2 Read GetObj;
  55. Published
  56. Procedure TestHookup;
  57. Procedure TestLock;
  58. Procedure TestLockMulti;
  59. Procedure TestTryLock;
  60. Procedure TestPulse;
  61. Procedure TestPulseAll;
  62. procedure TestWait;
  63. end;
  64. implementation
  65. Uses TypInfo;
  66. { TTestThread }
  67. constructor TTestThread.Create(aObj: TTestObject; aOperation: TThreadOperation; aId,aTimeout: Integer; aOnFree : TNotifyEvent);
  68. begin
  69. FObj:=aObj;
  70. FOperation:=aOperation;
  71. FTimeout:=aTimeout;
  72. FID:=aID;
  73. FreeOnTerminate:=True;
  74. OnTerminate:=aOnfree;
  75. Inherited Create(false);
  76. end;
  77. procedure TTestThread.Execute;
  78. var
  79. OpRes : TOperationResult;
  80. begin
  81. {$IFDEF DEBUG_MONITOR} Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' Begin executing operation ',FOperation);{$ENDIF}
  82. try
  83. OpRes.Res:=True;
  84. Case FOperation of
  85. toEnter : TMonitor.Enter(Fobj);
  86. toTryEnter : OpRes.Res:=TMonitor.TryEnter(Fobj);
  87. toExit : TMonitor.Exit(Fobj);
  88. toPulse : begin
  89. Sleep(WaitPeriod * 2);
  90. TMonitor.Pulse(Fobj);
  91. end;
  92. toPulseAll :
  93. begin
  94. TMonitor.Enter(Fobj);
  95. OpRes.Res:=TMonitor.Wait(FObj,FTimeout);
  96. end;
  97. toWait :
  98. begin
  99. TMonitor.Enter(Fobj);
  100. OpRes.Res:=TMonitor.Wait(FObj,FTimeout);
  101. end;
  102. end;
  103. OpRes.Tick:=GetTickCount64;
  104. OpRes.Op:=FOperation;
  105. FObj.Res[FID]:=OpRes;
  106. // We need to clean up !
  107. Case FOperation of
  108. toEnter,
  109. toWait,
  110. toPulseAll,
  111. toTryEnter:
  112. begin
  113. if OpRes.Res then
  114. begin
  115. TMonitor.Exit(Fobj);
  116. {$IFDEF DEBUG_MONITOR} Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' Unlocking previously locked object ',FOperation);{$ENDIF}
  117. end;
  118. end;
  119. else
  120. //
  121. end;
  122. except
  123. On E : Exception do
  124. Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' exception ',E.ClassName,' during operation ',FOperation,' : ',E.Message);
  125. end;
  126. {$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' End executing operation ',FOperation);{$ENDIF}
  127. end;
  128. { TTestMonitorSupport }
  129. function TTestMonitorSupport.GetObj(AIndex: Integer): TTestObject;
  130. begin
  131. Result:=FObj[aIndex];
  132. end;
  133. procedure TTestMonitorSupport.ThreadDone(Sender: TObject);
  134. var
  135. aCount,I : Integer;
  136. begin
  137. aCount:=0;
  138. {$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' Begin done executing');{$ENDIF}
  139. For I:=1 to MaxThrdCount do
  140. begin
  141. if FThrd[i]=Sender then
  142. begin
  143. {$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' Done executing: found thread at ',I){$ENDIF};
  144. FThrd[i]:=Nil;
  145. end
  146. else if assigned(FThrd[I]) then
  147. inc(aCount);
  148. end;
  149. {$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadID,' End done executing. Threads still active: ',aCount);{$ENDIF}
  150. end;
  151. procedure TTestMonitorSupport.WaitForAllThreads(aTimeOut : Integer = 0);
  152. var
  153. I : Integer;
  154. Last,Start : Int64;
  155. TimeOut,OK : Boolean;
  156. begin
  157. If aTimeOut=0 then
  158. aTimeout:=WaitTimeout;
  159. Start:=GetTickCount64;
  160. {$IFDEF DEBUG_MONITOR} Writeln(StdErr,Start,': Thread ',GetCurrentThreadID,' Waiting for ', FThrdCount,' threads to stop');{$ENDIF}
  161. Timeout:=False;
  162. Repeat
  163. OK:=True;
  164. CheckSynchronize(5);
  165. For I:=1 to MaxThrdCount do
  166. OK:=OK and (FThrd[i]=Nil);
  167. if not Ok then
  168. begin
  169. sleep(10);
  170. Last:=GetTickCount64;
  171. TimeOut:=(Last-Start)>aTimeout;
  172. end;
  173. Until OK or TimeOut;
  174. {$IFDEF DEBUG_MONITOR}
  175. if not OK then
  176. Writeln(StdErr,Last,': Thread ',GetCurrentThreadId,' Not all threads stopped');
  177. {$ENDIF}
  178. end;
  179. procedure TTestMonitorSupport.Setup;
  180. var
  181. I : Integer;
  182. begin
  183. inherited Setup;
  184. FThrdCount:=0;
  185. For I:=1 to MaxObjCount do
  186. FObj[i]:=TTestObject.Create;
  187. For I:=1 to MaxThrdCount do
  188. FThrd[i]:=Nil;
  189. end;
  190. procedure TTestMonitorSupport.TearDown;
  191. var
  192. I : Integer;
  193. begin
  194. FThrdCount:=0;
  195. For I:=1 to MaxObjCount do
  196. FreeAndNil(FObj[i]);
  197. For I:=1 to MaxThrdCount do
  198. FThrd[i]:=Nil;
  199. inherited TearDown;
  200. end;
  201. procedure TTestMonitorSupport.TestHookup;
  202. var
  203. I : integer;
  204. begin
  205. For I:=1 to MaxObjCount do
  206. AssertNotNull('Obj '+IntToStr(i),FObj[I]);
  207. For I:=1 to MaxThrdCount do
  208. AssertNull('Thrd '+IntToStr(i),FThrd[I]);
  209. end;
  210. function TTestMonitorSupport.DoCreateThread(aObj: TTestObject; aOperation: TThreadOperation; aId, aTimeout: Integer): TTestThread;
  211. begin
  212. Inc(FThrdCount);
  213. FThrd[FThrdCount]:=TTestThread.Create(aObj,aOperation,Aid,aTimeout,@ThreadDone);
  214. Result:=TTestThread(FThrd[FThrdCount]);
  215. end;
  216. class procedure TTestMonitorSupport.AssertEquals(Msg: String; aExpected, aActual: TThreadOperation);
  217. begin
  218. AssertEquals(Msg,GetEnumName(TypeInfo(TOperationResult),Ord(aExpected)),
  219. GetEnumName(TypeInfo(TOperationResult),Ord(aActual)));
  220. end;
  221. procedure TTestMonitorSupport.TestLock;
  222. var
  223. N : Int64;
  224. begin
  225. TMonitor.Enter(Obj1);
  226. DoCreateThread(Obj1,toEnter,1,0);
  227. Sleep(WaitPeriod);
  228. N:=GetTickCount64;
  229. TMonitor.Exit(Obj1);
  230. WaitForAllThreads;
  231. AssertTrue('Thread lock timestamp ',N<=FObj[1].Res[1].Tick);
  232. AssertEquals('Thread did a lock ',toEnter,Obj1.Res[1].Op);
  233. end;
  234. procedure TTestMonitorSupport.TestLockMulti;
  235. var
  236. N : Int64;
  237. I : integer;
  238. begin
  239. TMonitor.Enter(Obj1);
  240. For I:=1 to MaxThrdCount do
  241. DoCreateThread(Obj1,toEnter,I,0);
  242. Sleep(WaitPeriod);
  243. N:=GetTickCount64;
  244. TMonitor.Exit(Obj1);
  245. WaitForAllThreads;
  246. AssertTrue('Thread lock timestamp ',N<=FObj[1].Res[1].Tick);
  247. AssertEquals('Thread did a lock ',toEnter,Obj1.Res[1].Op);
  248. end;
  249. procedure TTestMonitorSupport.TestTryLock;
  250. begin
  251. TMonitor.Enter(Obj1);
  252. DoCreateThread(Obj1,toTryEnter,1,0);
  253. Sleep(WaitPeriod);
  254. TMonitor.Exit(Obj1);
  255. Writeln(GetTickCount64,': Thread ',GetCurrentThreadID,' Released lock');
  256. WaitForAllThreads;
  257. AssertEquals('Thread tried a lock ',toTryEnter,Obj1.Res[1].Op);
  258. AssertFalse('Thread lock failed ',Obj1.Res[1].Res);
  259. end;
  260. procedure TTestMonitorSupport.TestPulse;
  261. var
  262. N : Int64;
  263. begin
  264. // Acquire the lock
  265. TMonitor.Enter(Obj1);
  266. DoCreateThread(Obj1,toPulse,1,INFINITE);
  267. Sleep(WaitPeriod);
  268. N:=GetTickCount64;
  269. TMonitor.Wait(Obj1,500);
  270. TMonitor.Exit(Obj1);
  271. WaitForAllThreads;
  272. AssertTrue('Thread pulse timestamp ',N<=FObj[1].Res[1].Tick);
  273. AssertEquals('Thread did a pulse',toPulse,Obj1.Res[1].Op);
  274. AssertTrue('Thread Wait was successful',Obj1.Res[1].Res);
  275. end;
  276. procedure TTestMonitorSupport.TestPulseAll;
  277. var
  278. N : Int64;
  279. i : integer;
  280. begin
  281. // Acquire the lock
  282. For I:=1 to MaxThrdCount do
  283. DoCreateThread(Obj1,toPulseAll,I,INFINITE);
  284. Sleep(WaitPeriod*MaxThrdCount);
  285. N:=GetTickCount64;
  286. TMonitor.PulseAll(Obj1);
  287. WaitForAllThreads(WaitTimeOut*MaxThrdCount);
  288. For I:=0 to MaxThrdCount do
  289. begin
  290. AssertEquals('Thread '+IntToStr(i)+' did a Wait',toPulseAll,Obj1.Res[1].Op);
  291. AssertTrue('Thread '+IntToStr(i)+' Wait was successful',Obj1.Res[1].Res);
  292. AssertTrue('Thread '+IntToStr(i)+' pulse timestamp ',N<=FObj[1].Res[1].Tick);
  293. end;
  294. end;
  295. procedure TTestMonitorSupport.TestWait;
  296. var
  297. N : Int64;
  298. begin
  299. // Acquire the lock
  300. DoCreateThread(Obj1,toWait,1,INFINITE);
  301. Sleep(WaitPeriod*4);
  302. N:=GetTickCount64;
  303. TMonitor.Pulse(Obj1);
  304. WaitForAllThreads;
  305. AssertEquals('Thread did a Wait',toWait,Obj1.Res[1].Op);
  306. AssertTrue('Thread Wait was successful',Obj1.Res[1].Res);
  307. AssertTrue('Thread pulse timestamp ',N<=FObj[1].Res[1].Tick);
  308. end;
  309. initialization
  310. RegisterTest(TTestMonitorSupport);
  311. end.