You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
14 KiB

  1. unit uutlSyncObjs;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, syncobjs,
  6. uutlGenerics, uutlCommon;
  7. type
  8. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  9. TutlCheckSynchronizeEvent = class(TObject)
  10. private
  11. fEvent: TEvent;
  12. function WaitMainThread(const aTimeout: Cardinal): TWaitResult;
  13. public const
  14. MAIN_WAIT_GRANULARITY = 10;
  15. public
  16. procedure SetEvent;
  17. procedure ResetEvent;
  18. function WaitFor(const aTimeout: Cardinal): TWaitResult;
  19. constructor Create(
  20. const aEventAttributes: PSecurityAttributes;
  21. const aManualReset: Boolean;
  22. const aInitialState: Boolean;
  23. const aName: string);
  24. destructor Destroy; override;
  25. end;
  26. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  27. TutlEventList = class(specialize TutlSimpleList<TutlCheckSynchronizeEvent>)
  28. public
  29. function AddEvent(
  30. const aEventAttributes: PSecurityAttributes;
  31. const aManualReset: Boolean;
  32. const aInitialState: Boolean;
  33. const aName: String): TutlCheckSynchronizeEvent;
  34. function AddDefaultEvent: TutlCheckSynchronizeEvent;
  35. function WaitAll(const aTimeout: Cardinal): TWaitResult;
  36. constructor Create;
  37. end;
  38. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  39. TAutoResetEvent = class(TEvent)
  40. public
  41. constructor Create(const aInitial: boolean = false);
  42. end;
  43. // aliased to stay in LCL naming scheme for TSimpleEvent
  44. TutlAutoResetEvent = TAutoResetEvent;
  45. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  46. IutlLockable = interface(IUnknown)
  47. ['{CF01F747-D6A9-405B-8A8D-AC148FA9DABB}']
  48. procedure Lock;
  49. procedure Unlock;
  50. end;
  51. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  52. TutlSpinLock = class(
  53. TutlInterfacedObject
  54. , IutlLockable)
  55. private
  56. fLock: DWord;
  57. fLockReused: integer;
  58. public
  59. procedure Enter;
  60. procedure Leave;
  61. procedure Lock; inline;
  62. procedure Unlock; inline;
  63. constructor Create;
  64. destructor Destroy; override;
  65. end;
  66. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  67. TutlCriticalSection = class(
  68. TCriticalSection
  69. , IutlLockable)
  70. strict private
  71. fRefCount: Integer;
  72. fAutoFree: Boolean;
  73. public
  74. function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  75. function _AddRef: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  76. function _Release: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  77. public
  78. property RefCount: Integer read fRefCount;
  79. property AutoFree: Boolean read fAutoFree write fAutoFree;
  80. procedure Lock; inline;
  81. procedure Unlock; inline;
  82. end;
  83. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  84. generic IutlLock<T> = interface(IUnknown)
  85. function LockedObject: T;
  86. end;
  87. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  88. generic TutlLock<T> = class(
  89. TInterfacedObject,
  90. specialize IutlLock<T>)
  91. private
  92. fLock: IutlLockable;
  93. fObject: T;
  94. public
  95. function LockedObject: T; inline;
  96. constructor Create(constref aLock: IutlLockable; constref aObject: T);
  97. destructor Destroy; override;
  98. end;
  99. implementation
  100. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  101. //TutlCheckSynchronizeEvent/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  102. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  103. function TutlCheckSynchronizeEvent.WaitMainThread(const aTimeout: Cardinal): TWaitResult;
  104. var
  105. timeout: qword;
  106. begin
  107. timeout:= GetTickCount64 + aTimeout;
  108. repeat
  109. result := fEvent.WaitFor(TutlCheckSynchronizeEvent.MAIN_WAIT_GRANULARITY);
  110. CheckSynchronize();
  111. until (result <> wrTimeout) or ((GetTickCount64 > timeout) and (aTimeout <> INFINITE));
  112. end;
  113. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  114. procedure TutlCheckSynchronizeEvent.SetEvent;
  115. begin
  116. fEvent.SetEvent;
  117. end;
  118. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  119. procedure TutlCheckSynchronizeEvent.ResetEvent;
  120. begin
  121. fEvent.ResetEvent;
  122. end;
  123. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  124. function TutlCheckSynchronizeEvent.WaitFor(const aTimeout: Cardinal): TWaitResult;
  125. begin
  126. if (GetCurrentThreadId = MainThreadID) then
  127. result := WaitMainThread(aTimeout)
  128. else
  129. result := fEvent.WaitFor(aTimeout);
  130. end;
  131. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  132. constructor TutlCheckSynchronizeEvent.Create(
  133. const aEventAttributes: PSecurityAttributes;
  134. const aManualReset: Boolean;
  135. const aInitialState: Boolean;
  136. const aName: string);
  137. begin
  138. inherited Create;
  139. fEvent := TEvent.Create(aEventAttributes, aManualReset, aInitialState, aName);
  140. end;
  141. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  142. destructor TutlCheckSynchronizeEvent.Destroy;
  143. begin
  144. FreeAndNil(fEvent);
  145. inherited Destroy;
  146. end;
  147. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  148. //TutlEventList/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  149. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  150. function TutlEventList.AddEvent(
  151. const aEventAttributes: PSecurityAttributes;
  152. const aManualReset: Boolean;
  153. const aInitialState: Boolean;
  154. const aName: String): TutlCheckSynchronizeEvent;
  155. begin
  156. result := TutlCheckSynchronizeEvent.Create(aEventAttributes, aManualReset, aInitialState, aName);
  157. Add(result);
  158. end;
  159. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  160. function TutlEventList.AddDefaultEvent: TutlCheckSynchronizeEvent;
  161. begin
  162. result := AddEvent(nil, true, false, '');
  163. result.ResetEvent;
  164. end;
  165. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  166. function TutlEventList.WaitAll(const aTimeout: Cardinal): TWaitResult;
  167. var
  168. i: integer;
  169. timeout, tick: qword;
  170. begin
  171. result := wrError;
  172. timeout := GetTickCount64 + aTimeout;
  173. for i := 0 to Count-1 do begin
  174. if (aTimeout <> INFINITE) then begin
  175. tick := GetTickCount64;
  176. if (tick >= timeout) then begin
  177. result := wrTimeout;
  178. exit;
  179. end else
  180. result := Items[i].WaitFor(timeout - tick);
  181. end else
  182. result := Items[i].WaitFor(INFINITE);
  183. if result <> wrSignaled then
  184. exit;
  185. end;
  186. end;
  187. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  188. constructor TutlEventList.Create;
  189. begin
  190. inherited Create(true);
  191. end;
  192. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  193. //TAutoResetEvent///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  194. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  195. constructor TAutoResetEvent.Create(const aInitial: boolean);
  196. begin
  197. inherited Create(Nil, false, aInitial, '');
  198. end;
  199. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  200. //TutlSpinLock//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  201. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  202. procedure TutlSpinLock.Enter;
  203. var
  204. ti: DWord;
  205. begin
  206. ti := ThreadID;
  207. if (ti = InterlockedCompareExchange(fLock, ti, ti)) then begin
  208. {
  209. The lock is already held by this thread. This means it cannot be modified by a concurrent
  210. operation (assuming Enter/Leave bracket correctly), and we can act non-atomar on other variables.
  211. }
  212. inc(fLockReused);
  213. end else begin
  214. while InterlockedCompareExchange(fLock, ti, 0) <> 0 do ;
  215. end;
  216. end;
  217. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  218. procedure TutlSpinLock.Leave;
  219. var
  220. ti: DWord;
  221. begin
  222. ti := ThreadID;
  223. // Unlock only if we hold the lock
  224. if (ti = InterlockedCompareExchange(fLock, ti, ti)) then begin
  225. // our lock, but we haven't yet done anything (note the above is essentially a threadsafe CMP if successful)
  226. if fLockReused = 0 then
  227. InterLockedExchange(fLock, 0) // normal lock
  228. else
  229. dec(fLockReused); // nested locks
  230. end;
  231. end;
  232. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  233. procedure TutlSpinLock.Lock;
  234. begin
  235. Enter;
  236. end;
  237. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  238. procedure TutlSpinLock.Unlock;
  239. begin
  240. Leave;
  241. end;
  242. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  243. constructor TutlSpinLock.Create;
  244. begin
  245. inherited Create;
  246. fLock := 0;
  247. fLockReused := 0;
  248. end;
  249. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  250. destructor TutlSpinLock.Destroy;
  251. begin
  252. Enter;
  253. inherited Destroy;
  254. end;
  255. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  256. //TutlCriticalSection///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  257. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  258. function TutlCriticalSection.QueryInterface(constref iid: tguid; out obj): longint; stdcall;
  259. begin
  260. if GetInterface(iid,obj)
  261. then result := S_OK
  262. else result := longint(E_NOINTERFACE);
  263. end;
  264. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  265. function TutlCriticalSection._AddRef: longint; stdcall;
  266. begin
  267. result := InterLockedIncrement(fRefCount);
  268. end;
  269. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  270. function TutlCriticalSection._Release: longint; stdcall;
  271. begin
  272. result := InterLockedDecrement(fRefCount);
  273. if (result <= 0) and fAutoFree then
  274. Destroy;
  275. end;
  276. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  277. procedure TutlCriticalSection.Lock;
  278. begin
  279. Enter;
  280. end;
  281. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  282. procedure TutlCriticalSection.Unlock;
  283. begin
  284. Leave;
  285. end;
  286. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  287. //TutlLock//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  288. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  289. function TutlLock.LockedObject: T;
  290. begin
  291. result := fObject;
  292. end;
  293. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  294. constructor TutlLock.Create(constref aLock: IutlLockable; constref aObject: T);
  295. begin
  296. inherited Create;
  297. if not Assigned(aLock) then
  298. raise EArgumentNilException.Create('aLock');
  299. fObject := aObject;
  300. fLock := aLock;
  301. fLock.Lock;
  302. end;
  303. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  304. destructor TutlLock.Destroy;
  305. begin
  306. fLock.Unlock;
  307. inherited Destroy;
  308. end;
  309. end.