utcfpmonitor.pas 9.0 KB

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