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.

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