25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 

397 satır
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 - %s', [e.ClassName, e.Message]);
  187. end;
  188. end;
  189. until empty;
  190. end;
  191. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  192. constructor TutlMessageThread.TMessageQueue.Create(const aOwnsObjects: Boolean);
  193. begin
  194. inherited Create(aOwnsObjects);
  195. fEvent := TSimpleEvent.Create;
  196. end;
  197. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  198. destructor TutlMessageThread.TMessageQueue.Destroy;
  199. begin
  200. inherited Destroy;
  201. FreeAndNil(fEvent); // do not free event before all messages has been deleted
  202. end;
  203. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  204. //TutlMessageThreadMap//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  205. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  206. procedure TutlMessageThreadMap.Lock;
  207. begin
  208. fCS.Acquire;
  209. end;
  210. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  211. procedure TutlMessageThreadMap.Release;
  212. begin
  213. fCS.Release;
  214. end;
  215. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  216. constructor TutlMessageThreadMap.Create(const aOwnsObjects: Boolean);
  217. begin
  218. inherited;
  219. fCS:= TCriticalSection.Create;
  220. end;
  221. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  222. destructor TutlMessageThreadMap.Destroy;
  223. begin
  224. fCS.Acquire;
  225. try
  226. inherited Destroy;
  227. finally
  228. fCS.Release;
  229. end;
  230. FreeAndNil(fCS);
  231. end;
  232. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  233. //TutlMessageThread/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  234. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  235. function TutlMessageThread.QueryInterface(constref iid: tguid; out obj): longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  236. begin
  237. if getinterface(iid,obj) then
  238. result := S_OK
  239. else
  240. result := longint(E_NOINTERFACE);
  241. end;
  242. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  243. function TutlMessageThread._AddRef: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  244. begin
  245. result := InterLockedIncrement(fRefCount);
  246. end;
  247. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  248. function TutlMessageThread._Release: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  249. begin
  250. result := InterLockedDecrement(fRefCount);
  251. end;
  252. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  253. function TutlMessageThread.CreateMessageQueue: TMessageQueue;
  254. begin
  255. result := TMessageQueue.Create(true);
  256. end;
  257. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  258. function TutlMessageThread.WaitForMessages(const aWaitTime: Cardinal): Boolean;
  259. begin
  260. result := fMessages.WaitForMessages(aWaitTime);
  261. end;
  262. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  263. function TutlMessageThread.ProcessMessages: Boolean;
  264. begin
  265. result := fMessages.ProcessMessages(@ProcessMessage);
  266. end;
  267. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  268. procedure TutlMessageThread.ProcessMessage(const aMessage: TutlMessage);
  269. begin
  270. case aMessage.ID of
  271. MSG_CALLBACK:
  272. (aMessage as TutlCallbackMsg).ExecuteCallback;
  273. MSG_SYNC_CALLBACK:
  274. (aMessage as TutlSyncCallbackMsg).ExecuteCallback;
  275. end;
  276. end;
  277. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  278. procedure TutlMessageThread.PostMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt);
  279. begin
  280. fMessages.Push(TutlMessage.Create(aID, aWParam, aLParam));
  281. end;
  282. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  283. procedure TutlMessageThread.PostMessage(const aID: Cardinal; const aArgs: TObject);
  284. begin
  285. fMessages.Push(TutlMessage.Create(aID, aArgs));
  286. end;
  287. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  288. procedure TutlMessageThread.PostMessage(const aMsg: TutlMessage);
  289. begin
  290. fMessages.Push(aMsg);
  291. end;
  292. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  293. function TutlMessageThread.SendMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt; const aWaitTime: Cardinal): TWaitResult;
  294. begin
  295. result := SendMessage(TutlSynchronousMessage.Create(aID, aWParam, aLParam), aWaitTime);
  296. end;
  297. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  298. function TutlMessageThread.SendMessage(const aID: Cardinal; const aArgs: TObject; const aWaitTime: Cardinal): TWaitResult;
  299. begin
  300. result := SendMessage(TutlSynchronousMessage.Create(aID, aArgs), aWaitTime);
  301. end;
  302. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  303. function TutlMessageThread.SendMessage(const aMsg: TutlSynchronousMessage; const aWaitTime: Cardinal): TWaitResult;
  304. begin
  305. fMessages.Push(aMsg);
  306. result := aMsg.WaitFor(aWaitTime);
  307. end;
  308. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  309. constructor TutlMessageThread.Create(CreateSuspended: Boolean; const StackSize: SizeUInt);
  310. begin
  311. inherited Create(CreateSuspended, StackSize);
  312. fMessages := CreateMessageQueue;
  313. Threads.Lock;
  314. try
  315. Threads.Add(ThreadID, self);
  316. finally
  317. Threads.Release;
  318. end;
  319. end;
  320. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  321. destructor TutlMessageThread.Destroy;
  322. begin
  323. Threads.Lock;
  324. try
  325. Threads.Delete(ThreadID);
  326. finally
  327. Threads.Release;
  328. end;
  329. FreeAndNil(fMessages);
  330. inherited Destroy;
  331. end;
  332. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  333. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  334. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  335. initialization
  336. Threads := TutlMessageThreadMap.Create(false);
  337. finalization
  338. Threads.Lock;
  339. try
  340. while (Threads.Count > 0) do
  341. Threads.ValueAt[Threads.Count-1].Free;
  342. finally
  343. Threads.Release;
  344. end;
  345. FreeAndNil(Threads);
  346. end.