No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 

392 líneas
17 KiB

  1. unit uutlMessageThread;
  2. { Package: Utils
  3. Prefix: utl - UTiLs
  4. Beschreibung: diese Unit definiert einen Thread, der mit Hilfe von Messages Daten synchronisiert
  5. mit anderen Threads austauschen kann }
  6. {$mode objfpc}{$H+}
  7. interface
  8. uses
  9. Classes, SysUtils, syncobjs, uutlMessages, uutlGenerics;
  10. type
  11. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  12. TutlMessageThread = class(TThread, IUnknown)
  13. public type
  14. TMessageQueue = class(specialize TutlQueue<TutlMessage>)
  15. private
  16. fEvent: TSimpleEvent;
  17. public
  18. procedure Push(const aItem: T); override;
  19. function Pop(out aItem: T): Boolean; override;
  20. function WaitForMessages(const aWaitTime: Cardinal = INFINITE): Boolean;
  21. constructor Create(const aOwnsObjects: Boolean = true);
  22. destructor Destroy; override;
  23. end;
  24. protected
  25. fMessages: TMessageQueue;
  26. fRefCount : longint;
  27. { implement methods of IUnknown }
  28. function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  29. function _AddRef : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual;
  30. function _Release : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual;
  31. protected
  32. function CreateMessageQueue: TMessageQueue; virtual;
  33. function WaitForMessages(const aWaitTime: Cardinal): Boolean;
  34. function ProcessMessages: Boolean; virtual;
  35. procedure ProcessMessage(const {%H-}aMessage: TutlMessage); virtual;
  36. public
  37. //Messages Objects passed to PostMessage will be freed automatically
  38. procedure PostMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt); overload;
  39. procedure PostMessage(const aID: Cardinal; const aArgs: TObject); overload;
  40. procedure PostMessage(const aMsg: TutlMessage); overload;
  41. //Messages Objects passed to SendMessage must be freed by user when WaitResult is wrSignaled (otherwise the thread will handle it)
  42. function SendMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt;
  43. const aWaitTime: Cardinal = INFINITE): TWaitResult; overload;
  44. function SendMessage(const aID: Cardinal; const aArgs: TObject;
  45. const aWaitTime: Cardinal = INFINITE): TWaitResult; overload;
  46. function SendMessage(const aMsg: TutlSynchronousMessage;
  47. const aWaitTime: Cardinal = INFINITE): TWaitResult;
  48. constructor Create(CreateSuspended: Boolean; const StackSize: SizeUInt=DefaultStackSize);
  49. destructor Destroy; override;
  50. end;
  51. //Messages Objects passed to PostMessage will be freed automatically
  52. function utlPostMessage(const aThreadID: TThreadID; const aID: Cardinal; const aWParam, aLParam: PtrInt): Boolean; overload;
  53. function utlPostMessage(const aThreadID: TThreadID; const aID: Cardinal; const aArgs: TObject): Boolean; overload;
  54. function utlPostMessage(const aThreadID: TThreadID; const aMsg: TutlMessage): Boolean; overload;
  55. //Messages Objects passed to SendMessage must be freed by user when WaitResult is wrSignaled (otherwise the thread will handle it)
  56. function utlSendMessage(const aThreadID: TThreadID; const aID: Cardinal; const aWParam, aLParam: PtrInt;
  57. const aWaitTime: Cardinal = INFINITE): TWaitResult; overload;
  58. function utlSendMessage(const aThreadID: TThreadID; const aID: Cardinal; const aArgs: TObject;
  59. const aWaitTime: Cardinal = INFINITE): TWaitResult; overload;
  60. function utlSendMessage(const aThreadID: TThreadID; const aMsg: TutlSynchronousMessage;
  61. const aWaitTime: Cardinal = INFINITE): TWaitResult; overload;
  62. implementation
  63. uses
  64. uutlLogger, uutlExceptions;
  65. type
  66. TutlMessageThreadMap = class(specialize TutlMap<TThreadID, TutlMessageThread>)
  67. private
  68. fCS: TCriticalSection;
  69. public
  70. procedure Lock;
  71. procedure Release;
  72. constructor Create(const aOwnsObjects: Boolean = true);
  73. destructor Destroy; override;
  74. end;
  75. var
  76. Threads: TutlMessageThreadMap;
  77. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  78. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  79. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  80. function utlPostMessage(const aThreadID: TThreadID; const aID: Cardinal; const aWParam, aLParam: PtrInt): Boolean;
  81. begin
  82. result := utlPostMessage(aThreadID, TutlMessage.Create(aID, aWParam, aLParam));
  83. end;
  84. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  85. function utlPostMessage(const aThreadID: TThreadID; const aID: Cardinal; const aArgs: TObject): Boolean;
  86. begin
  87. result := utlPostMessage(aThreadID, TutlMessage.Create(aID, aArgs));
  88. end;
  89. function utlPostMessage(const aThreadID: TThreadID; const aMsg: TutlMessage): Boolean;
  90. var
  91. t: TutlMessageThread;
  92. begin
  93. Threads.Lock;
  94. try
  95. t := Threads[aThreadID];
  96. finally
  97. Threads.Release;
  98. end;
  99. result := Assigned(t);
  100. if (result) then
  101. t.PostMessage(aMsg)
  102. else
  103. aMsg.Free;
  104. end;
  105. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  106. function utlSendMessage(const aThreadID: TThreadID; const aID: Cardinal; const aWParam, aLParam: PtrInt; const aWaitTime: Cardinal): TWaitResult;
  107. begin
  108. result := utlSendMessage(aThreadID, TutlSynchronousMessage.Create(aID, aWParam, aLParam), aWaitTime);
  109. end;
  110. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  111. function utlSendMessage(const aThreadID: TThreadID; const aID: Cardinal; const aArgs: TObject; const aWaitTime: Cardinal): TWaitResult;
  112. begin
  113. result := utlSendMessage(aThreadID, TutlSynchronousMessage.Create(aID, aArgs), aWaitTime);
  114. end;
  115. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  116. function utlSendMessage(const aThreadID: TThreadID; const aMsg: TutlSynchronousMessage; const aWaitTime: Cardinal): TWaitResult;
  117. var
  118. t: TutlMessageThread;
  119. begin
  120. Threads.Lock;
  121. try
  122. t := Threads[aThreadID];
  123. finally
  124. Threads.Release;
  125. end;
  126. if Assigned(t) then
  127. result := t.SendMessage(aMsg)
  128. else begin
  129. result := wrError;
  130. aMsg.Free;
  131. end;
  132. end;
  133. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  134. //TutlMessageThread.TMessageQueue///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  135. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  136. procedure TutlMessageThread.TMessageQueue.Push(const aItem: T);
  137. begin
  138. inherited Push(aItem);
  139. fEvent.SetEvent;
  140. end;
  141. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  142. function TutlMessageThread.TMessageQueue.Pop(out aItem: T): Boolean;
  143. begin
  144. result := inherited Pop(aItem);
  145. if (Count <= 0) then
  146. fEvent.ResetEvent;
  147. end;
  148. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  149. function TutlMessageThread.TMessageQueue.WaitForMessages(const aWaitTime: Cardinal): Boolean;
  150. var
  151. wr: TWaitResult;
  152. begin
  153. wr := fEvent.WaitFor(aWaitTime);
  154. result := (wr = wrSignaled);
  155. if not result and (wr <> wrTimeout) then
  156. raise EWait.Create('Error while waiting for messages', wr);
  157. end;
  158. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  159. constructor TutlMessageThread.TMessageQueue.Create(const aOwnsObjects: Boolean);
  160. begin
  161. inherited Create(aOwnsObjects);
  162. fEvent := TSimpleEvent.Create;
  163. end;
  164. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  165. destructor TutlMessageThread.TMessageQueue.Destroy;
  166. begin
  167. inherited Destroy;
  168. FreeAndNil(fEvent); // do not free event before all messages has been deleted
  169. end;
  170. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  171. //TutlMessageThreadMap//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  172. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  173. procedure TutlMessageThreadMap.Lock;
  174. begin
  175. fCS.Acquire;
  176. end;
  177. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  178. procedure TutlMessageThreadMap.Release;
  179. begin
  180. fCS.Release;
  181. end;
  182. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  183. constructor TutlMessageThreadMap.Create(const aOwnsObjects: Boolean);
  184. begin
  185. inherited;
  186. fCS:= TCriticalSection.Create;
  187. end;
  188. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  189. destructor TutlMessageThreadMap.Destroy;
  190. begin
  191. fCS.Acquire;
  192. try
  193. inherited Destroy;
  194. finally
  195. fCS.Release;
  196. end;
  197. FreeAndNil(fCS);
  198. end;
  199. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  200. //TutlMessageThread/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  201. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  202. function TutlMessageThread.QueryInterface(constref iid: tguid; out obj): longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  203. begin
  204. if getinterface(iid,obj) then
  205. result := S_OK
  206. else
  207. result := longint(E_NOINTERFACE);
  208. end;
  209. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  210. function TutlMessageThread._AddRef: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  211. begin
  212. result := InterLockedIncrement(fRefCount);
  213. end;
  214. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  215. function TutlMessageThread._Release: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  216. begin
  217. result := InterLockedDecrement(fRefCount);
  218. end;
  219. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  220. function TutlMessageThread.CreateMessageQueue: TMessageQueue;
  221. begin
  222. result := TMessageQueue.Create(true);
  223. end;
  224. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  225. function TutlMessageThread.WaitForMessages(const aWaitTime: Cardinal): Boolean;
  226. begin
  227. result := fMessages.WaitForMessages(aWaitTime);
  228. end;
  229. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  230. function TutlMessageThread.ProcessMessages: Boolean;
  231. var
  232. m: TutlMessage;
  233. empty: Boolean;
  234. begin
  235. empty := false;
  236. result := false;
  237. repeat
  238. try
  239. if fMessages.Pop(m) then begin
  240. result := true;
  241. try
  242. ProcessMessage(m);
  243. finally
  244. if (m is TutlSynchronousMessage) then
  245. (m as TutlSynchronousMessage).Finish
  246. else
  247. FreeAndNil(m);
  248. end;
  249. end else
  250. empty := true;
  251. except
  252. on e: Exception do begin
  253. utlLogger.Error(self, 'error while progressing message %s(ID: %d; wParam: %s; lParam: %s): %s - %s', [
  254. m.ClassName,
  255. m.ID,
  256. IntToHex(m.wParam, SizeOf(m.wParam) div 4),
  257. IntToHex(m.wParam, SizeOf(m.wParam) div 4),
  258. e.ClassName,
  259. e.Message]);
  260. end;
  261. end;
  262. until empty;
  263. end;
  264. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  265. procedure TutlMessageThread.ProcessMessage(const aMessage: TutlMessage);
  266. begin
  267. case aMessage.ID of
  268. MSG_CALLBACK:
  269. (aMessage as TutlCallbackMsg).ExecuteCallback;
  270. MSG_SYNC_CALLBACK:
  271. (aMessage as TutlSyncCallbackMsg).ExecuteCallback;
  272. end;
  273. end;
  274. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  275. procedure TutlMessageThread.PostMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt);
  276. begin
  277. fMessages.Push(TutlMessage.Create(aID, aWParam, aLParam));
  278. end;
  279. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  280. procedure TutlMessageThread.PostMessage(const aID: Cardinal; const aArgs: TObject);
  281. begin
  282. fMessages.Push(TutlMessage.Create(aID, aArgs));
  283. end;
  284. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  285. procedure TutlMessageThread.PostMessage(const aMsg: TutlMessage);
  286. begin
  287. fMessages.Push(aMsg);
  288. end;
  289. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  290. function TutlMessageThread.SendMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt; const aWaitTime: Cardinal): TWaitResult;
  291. begin
  292. result := SendMessage(TutlSynchronousMessage.Create(aID, aWParam, aLParam), aWaitTime);
  293. end;
  294. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  295. function TutlMessageThread.SendMessage(const aID: Cardinal; const aArgs: TObject; const aWaitTime: Cardinal): TWaitResult;
  296. begin
  297. result := SendMessage(TutlSynchronousMessage.Create(aID, aArgs), aWaitTime);
  298. end;
  299. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  300. function TutlMessageThread.SendMessage(const aMsg: TutlSynchronousMessage; const aWaitTime: Cardinal): TWaitResult;
  301. begin
  302. fMessages.Push(aMsg);
  303. result := aMsg.WaitFor(aWaitTime);
  304. end;
  305. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  306. constructor TutlMessageThread.Create(CreateSuspended: Boolean; const StackSize: SizeUInt);
  307. begin
  308. inherited Create(CreateSuspended, StackSize);
  309. fMessages := CreateMessageQueue;
  310. Threads.Lock;
  311. try
  312. Threads.Add(ThreadID, self);
  313. finally
  314. Threads.Release;
  315. end;
  316. end;
  317. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  318. destructor TutlMessageThread.Destroy;
  319. begin
  320. Threads.Lock;
  321. try
  322. Threads.Delete(ThreadID);
  323. finally
  324. Threads.Release;
  325. end;
  326. FreeAndNil(fMessages);
  327. inherited Destroy;
  328. end;
  329. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  330. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  331. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  332. initialization
  333. Threads := TutlMessageThreadMap.Create(false);
  334. finalization
  335. Threads.Lock;
  336. try
  337. while (Threads.Count > 0) do
  338. Threads.ValueAt[Threads.Count-1].Free;
  339. finally
  340. Threads.Release;
  341. end;
  342. FreeAndNil(Threads);
  343. end.