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.

375 lines
15 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. public type
  92. ILock = specialize IutlLock<T>;
  93. protected
  94. fLock: IutlLockable;
  95. fObject: T;
  96. public
  97. function LockedObject: T; inline;
  98. constructor Create(constref aLock: IutlLockable; constref aObject: T);
  99. destructor Destroy; override;
  100. class function CreateLock(constref aLock: IutlLockable; constref aObject: T): ILock;
  101. end;
  102. implementation
  103. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  104. //TutlCheckSynchronizeEvent/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  105. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  106. function TutlCheckSynchronizeEvent.WaitMainThread(const aTimeout: Cardinal): TWaitResult;
  107. var
  108. timeout: qword;
  109. begin
  110. timeout:= GetTickCount64 + aTimeout;
  111. repeat
  112. result := fEvent.WaitFor(TutlCheckSynchronizeEvent.MAIN_WAIT_GRANULARITY);
  113. CheckSynchronize();
  114. until (result <> wrTimeout) or ((GetTickCount64 > timeout) and (aTimeout <> INFINITE));
  115. end;
  116. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  117. procedure TutlCheckSynchronizeEvent.SetEvent;
  118. begin
  119. fEvent.SetEvent;
  120. end;
  121. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  122. procedure TutlCheckSynchronizeEvent.ResetEvent;
  123. begin
  124. fEvent.ResetEvent;
  125. end;
  126. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  127. function TutlCheckSynchronizeEvent.WaitFor(const aTimeout: Cardinal): TWaitResult;
  128. begin
  129. if (GetCurrentThreadId = MainThreadID) then
  130. result := WaitMainThread(aTimeout)
  131. else
  132. result := fEvent.WaitFor(aTimeout);
  133. end;
  134. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  135. constructor TutlCheckSynchronizeEvent.Create(
  136. const aEventAttributes: PSecurityAttributes;
  137. const aManualReset: Boolean;
  138. const aInitialState: Boolean;
  139. const aName: string);
  140. begin
  141. inherited Create;
  142. fEvent := TEvent.Create(aEventAttributes, aManualReset, aInitialState, aName);
  143. end;
  144. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  145. destructor TutlCheckSynchronizeEvent.Destroy;
  146. begin
  147. FreeAndNil(fEvent);
  148. inherited Destroy;
  149. end;
  150. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  151. //TutlEventList/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  152. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  153. function TutlEventList.AddEvent(
  154. const aEventAttributes: PSecurityAttributes;
  155. const aManualReset: Boolean;
  156. const aInitialState: Boolean;
  157. const aName: String): TutlCheckSynchronizeEvent;
  158. begin
  159. result := TutlCheckSynchronizeEvent.Create(aEventAttributes, aManualReset, aInitialState, aName);
  160. Add(result);
  161. end;
  162. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  163. function TutlEventList.AddDefaultEvent: TutlCheckSynchronizeEvent;
  164. begin
  165. result := AddEvent(nil, true, false, '');
  166. result.ResetEvent;
  167. end;
  168. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  169. function TutlEventList.WaitAll(const aTimeout: Cardinal): TWaitResult;
  170. var
  171. i: integer;
  172. timeout, tick: qword;
  173. begin
  174. result := wrError;
  175. timeout := GetTickCount64 + aTimeout;
  176. for i := 0 to Count-1 do begin
  177. if (aTimeout <> INFINITE) then begin
  178. tick := GetTickCount64;
  179. if (tick >= timeout) then begin
  180. result := wrTimeout;
  181. exit;
  182. end else
  183. result := Items[i].WaitFor(timeout - tick);
  184. end else
  185. result := Items[i].WaitFor(INFINITE);
  186. if result <> wrSignaled then
  187. exit;
  188. end;
  189. end;
  190. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  191. constructor TutlEventList.Create;
  192. begin
  193. inherited Create(true);
  194. end;
  195. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  196. //TAutoResetEvent///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  197. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  198. constructor TAutoResetEvent.Create(const aInitial: boolean);
  199. begin
  200. inherited Create(Nil, false, aInitial, '');
  201. end;
  202. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  203. //TutlSpinLock//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  204. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  205. procedure TutlSpinLock.Enter;
  206. var
  207. ti: DWord;
  208. begin
  209. ti := ThreadID;
  210. if (ti = InterlockedCompareExchange(fLock, ti, ti)) then begin
  211. {
  212. The lock is already held by this thread. This means it cannot be modified by a concurrent
  213. operation (assuming Enter/Leave bracket correctly), and we can act non-atomar on other variables.
  214. }
  215. inc(fLockReused);
  216. end else begin
  217. while InterlockedCompareExchange(fLock, ti, 0) <> 0 do ;
  218. end;
  219. end;
  220. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  221. procedure TutlSpinLock.Leave;
  222. var
  223. ti: DWord;
  224. begin
  225. ti := ThreadID;
  226. // Unlock only if we hold the lock
  227. if (ti = InterlockedCompareExchange(fLock, ti, ti)) then begin
  228. // our lock, but we haven't yet done anything (note the above is essentially a threadsafe CMP if successful)
  229. if fLockReused = 0 then
  230. InterLockedExchange(fLock, 0) // normal lock
  231. else
  232. dec(fLockReused); // nested locks
  233. end;
  234. end;
  235. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  236. procedure TutlSpinLock.Lock;
  237. begin
  238. Enter;
  239. end;
  240. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  241. procedure TutlSpinLock.Unlock;
  242. begin
  243. Leave;
  244. end;
  245. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  246. constructor TutlSpinLock.Create;
  247. begin
  248. inherited Create;
  249. fLock := 0;
  250. fLockReused := 0;
  251. end;
  252. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  253. destructor TutlSpinLock.Destroy;
  254. begin
  255. Enter;
  256. inherited Destroy;
  257. end;
  258. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  259. //TutlCriticalSection///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  260. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  261. function TutlCriticalSection.QueryInterface(constref iid: tguid; out obj): longint; stdcall;
  262. begin
  263. if GetInterface(iid,obj)
  264. then result := S_OK
  265. else result := longint(E_NOINTERFACE);
  266. end;
  267. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  268. function TutlCriticalSection._AddRef: longint; stdcall;
  269. begin
  270. result := InterLockedIncrement(fRefCount);
  271. end;
  272. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  273. function TutlCriticalSection._Release: longint; stdcall;
  274. begin
  275. result := InterLockedDecrement(fRefCount);
  276. if (result <= 0) and fAutoFree then
  277. Destroy;
  278. end;
  279. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  280. procedure TutlCriticalSection.Lock;
  281. begin
  282. Enter;
  283. end;
  284. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  285. procedure TutlCriticalSection.Unlock;
  286. begin
  287. Leave;
  288. end;
  289. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  290. //TutlLock//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  291. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  292. function TutlLock.LockedObject: T;
  293. begin
  294. result := fObject;
  295. end;
  296. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  297. constructor TutlLock.Create(constref aLock: IutlLockable; constref aObject: T);
  298. begin
  299. inherited Create;
  300. if not Assigned(aLock) then
  301. raise EArgumentNilException.Create('aLock');
  302. fObject := aObject;
  303. fLock := aLock;
  304. fLock.Lock;
  305. end;
  306. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  307. destructor TutlLock.Destroy;
  308. begin
  309. fLock.Unlock;
  310. inherited Destroy;
  311. end;
  312. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  313. class function TutlLock.CreateLock(constref aLock: IutlLockable; constref aObject: T): ILock;
  314. begin
  315. result := TutlLock.Create(aLock, aObject);
  316. end;
  317. end.