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.

311 lines
12 KiB

  1. unit uutlEvent;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, syncobjs,
  6. uutlTypes, uutlCommon, uutlGenerics, uutlInterfaces;
  7. type
  8. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  9. IutlEventArgs = interface(IUnknown)
  10. ['{FC7AA96D-9C2C-42AD-A680-DE55341F2B35}']
  11. end;
  12. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  13. IutlEventListener = interface(IUnknown)
  14. ['{BC45E26B-96F7-4151-87F1-C330C8C668E5}']
  15. procedure DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs);
  16. end;
  17. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  18. TutlEventHandler = procedure (constref aSender: TObject; constref aEvent: IutlEventArgs) of object;
  19. TutlEventArgs = class(TInterfacedObject, IutlEventArgs);
  20. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  21. generic IutlObservable<T> = interface(specialize {$IFDEF UTL_ADVANCED_ENUMERATORS}IutlEnumerable{$ELSE}IEnumerable{$ENDIF}<T>)
  22. ['{C54BD844-8273-4ACF-90C5-05DACF4359AF}']
  23. procedure RegisterEventHandler (const aHandler: TutlEventHandler);
  24. procedure UnregisterEventHandler(const aHandler: TutlEventHandler);
  25. end;
  26. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  27. generic TutlEventList<T> = class(specialize TutlCustomHashSet<T>)
  28. private type
  29. TComparer = class(TInterfacedObject, IComparer)
  30. public
  31. function EqualityCompare(constref i1, i2: T): Boolean;
  32. function Compare (constref i1, i2: T): Integer;
  33. end;
  34. public
  35. constructor Create;
  36. end;
  37. TutlNotifyEventList = specialize TutlEventList<TNotifyEvent>;
  38. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  39. TutlEventListenerSet = class(
  40. specialize TutlCustomHashSet<IutlEventListener>
  41. , IutlEventListener)
  42. private type
  43. TComparer = class(TInterfacedObject, IComparer)
  44. public
  45. function EqualityCompare(constref i1, i2: IutlEventListener): Boolean;
  46. function Compare (constref i1, i2: IutlEventListener): Integer;
  47. end;
  48. public { IutlEventListener }
  49. procedure DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs);
  50. public
  51. constructor Create; reintroduce;
  52. end;
  53. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  54. TutlEventListenerCallback = class(
  55. TInterfacedObject
  56. , IutlEventListener)
  57. private
  58. fHandler: TutlEventHandler;
  59. public { IEventListener }
  60. procedure DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs);
  61. public
  62. constructor Create(const aHandler: TutlEventHandler);
  63. end;
  64. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  65. TutlEventListenerAsync = class(
  66. TutlInterfaceNoRefCount
  67. , IutlEventListener)
  68. private type
  69. TEventPair = specialize TutlPair<TObject, IutlEventArgs>;
  70. TEventList = class(specialize TutlSimpleList<TEventPair>)
  71. protected
  72. procedure Release(var aItem: TEventPair; const aFreeItem: Boolean); override;
  73. end;
  74. private
  75. fEventLock: TCriticalSection;
  76. fListenerLock: TCriticalSection;
  77. fEvents: TEventList;
  78. fListener: TutlEventListenerSet;
  79. function PopEventPair(out aPair: TEventPair): Boolean;
  80. public { IEventListener }
  81. procedure DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs);
  82. public
  83. function RegisterListener (const aListener: IutlEventListener): Boolean;
  84. function UnregisterListener(const aListener: IutlEventListener): Boolean;
  85. procedure DispatchEvents;
  86. constructor Create;
  87. destructor Destroy; override;
  88. end;
  89. implementation
  90. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  91. //TutlEventList/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  92. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  93. function TutlEventList.TComparer.EqualityCompare(constref i1, i2: T): Boolean;
  94. begin
  95. result := (TMethod(i1).Data = TMethod(i2).Data)
  96. and (TMethod(i1).Code = TMethod(i2).Code);
  97. end;
  98. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  99. function TutlEventList.TComparer.Compare(constref i1, i2: T): Integer;
  100. var
  101. m1, m2: TMethod;
  102. begin
  103. m1 := TMethod(i1);
  104. m2 := TMethod(i2);
  105. if (m1.Data < m2.Data) then
  106. result := -1
  107. else if (m1.Data > m2.Data) then
  108. result := 1
  109. else if (m1.Code < m2.Code) then
  110. result := -1
  111. else if (m1.Code > m2.Code) then
  112. result := 1
  113. else
  114. result := 0;
  115. end;
  116. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  117. constructor TutlEventList.Create;
  118. begin
  119. inherited Create(TComparer.Create, true);
  120. end;
  121. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  122. //TutlEventListenerSet///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  123. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  124. function TutlEventListenerSet.TComparer.EqualityCompare(constref i1, i2: IutlEventListener): Boolean;
  125. begin
  126. result := (i1 = i2);
  127. end;
  128. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  129. function TutlEventListenerSet.TComparer.Compare(constref i1, i2: IutlEventListener): Integer;
  130. begin
  131. if (Pointer(i1) < Pointer(i2)) then
  132. result := -1
  133. else if (Pointer(i1) > Pointer(i2)) then
  134. result := 1
  135. else
  136. result := 0;
  137. end;
  138. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  139. //TutlEventListenerSet///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  140. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  141. procedure TutlEventListenerSet.DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs);
  142. var
  143. l: IutlEventListener;
  144. begin
  145. for l in self do
  146. l.DispatchEvent(aSender, aEvent);
  147. end;
  148. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  149. constructor TutlEventListenerSet.Create;
  150. begin
  151. inherited Create(TComparer.Create, true);
  152. end;
  153. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  154. //TutlEventListenerCallback//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  155. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  156. procedure TutlEventListenerCallback.DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs);
  157. begin
  158. fHandler(aSender, aEvent);
  159. end;
  160. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  161. constructor TutlEventListenerCallback.Create(const aHandler: TutlEventHandler);
  162. begin
  163. inherited Create;
  164. if not Assigned(aHandler) then
  165. raise EArgumentNilException.Create('aHandler is not assigned');
  166. fHandler := aHandler;
  167. end;
  168. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  169. //TutlEventListenerAsync.TEventList//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  170. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  171. procedure TutlEventListenerAsync.TEventList.Release(var aItem: TEventPair; const aFreeItem: Boolean);
  172. begin
  173. aItem.first := nil;
  174. aItem.second := nil;
  175. inherited Release(aItem, aFreeItem);
  176. end;
  177. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  178. //TutlEventListenerAsync/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  179. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  180. function TutlEventListenerAsync.PopEventPair(out aPair: TEventPair): Boolean;
  181. begin
  182. fEventLock.Enter;
  183. try
  184. result := not fEvents.IsEmpty;
  185. if result
  186. then aPair := fEvents.PopFirst(false)
  187. else FillByte(aPair, SizeOf(aPair), 0);
  188. finally
  189. fEventLock.Leave;
  190. end;
  191. end;
  192. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  193. procedure TutlEventListenerAsync.DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs);
  194. var
  195. p: TEventPair;
  196. begin
  197. p.first := aSender;
  198. p.second := aEvent;
  199. fEventLock.Enter;
  200. try
  201. fEvents.Add(p);
  202. finally
  203. fEventLock.Leave;
  204. end;
  205. end;
  206. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  207. function TutlEventListenerAsync.RegisterListener(const aListener: IutlEventListener): Boolean;
  208. begin
  209. fListenerLock.Enter;
  210. try
  211. result := fListener.Add(aListener);
  212. finally
  213. fListenerLock.Leave;
  214. end;
  215. end;
  216. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  217. function TutlEventListenerAsync.UnregisterListener(const aListener: IutlEventListener): Boolean;
  218. begin
  219. fListenerLock.Enter;
  220. try
  221. result := fListener.Remove(aListener);
  222. finally
  223. fListenerLock.Leave;
  224. end;
  225. end;
  226. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  227. procedure TutlEventListenerAsync.DispatchEvents;
  228. var
  229. p: TEventPair;
  230. begin
  231. while PopEventPair(p) do begin
  232. fListenerLock.Enter;
  233. try
  234. fListener.DispatchEvent(p.first, p.second);
  235. finally
  236. fListenerLock.Leave;
  237. end;
  238. end;
  239. end;
  240. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  241. constructor TutlEventListenerAsync.Create;
  242. begin
  243. inherited Create;
  244. fEventLock := TCriticalSection.Create;
  245. fListenerLock := TCriticalSection.Create;
  246. fEvents := TEventList.Create(true);
  247. fListener := TutlEventListenerSet.Create;
  248. end;
  249. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  250. destructor TutlEventListenerAsync.Destroy;
  251. begin
  252. fEventLock.Enter;
  253. fListenerLock.Enter;
  254. try
  255. FreeAndNil(fEvents);
  256. FreeAndNil(fListener);
  257. finally
  258. fListenerLock.Leave;
  259. fEventLock.Leave;
  260. end;
  261. FreeAndNil(fEventLock);
  262. FreeAndNil(fListenerLock);
  263. inherited Destroy;
  264. end;
  265. end.