| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- { $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.
|