tinterlocked64mt.pp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. {%skiptarget=$nothread }
  2. {$ifdef FPC}
  3. {$mode objfpc}
  4. {$else}
  5. {$apptype console}
  6. {$endif}
  7. {$ifdef CPU64}
  8. uses
  9. {$ifndef FPC}
  10. Windows,
  11. {$endif FPC}
  12. {$ifdef unix}
  13. cthreads,
  14. {$endif unix}
  15. SysUtils, Classes;
  16. type
  17. TOperation = (opAdd, opDec, opExchange, opExchangeAdd, opExchangeDec, opCompareExchange);
  18. TWorker = class(TThread)
  19. private
  20. FOp: TOperation;
  21. FCount: int64;
  22. FOption: int64;
  23. protected
  24. procedure Execute; override;
  25. public
  26. constructor Create(ACount: int64; AOp: TOperation; AOption: int64 = 0);
  27. end;
  28. //{$define TEST_BROKEN_IncDec}
  29. //{$define TEST_BROKEN_Exchange}
  30. //{$define TEST_BROKEN_ExchangeAdd}
  31. //{$define TEST_BROKEN_CompareExchange}
  32. const
  33. TotalThreadCount = 50;
  34. TestCount = 1000000;
  35. WaitTime = 60;
  36. var
  37. Counter, Counter2, Counter3: int64;
  38. WorkingCount, FinishedCount: int64;
  39. AbortThread: boolean;
  40. LastCompareVal: int64;
  41. {$ifndef FPC}
  42. {$ifndef TEST_BROKEN_CompareExchange}
  43. function InterlockedCompareExchange64(var Target: int64; NewValue: int64; Comperand: int64): int64;
  44. begin
  45. Result:=int64(Windows.InterlockedCompareExchange64(pointer(Target), pointer(NewValue), pointer(Comperand)));
  46. end;
  47. {$endif TEST_BROKEN_CompareExchange}
  48. procedure ThreadSwitch;
  49. begin
  50. Sleep(0);
  51. end;
  52. {$endif FPC}
  53. {$ifdef TEST_BROKEN_CompareExchange}
  54. function InterlockedCompareExchange64(var Target: int64; NewValue: int64; Comperand: int64): int64;
  55. begin
  56. Result:=Target;
  57. if Result = Comperand then
  58. Target:=NewValue;
  59. end;
  60. {$endif TEST_BROKEN_CompareExchange}
  61. {$ifdef TEST_BROKEN_IncDec}
  62. function InterlockedIncrement64(var Target: int64): int64;
  63. begin
  64. Result:=Target;
  65. Inc(Target);
  66. end;
  67. function InterlockedDecrement64(var Target: int64): int64;
  68. begin
  69. Result:=Target;
  70. Dec(Target);
  71. end;
  72. {$endif TEST_BROKEN_IncDec}
  73. {$ifdef TEST_BROKEN_Exchange}
  74. function InterLockedExchange64(var Target: int64; Source: int64): int64;
  75. begin
  76. Result:=Target;
  77. Target:=Source;
  78. end;
  79. {$endif TEST_BROKEN_Exchange}
  80. {$ifdef TEST_BROKEN_ExchangeAdd}
  81. function InterLockedExchangeAdd64(var Target: int64; Source: int64): int64;
  82. begin
  83. Result:=Target;
  84. Inc(Target, Source);
  85. end;
  86. {$endif TEST_BROKEN_ExchangeAdd}
  87. procedure CheckResult(check, expected, code: int64; const Msg: string);
  88. begin
  89. if check <> expected then begin
  90. writeln(Msg, ' Result: ', check, '; Expected: ', expected);
  91. Halt(code);
  92. end;
  93. end;
  94. constructor TWorker.Create(ACount: int64; AOp: TOperation; AOption: int64);
  95. begin
  96. FCount:=ACount;
  97. FOp:=AOp;
  98. FOption:=AOption;
  99. inherited Create(True);
  100. FreeOnTerminate:=True;
  101. if FOp = opCompareExchange then
  102. Priority:=tpHighest;
  103. end;
  104. procedure TWorker.Execute;
  105. var
  106. i : longint;
  107. j, k, opt: int64;
  108. t: TDateTime;
  109. begin
  110. InterLockedIncrement64(WorkingCount);
  111. Sleep(10);
  112. case FOp of
  113. opAdd:
  114. begin
  115. for i:=1 to FCount do begin
  116. InterLockedIncrement64(Counter);
  117. if AbortThread then
  118. break;
  119. end;
  120. end;
  121. opDec:
  122. begin
  123. for i:=1 to FCount do begin
  124. InterLockedDecrement64(Counter);
  125. if AbortThread then
  126. break;
  127. end;
  128. end;
  129. opExchange:
  130. begin
  131. for i:=1 to FCount do begin
  132. j:=InterLockedExchange64(Counter, 10);
  133. InterLockedExchangeAdd64(Counter, j - 10);
  134. if AbortThread then
  135. break;
  136. end;
  137. end;
  138. opExchangeAdd:
  139. begin
  140. for i:=1 to FCount do begin
  141. InterLockedExchangeAdd64(Counter, 3);
  142. if AbortThread then
  143. break;
  144. end;
  145. end;
  146. opExchangeDec:
  147. begin
  148. for i:=1 to FCount do begin
  149. InterLockedExchangeAdd64(Counter, -3);
  150. if AbortThread then
  151. break;
  152. end;
  153. end;
  154. opCompareExchange:
  155. begin
  156. opt:=FOption and 1;
  157. for i:=1 to FCount do begin
  158. t:=Now;
  159. j:=0;
  160. while not AbortThread do begin
  161. k:=InterLockedCompareExchange64(Counter3, FOption, opt);
  162. if k = opt then
  163. break;
  164. if (k < 0) or (k >= LastCompareVal) then begin
  165. writeln('InterLockedCompareExchange. Invalid return value (', k, ').');
  166. Halt(10);
  167. end;
  168. Inc(j);
  169. if j and $F = 0 then
  170. ThreadSwitch;
  171. if j and $FFFF = 0 then begin
  172. if Now - t >= 30/SecsPerDay then begin
  173. writeln('InterLockedCompareExchange seems to be broken.');
  174. Halt(12);
  175. end;
  176. Sleep(1);
  177. end;
  178. end;
  179. if AbortThread then
  180. break;
  181. ThreadSwitch;
  182. k:=InterLockedExchange64(Counter3, opt xor 1);
  183. if k <> FOption then begin
  184. writeln('InterLockedCompareExchange seems to be broken (', k, ').');
  185. Halt(11);
  186. end;
  187. InterLockedIncrement64(Counter2);
  188. end;
  189. end;
  190. end;
  191. { ensure the writes to Counter and Counter2 are ordered vs the writes to FinishedCount }
  192. WriteBarrier;
  193. InterLockedIncrement64(FinishedCount);
  194. end;
  195. procedure Run;
  196. var
  197. i : longint;
  198. j, k, CmpCount, ThreadCount: int64;
  199. t: TDateTime;
  200. workers: array[0..TotalThreadCount - 1] of TWorker;
  201. begin
  202. Counter:=0;
  203. Counter2:=0;
  204. Counter3:=0;
  205. CmpCount:=TestCount div 1000;
  206. writeln('Creating threads...');
  207. j:=0;
  208. k:=2;
  209. repeat
  210. i:=j;
  211. workers[j]:=TWorker.Create(TestCount, opAdd);
  212. Inc(j);
  213. workers[j]:=TWorker.Create(TestCount, opDec);
  214. Inc(j);
  215. workers[j]:=TWorker.Create(TestCount div 3, opExchange);
  216. Inc(j);
  217. workers[j]:=TWorker.Create(TestCount, opExchangeAdd);
  218. Inc(j);
  219. workers[j]:=TWorker.Create(TestCount, opExchangeDec);
  220. Inc(j);
  221. workers[j]:=TWorker.Create(CmpCount, opCompareExchange, k);
  222. Inc(j);
  223. Inc(k);
  224. workers[j]:=TWorker.Create(CmpCount, opCompareExchange, k);
  225. Inc(j);
  226. Inc(k);
  227. until j + (j - i) > TotalThreadCount;
  228. ThreadCount:=j;
  229. LastCompareVal:=k;
  230. writeln('Created ', ThreadCount ,' threads.');
  231. writeln('Starting threads...');
  232. t:=Now;
  233. for i:=0 to ThreadCount - 1 do begin
  234. workers[i].Suspended:=False;
  235. if Now - t > 30/SecsPerDay then begin
  236. writeln('Threads start takes too long to complete.');
  237. Halt(4);
  238. end;
  239. end;
  240. t:=Now;
  241. while WorkingCount <> ThreadCount do begin
  242. if Now - t > 30/SecsPerDay then begin
  243. writeln('Not all threads have started: ', ThreadCount - WorkingCount);
  244. Halt(5);
  245. end;
  246. Sleep(10);
  247. end;
  248. writeln('Waiting for threads to complete...');
  249. t:=Now;
  250. while FinishedCount <> ThreadCount do begin
  251. if Now - t > WaitTime/SecsPerDay then begin
  252. if AbortThread then begin
  253. writeln('Unable to abort threads.');
  254. Halt(3);
  255. end
  256. else begin
  257. AbortThread:=True;
  258. writeln('Timeout has expired. Active threads left: ', ThreadCount - FinishedCount);
  259. t:=Now;
  260. end;
  261. end;
  262. Sleep(10);
  263. end;
  264. if AbortThread then begin
  265. writeln('The execution is too slow (', Counter2, ').');
  266. Halt(2);
  267. end;
  268. t:=Now - t;
  269. if t = 0 then
  270. t:=1/MSecsPerDay;
  271. { ensure the read from FinishedCount above is ordered relative to the reads from
  272. Counter and Counter2 (counterpart to WriteBarrier in the thread function) }
  273. ReadBarrier();
  274. CheckResult(Counter, 0, 20, 'Counter error:');
  275. CheckResult(Counter2, (LastCompareVal - 2)*CmpCount, 21, 'Counter2 error:');
  276. writeln('Test OK.');
  277. writeln('InterLockedCompareExchange: ', Round(Counter2/(t*SecsPerDay)), ' ops/sec.');
  278. end;
  279. begin
  280. Run;
  281. end.
  282. {$else CPU64}
  283. begin
  284. end.
  285. {$endif CPU64}