IdSync.pas 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10355: IdSync.pas
  11. {
  12. { Rev 1.2 3/11/04 12:28:58 AM RLebeau
  13. { Improper cast fixed.
  14. }
  15. {
  16. { Rev 1.1 05.6.2003 ã. 11:25:36 DBondzhev
  17. { Fix for Memleak when notification object is about to be executed in main
  18. { thread.
  19. { Also WaitFor to wait for notification to be executed.
  20. }
  21. {
  22. { Rev 1.0 2002.11.12 10:54:24 PM czhower
  23. }
  24. unit IdSync;
  25. interface
  26. // Author: Chad Z. Hower - a.k.a. Kudzu
  27. uses
  28. Classes,
  29. IdGlobal, IdThread;
  30. type
  31. TIdSync = class(TObject)
  32. protected
  33. FThread: TIdBaseThread;
  34. public
  35. constructor Create; overload; virtual;
  36. constructor Create(AThread: TIdBaseThread); overload; virtual;
  37. procedure DoSynchronize; virtual; abstract;
  38. procedure Synchronize;
  39. //
  40. property Thread: TIdBaseThread read FThread;
  41. end;
  42. TIdNotify = class(TObject)
  43. protected
  44. FMainThreadUsesNotify: Boolean;
  45. public
  46. constructor Create; virtual; // here to make virtual
  47. procedure DoNotify; virtual; abstract;
  48. procedure Notify;
  49. procedure WaitFor;
  50. class procedure NotifyMethod(AMethod: TThreadMethod);
  51. //
  52. property MainThreadUsesNotify: Boolean read FMainThreadUsesNotify write FMainThreadUsesNotify;
  53. end;
  54. TIdNotifyMethod = class(TIdNotify)
  55. protected
  56. FMethod: TThreadMethod;
  57. public
  58. constructor Create(AMethod: TThreadMethod); reintroduce;
  59. procedure DoNotify; override;
  60. end;
  61. implementation
  62. uses
  63. SysUtils;
  64. type
  65. // This is done with a NotifyThread instead of PostMessage because starting with D6/Kylix Borland
  66. // radically modified the mecanisms for .Synchronize. This is a bit more code in the end, but
  67. // its source compatible and does not rely on Indy directly accessing any OS APIs and performance
  68. // is still more than acceptable, especially considering Notifications are low priority.
  69. TIdNotifyThread = class(TIdBaseThread)
  70. protected
  71. FEvent: TIdLocalEvent;
  72. FNotifications: TThreadList;
  73. public
  74. procedure AddNotification(ASync: TIdNotify);
  75. constructor Create(ASuspended: Boolean);
  76. destructor Destroy; override;
  77. procedure Execute; override;
  78. end;
  79. var
  80. GNotifyThread: TIdNotifyThread = nil;
  81. procedure CreateNotifyThread;
  82. begin
  83. if GNotifyThread = nil then begin
  84. GNotifyThread := TIdNotifyThread.Create(False);
  85. end;
  86. end;
  87. { TIdSync }
  88. constructor TIdSync.Create(AThread: TIdBaseThread);
  89. begin
  90. inherited Create;
  91. FThread := AThread;
  92. end;
  93. constructor TIdNotify.Create;
  94. begin
  95. inherited Create;
  96. end;
  97. procedure TIdNotify.Notify;
  98. begin
  99. if InMainThread and (MainThreadUsesNotify = False) then begin
  100. DoNotify;
  101. Free;
  102. end else begin
  103. CreateNotifyThread;
  104. GNotifyThread.AddNotification(Self);
  105. end;
  106. end;
  107. class procedure TIdNotify.NotifyMethod(AMethod: TThreadMethod);
  108. begin
  109. TIdNotifyMethod.Create(AMethod).Notify;
  110. end;
  111. constructor TIdSync.Create;
  112. begin
  113. CreateNotifyThread;
  114. FThread := GNotifyThread;
  115. end;
  116. procedure TIdSync.Synchronize;
  117. begin
  118. FThread.Synchronize(DoSynchronize);
  119. end;
  120. { TIdNotifyThread }
  121. procedure TIdNotifyThread.AddNotification(ASync: TIdNotify);
  122. begin
  123. FNotifications.Add(ASync);
  124. FEvent.SetEvent;
  125. end;
  126. constructor TIdNotifyThread.Create(ASuspended: Boolean);
  127. begin
  128. FEvent := TIdLocalEvent.Create;
  129. FNotifications := TThreadList.Create;
  130. // Must be before - Thread starts running when we call inherited
  131. inherited Create(ASuspended);
  132. end;
  133. destructor TIdNotifyThread.Destroy;
  134. begin
  135. // Free remaining Notifications if thre is somthing that is still in
  136. // the queue after thread was terminated
  137. with FNotifications.LockList do try
  138. while Count > 0 do begin
  139. TIdNotify(Items[0]).Free;
  140. Delete(0);
  141. end;
  142. finally FNotifications.UnlockList; end;
  143. FreeAndNil(FNotifications);
  144. FreeAndNil(FEvent);
  145. inherited Destroy;
  146. end;
  147. procedure TIdNotifyThread.Execute;
  148. // NOTE: Be VERY careful with making changes to this proc. It is VERY delicate and the order
  149. // of execution is very important. Small changes can have drastic effects
  150. var
  151. LNotifications: TList;
  152. LNotify: TIdNotify;
  153. begin
  154. repeat
  155. FEvent.WaitFor;
  156. // If terminated while waiting on the event or during the loop
  157. while not Terminated do begin
  158. try
  159. LNotifications := FNotifications.LockList;
  160. try
  161. if LNotifications.Count = 0 then begin
  162. Break;
  163. end;
  164. LNotify := TIdNotify(LNotifications.Items[0]);
  165. LNotifications.Delete(0);
  166. finally
  167. FNotifications.UnlockList;
  168. end;
  169. try
  170. Synchronize(LNotify.DoNotify);
  171. finally
  172. FreeAndNil(LNotify);
  173. end;
  174. except // Catch all exceptions especially these which are raised during the application close
  175. end;
  176. end;
  177. until Terminated;
  178. end;
  179. { TIdNotifyMethod }
  180. constructor TIdNotifyMethod.Create(AMethod: TThreadMethod);
  181. begin
  182. FMethod := AMethod;
  183. end;
  184. procedure TIdNotifyMethod.DoNotify;
  185. begin
  186. FMethod;
  187. end;
  188. procedure TIdNotify.WaitFor;
  189. Var
  190. LNotifyIndex: Integer;
  191. begin
  192. LNotifyIndex := 0;
  193. while LNotifyIndex <> -1 do begin
  194. with GNotifyThread.FNotifications.LockList do try
  195. LNotifyIndex := IndexOf(Self);
  196. finally
  197. GNotifyThread.FNotifications.UnlockList;
  198. end;
  199. Sleep(10);
  200. end;
  201. end;
  202. initialization
  203. finalization
  204. // Will free itself using FreeOnTerminate
  205. if GNotifyThread <> nil then begin
  206. GNotifyThread.Terminate;
  207. GNotifyThread.FEvent.SetEvent;
  208. GNotifyThread.WaitFor;
  209. FreeAndNil(GNotifyThread);
  210. end;
  211. end.