{ $HDR$} {**********************************************************************} { Unit archived using Team Coherence } { Team Coherence is Copyright 2002 by Quality Software Components } { } { For further information / comments, visit our WEB site at } { http://www.TeamCoherence.com } {**********************************************************************} {} { $Log: 10375: IdThread.pas { { Rev 1.3 12.9.2003 ã. 16:37:08 DBondzhev { Fixed AV when exception is raised in BeforeRun and thread is terminated { before Start is compleated } { { Rev 1.3 12.9.2003 ã. 16:37:08 DBondzhev { Fixed AV when exception is raised in BeforeRun and thread is terminated { before Start is compleated } { Rev 1.2 4/21/2003 8:23:48 PM BGooijen Changed Handle to ThreadID } { Rev 1.1 3/22/2003 1:56:42 AM BGooijen Fixed a bug where non-paged memory was leaked when an exception occured in TIdListenerThread.Run } { { Rev 1.0 2002.11.12 10:55:54 PM czhower } unit IdThread; { 2002-03-12 -Andrew P.Rybin -TerminatingExceptionClass,SynchronizeEx } {$I IdCompilerDefines.inc} interface uses Classes, IdException, IdGlobal, SysUtils, SyncObjs; type EIdThreadException = class(EIdException); EIdThreadTerminateAndWaitFor = class(EIdThreadException); TIdThreadStopMode = (smTerminate, smSuspend); TIdThread = class; TIdExceptionThreadEvent = procedure(AThread: TIdThread; AException: Exception) of object; TIdNotifyThreadEvent = procedure(AThread: TIdThread) of object; TIdSynchronizeThreadEvent = procedure(AThread: TIdThread; AData: Pointer) of object; // Expose protected members TIdBaseThread = class(TThread) public procedure Synchronize(Method: TThreadMethod); overload; procedure Synchronize(Method: TMethod); overload; // property ReturnValue; property Terminated; End;//TIdBaseThread TIdThread = class(TIdBaseThread) protected FData: TObject; FLock: TCriticalSection; FStopMode: TIdThreadStopMode; FStopped: Boolean; FTerminatingException: string; FTerminatingExceptionClass: TClass; FOnException: TIdExceptionThreadEvent; FOnStopped: TIdNotifyThreadEvent; // procedure AfterRun; virtual; //3* Not abstract - otherwise it is required procedure AfterExecute; virtual;//5 Not abstract - otherwise it is required procedure BeforeExecute; virtual;//1 Not abstract - otherwise it is required procedure BeforeRun; virtual; //2* Not abstract - otherwise it is required procedure Cleanup; virtual;//4* procedure DoException (AException: Exception); virtual; procedure DoStopped; virtual; procedure Execute; override; function GetStopped: Boolean; procedure Run; virtual; abstract; public constructor Create(ACreateSuspended: Boolean = True); virtual; destructor Destroy; override; procedure Start; virtual; procedure Stop; virtual; // Here to make virtual procedure Terminate; virtual; procedure TerminateAndWaitFor; virtual; // property Data: TObject read FData write FData; property StopMode: TIdThreadStopMode read FStopMode write FStopMode; property Stopped: Boolean read GetStopped; // in future versions (D6+) we must move to TThread.FatalException property TerminatingException: string read FTerminatingException; property TerminatingExceptionClass: TClass read FTerminatingExceptionClass; // events property OnException: TIdExceptionThreadEvent read FOnException write FOnException; property OnStopped: TIdNotifyThreadEvent read FOnStopped write FOnStopped; End;//TIdThread TIdThreadClass = class of TIdThread; implementation uses IdResourceStrings; var GThreadLock: TCriticalSection; GThreadCount: Integer; procedure IncThread(AThread: TIdThread); Begin if NOT Assigned(GThreadLock) then begin GThreadLock := TCriticalSection.Create; end; GThreadLock.Enter; inc(GThreadCount); GThreadLock.Leave; End;//IncThread procedure DecThread(AThread: TIdThread); Begin if Assigned(GThreadLock) then begin GThreadLock.Enter; dec(GThreadCount); GThreadLock.Leave; end; End;//DecThread procedure WaitAllTerminated; var LDone: Boolean; Begin while Assigned(GThreadLock) do begin GThreadLock.Enter; LDone := GThreadCount = 0; GThreadLock.Leave; if LDone then begin FreeAndNIL(GThreadLock); end else begin Sleep(5000); end; end; End;//WaitAllTerminated procedure TIdThread.TerminateAndWaitFor; begin if FreeOnTerminate then begin raise EIdThreadTerminateAndWaitFor.Create(RSThreadTerminateAndWaitFor); end; Terminate; Start; WaitFor; end; procedure TIdThread.BeforeRun; begin end; procedure TIdThread.AfterRun; begin end; procedure TIdThread.BeforeExecute; begin end; procedure TIdThread.AfterExecute; Begin end; procedure TIdThread.Execute; begin try try BeforeExecute; while not Terminated do begin if Stopped then begin DoStopped; // It is possible that either in the DoStopped or from another thread, // the thread is restarted, in which case we dont want to restop it. if Stopped then begin // DONE: if terminated? if Terminated then begin Break; end; Suspend; // Thread manager will revive us if Terminated then begin Break; end; end; end; try BeforeRun; try while not Stopped do begin try Run; except on E: Exception do begin Terminate; raise; end; end;//trye end;//while finally AfterRun; end;//tryf finally Cleanup; end; end;//while NOT Terminated finally AfterExecute; end; except on E: Exception do begin FTerminatingExceptionClass := E.ClassType; FTerminatingException := E.Message; DoException(E); Terminate; end; end;//trye end; constructor TIdThread.Create(ACreateSuspended: Boolean); begin // Before inherited - inherited creates the actual thread and if not suspeded // will start before we initialize FStopped := ACreateSuspended; FLock := TCriticalSection.Create; inherited Create(ACreateSuspended); {$IFNDEF VCL6ORABOVE} if (ThreadID=0) then begin RaiseLastWin32Error; end; {$ENDIF} try IncThread(SELF); except end; end; destructor TIdThread.Destroy; begin FreeOnTerminate := FALSE; //prevent destroy between Terminate & WaitFor Terminate; inherited Destroy; //+WaitFor! try Cleanup; finally // Protect FLock if thread was resumed by Start Method and we are still there. // This usually happens if Exception was raised in BeforeRun for some reason // And thread was terminated there before Start method was completed. FLock.Enter; try finally FLock.Leave; end; try FreeAndNil(FLock); if ThreadID<>0 then begin // did we ever create a thread? DecThread(SELF); end; except end; end;//tryf end; procedure TIdThread.Start; begin FLock.Enter; try if Stopped then begin // Resume is also called for smTerminate as .Start can be used to initially start a // thread that is created suspended FStopped := Terminated;//FALSE Resume; end; finally FLock.Leave; end; end; procedure TIdThread.Stop; begin FLock.Enter; try if not Stopped then begin case FStopMode of smTerminate: Terminate; // DO NOT suspend here. Suspend is immediate. See Execute for implementation smSuspend: ; end; FStopped := True; end; finally FLock.Leave; end; end; function TIdThread.GetStopped: Boolean; begin if Assigned(FLock) then begin FLock.Enter; try // Suspended may be true if checking stopped from another thread Result := Terminated or FStopped or Suspended; finally FLock.Leave; end; end else begin Result := TRUE; //user call Destroy end; End;//GetStopped procedure TIdThread.DoStopped; begin if Assigned(OnStopped) then begin OnStopped(Self); end; end; procedure TIdThread.DoException (AException: Exception); Begin if Assigned(FOnException) then begin FOnException(self, AException); end; end; procedure TIdThread.Terminate; begin FLock.Enter; try FStopped := True; inherited Terminate; finally FLock.Leave; end;//tryf end; procedure TIdThread.Cleanup; begin FreeAndNil(FData); end; { TIdBaseThread } procedure TIdBaseThread.Synchronize(Method: TThreadMethod); Begin inherited Synchronize(Method); End;// procedure TIdBaseThread.Synchronize(Method: TMethod); Begin inherited Synchronize(TThreadMethod(Method)); End;// initialization finalization WaitAllTerminated; end.