IdThread.pas 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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: 10375: IdThread.pas
  11. {
  12. { Rev 1.3 12.9.2003 ã. 16:37:08 DBondzhev
  13. { Fixed AV when exception is raised in BeforeRun and thread is terminated
  14. { before Start is compleated
  15. }
  16. {
  17. { Rev 1.3 12.9.2003 ã. 16:37:08 DBondzhev
  18. { Fixed AV when exception is raised in BeforeRun and thread is terminated
  19. { before Start is compleated
  20. }
  21. {
  22. Rev 1.2 4/21/2003 8:23:48 PM BGooijen
  23. Changed Handle to ThreadID
  24. }
  25. {
  26. Rev 1.1 3/22/2003 1:56:42 AM BGooijen
  27. Fixed a bug where non-paged memory was leaked when an exception occured in
  28. TIdListenerThread.Run
  29. }
  30. {
  31. { Rev 1.0 2002.11.12 10:55:54 PM czhower
  32. }
  33. unit IdThread;
  34. {
  35. 2002-03-12 -Andrew P.Rybin
  36. -TerminatingExceptionClass,SynchronizeEx
  37. }
  38. {$I IdCompilerDefines.inc}
  39. interface
  40. uses
  41. Classes,
  42. IdException,
  43. IdGlobal,
  44. SysUtils, SyncObjs;
  45. type
  46. EIdThreadException = class(EIdException);
  47. EIdThreadTerminateAndWaitFor = class(EIdThreadException);
  48. TIdThreadStopMode = (smTerminate, smSuspend);
  49. TIdThread = class;
  50. TIdExceptionThreadEvent = procedure(AThread: TIdThread; AException: Exception) of object;
  51. TIdNotifyThreadEvent = procedure(AThread: TIdThread) of object;
  52. TIdSynchronizeThreadEvent = procedure(AThread: TIdThread; AData: Pointer) of object;
  53. // Expose protected members
  54. TIdBaseThread = class(TThread)
  55. public
  56. procedure Synchronize(Method: TThreadMethod); overload;
  57. procedure Synchronize(Method: TMethod); overload;
  58. //
  59. property ReturnValue;
  60. property Terminated;
  61. End;//TIdBaseThread
  62. TIdThread = class(TIdBaseThread)
  63. protected
  64. FData: TObject;
  65. FLock: TCriticalSection;
  66. FStopMode: TIdThreadStopMode;
  67. FStopped: Boolean;
  68. FTerminatingException: string;
  69. FTerminatingExceptionClass: TClass;
  70. FOnException: TIdExceptionThreadEvent;
  71. FOnStopped: TIdNotifyThreadEvent;
  72. //
  73. procedure AfterRun; virtual; //3* Not abstract - otherwise it is required
  74. procedure AfterExecute; virtual;//5 Not abstract - otherwise it is required
  75. procedure BeforeExecute; virtual;//1 Not abstract - otherwise it is required
  76. procedure BeforeRun; virtual; //2* Not abstract - otherwise it is required
  77. procedure Cleanup; virtual;//4*
  78. procedure DoException (AException: Exception); virtual;
  79. procedure DoStopped; virtual;
  80. procedure Execute; override;
  81. function GetStopped: Boolean;
  82. procedure Run; virtual; abstract;
  83. public
  84. constructor Create(ACreateSuspended: Boolean = True); virtual;
  85. destructor Destroy; override;
  86. procedure Start; virtual;
  87. procedure Stop; virtual;
  88. // Here to make virtual
  89. procedure Terminate; virtual;
  90. procedure TerminateAndWaitFor; virtual;
  91. //
  92. property Data: TObject read FData write FData;
  93. property StopMode: TIdThreadStopMode read FStopMode write FStopMode;
  94. property Stopped: Boolean read GetStopped;
  95. // in future versions (D6+) we must move to TThread.FatalException
  96. property TerminatingException: string read FTerminatingException;
  97. property TerminatingExceptionClass: TClass read FTerminatingExceptionClass;
  98. // events
  99. property OnException: TIdExceptionThreadEvent read FOnException write FOnException;
  100. property OnStopped: TIdNotifyThreadEvent read FOnStopped write FOnStopped;
  101. End;//TIdThread
  102. TIdThreadClass = class of TIdThread;
  103. implementation
  104. uses IdResourceStrings;
  105. var
  106. GThreadLock: TCriticalSection;
  107. GThreadCount: Integer;
  108. procedure IncThread(AThread: TIdThread);
  109. Begin
  110. if NOT Assigned(GThreadLock) then begin
  111. GThreadLock := TCriticalSection.Create;
  112. end;
  113. GThreadLock.Enter;
  114. inc(GThreadCount);
  115. GThreadLock.Leave;
  116. End;//IncThread
  117. procedure DecThread(AThread: TIdThread);
  118. Begin
  119. if Assigned(GThreadLock) then begin
  120. GThreadLock.Enter;
  121. dec(GThreadCount);
  122. GThreadLock.Leave;
  123. end;
  124. End;//DecThread
  125. procedure WaitAllTerminated;
  126. var
  127. LDone: Boolean;
  128. Begin
  129. while Assigned(GThreadLock) do begin
  130. GThreadLock.Enter;
  131. LDone := GThreadCount = 0;
  132. GThreadLock.Leave;
  133. if LDone then begin
  134. FreeAndNIL(GThreadLock);
  135. end
  136. else begin
  137. Sleep(5000);
  138. end;
  139. end;
  140. End;//WaitAllTerminated
  141. procedure TIdThread.TerminateAndWaitFor;
  142. begin
  143. if FreeOnTerminate then begin
  144. raise EIdThreadTerminateAndWaitFor.Create(RSThreadTerminateAndWaitFor);
  145. end;
  146. Terminate;
  147. Start;
  148. WaitFor;
  149. end;
  150. procedure TIdThread.BeforeRun;
  151. begin
  152. end;
  153. procedure TIdThread.AfterRun;
  154. begin
  155. end;
  156. procedure TIdThread.BeforeExecute;
  157. begin
  158. end;
  159. procedure TIdThread.AfterExecute;
  160. Begin
  161. end;
  162. procedure TIdThread.Execute;
  163. begin
  164. try
  165. try
  166. BeforeExecute;
  167. while not Terminated do begin
  168. if Stopped then begin
  169. DoStopped;
  170. // It is possible that either in the DoStopped or from another thread,
  171. // the thread is restarted, in which case we dont want to restop it.
  172. if Stopped then begin // DONE: if terminated?
  173. if Terminated then begin
  174. Break;
  175. end;
  176. Suspend; // Thread manager will revive us
  177. if Terminated then begin
  178. Break;
  179. end;
  180. end;
  181. end;
  182. try
  183. BeforeRun;
  184. try
  185. while not Stopped do begin
  186. try
  187. Run;
  188. except
  189. on E: Exception do begin
  190. Terminate;
  191. raise;
  192. end;
  193. end;//trye
  194. end;//while
  195. finally
  196. AfterRun;
  197. end;//tryf
  198. finally
  199. Cleanup;
  200. end;
  201. end;//while NOT Terminated
  202. finally
  203. AfterExecute;
  204. end;
  205. except
  206. on E: Exception do begin
  207. FTerminatingExceptionClass := E.ClassType;
  208. FTerminatingException := E.Message;
  209. DoException(E);
  210. Terminate;
  211. end;
  212. end;//trye
  213. end;
  214. constructor TIdThread.Create(ACreateSuspended: Boolean);
  215. begin
  216. // Before inherited - inherited creates the actual thread and if not suspeded
  217. // will start before we initialize
  218. FStopped := ACreateSuspended;
  219. FLock := TCriticalSection.Create;
  220. inherited Create(ACreateSuspended);
  221. {$IFNDEF VCL6ORABOVE}
  222. if (ThreadID=0) then begin
  223. RaiseLastWin32Error;
  224. end;
  225. {$ENDIF}
  226. try
  227. IncThread(SELF);
  228. except end;
  229. end;
  230. destructor TIdThread.Destroy;
  231. begin
  232. FreeOnTerminate := FALSE; //prevent destroy between Terminate & WaitFor
  233. Terminate;
  234. inherited Destroy; //+WaitFor!
  235. try
  236. Cleanup;
  237. finally
  238. // Protect FLock if thread was resumed by Start Method and we are still there.
  239. // This usually happens if Exception was raised in BeforeRun for some reason
  240. // And thread was terminated there before Start method was completed.
  241. FLock.Enter; try
  242. finally FLock.Leave; end;
  243. try
  244. FreeAndNil(FLock);
  245. if ThreadID<>0 then begin // did we ever create a thread?
  246. DecThread(SELF);
  247. end;
  248. except end;
  249. end;//tryf
  250. end;
  251. procedure TIdThread.Start;
  252. begin
  253. FLock.Enter; try
  254. if Stopped then begin
  255. // Resume is also called for smTerminate as .Start can be used to initially start a
  256. // thread that is created suspended
  257. FStopped := Terminated;//FALSE
  258. Resume;
  259. end;
  260. finally FLock.Leave; end;
  261. end;
  262. procedure TIdThread.Stop;
  263. begin
  264. FLock.Enter;
  265. try
  266. if not Stopped then begin
  267. case FStopMode of
  268. smTerminate: Terminate;
  269. // DO NOT suspend here. Suspend is immediate. See Execute for implementation
  270. smSuspend: ;
  271. end;
  272. FStopped := True;
  273. end;
  274. finally FLock.Leave; end;
  275. end;
  276. function TIdThread.GetStopped: Boolean;
  277. begin
  278. if Assigned(FLock) then begin
  279. FLock.Enter;
  280. try
  281. // Suspended may be true if checking stopped from another thread
  282. Result := Terminated or FStopped or Suspended;
  283. finally FLock.Leave; end;
  284. end else begin
  285. Result := TRUE; //user call Destroy
  286. end;
  287. End;//GetStopped
  288. procedure TIdThread.DoStopped;
  289. begin
  290. if Assigned(OnStopped) then begin
  291. OnStopped(Self);
  292. end;
  293. end;
  294. procedure TIdThread.DoException (AException: Exception);
  295. Begin
  296. if Assigned(FOnException) then begin
  297. FOnException(self, AException);
  298. end;
  299. end;
  300. procedure TIdThread.Terminate;
  301. begin
  302. FLock.Enter;
  303. try
  304. FStopped := True;
  305. inherited Terminate;
  306. finally
  307. FLock.Leave;
  308. end;//tryf
  309. end;
  310. procedure TIdThread.Cleanup;
  311. begin
  312. FreeAndNil(FData);
  313. end;
  314. { TIdBaseThread }
  315. procedure TIdBaseThread.Synchronize(Method: TThreadMethod);
  316. Begin
  317. inherited Synchronize(Method);
  318. End;//
  319. procedure TIdBaseThread.Synchronize(Method: TMethod);
  320. Begin
  321. inherited Synchronize(TThreadMethod(Method));
  322. End;//
  323. initialization
  324. finalization
  325. WaitAllTerminated;
  326. end.