Componentes.Terceros.jvcl/official/3.39/run/JvThread.pas
2010-01-18 16:55:50 +00:00

1359 lines
40 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvThread.PAS, released on 2001-02-28.
The Initial Developer of the Original Code is Sébastien Buysse [sbuysse att buypin dott com]
Portions created by Sébastien Buysse are Copyright (C) 2001 Sébastien Buysse.
All Rights Reserved.
Contributor(s): Michael Beck [mbeck att bigfoot dott com].
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.delphi-jedi.org
Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvThread.pas 12580 2009-10-26 22:37:27Z jfudickar $
unit JvThread;
{$I jvcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
SysUtils, Classes, SyncObjs,
{$IFDEF MSWINDOWS}
Windows, Controls, ExtCtrls,
{$ENDIF MSWINDOWS}
{$IFDEF UNIX}
QWindows,
{$ENDIF UNIX}
Forms, Dialogs,
JvTypes, JvComponentBase, JvComponent;
type
TJvCustomThreadDialog = class;
TJvCustomThreadDialogForm = class;
TJvThread = class;
TJvCustomThreadDialogFormEvent = procedure(DialogForm: TJvCustomThreadDialogForm) of object;
TJvThreadCancelEvent = procedure(CurrentThread: TJvThread) of object;
TJvCustomThreadDialogOptions = class(TPersistent)
private
FFormStyle: TFormStyle;
FOwner: TJvCustomThreadDialog;
FShowDelay: Integer;
FShowDialog: Boolean;
FShowModal: Boolean;
procedure SetShowDelay(const Value: Integer);
procedure SetShowDialog(Value: Boolean);
procedure SetShowModal(Value: Boolean);
public
constructor Create(AOwner: TJvCustomThreadDialog); virtual;
published
property FormStyle: TFormStyle read FFormStyle write FFormStyle;
//1 Delay in milliseconds for starting the thread dialog
property ShowDelay: Integer read FShowDelay write SetShowDelay default 0;
//1 Flag if there should be a dialog which shows the thread status
property ShowDialog: Boolean read FShowDialog write SetShowDialog default False;
//1 Flag if the status dialog is modal
property ShowModal: Boolean read FShowModal write SetShowModal default True;
end;
TJvCustomThreadDialogForm = class(TJvForm)
private
FConnectedDataObject: TObject;
FConnectedThread: TJvThread;
FDialogOptions: TJvCustomThreadDialogOptions;
FFormIsShown: Boolean;
FInternalShowDelay: Integer;
FInternalTimer: TTimer;
FInternalTimerInterval: Integer;
FOnClose: TCloseEvent;
FSaveOnClose: TCloseEvent;
FOnCloseQuery: TCloseQueryEvent;
FOnPressCancel: TJvThreadCancelEvent;
FOnShow: TNotifyEvent;
FSaveOnShow: TNotifyEvent;
FParentHandle: HWND;
procedure CloseThreadForm;
function GetConnectedDataComponent: TComponent;
procedure SetConnectedDataComponent(Value: TComponent);
procedure SetConnectedDataObject(Value: TObject);
procedure SetInternalTimerInterval(Value: Integer);
procedure SetOnClose(Value: TCloseEvent);
procedure OnInternalTimer(Sender: TObject); virtual;
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure InitializeFormContents; virtual;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure ReplaceFormClose(Sender: TObject; var Action: TCloseAction);
procedure ReplaceFormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure ReplaceFormShow(Sender: TObject);
procedure TransferDialogOptions; virtual;
procedure UpdateFormContents; virtual;
property FormIsShown: Boolean read FFormIsShown default False;
property OnPressCancel: TJvThreadCancelEvent read FOnPressCancel write FOnPressCancel;
public
constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
constructor CreateNewFormStyle(AOwner: TJvThread; FormStyle: TFormStyle;
Parent: TWinControl = nil); virtual;
destructor Destroy; override;
procedure DefaultCancelBtnClick(Sender: TObject);
property ConnectedDataComponent: TComponent read GetConnectedDataComponent write SetConnectedDataComponent;
property ConnectedDataObject: TObject read FConnectedDataObject write SetConnectedDataObject;
property ConnectedThread: TJvThread read FConnectedThread;
property DialogOptions: TJvCustomThreadDialogOptions read FDialogOptions write FDialogOptions;
property InternalTimerInterval: Integer read FInternalTimerInterval write SetInternalTimerInterval;
published
property OnClose: TCloseEvent read FOnClose write SetOnClose;
property OnCloseQuery: TCloseQueryEvent read FOnCloseQuery write FOnCloseQuery;
property OnShow: TNotifyEvent read FOnShow write FOnShow;
end;
TJvCustomThreadDialog = class(TJvComponent)
private
FDialogOptions: TJvCustomThreadDialogOptions;
FOnPressCancel: TJvThreadCancelEvent;
protected
function CreateDialogOptions: TJvCustomThreadDialogOptions; virtual; abstract;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function CreateThreadDialogForm(ConnectedThread: TJvThread): TJvCustomThreadDialogForm; virtual; abstract;
published
property DialogOptions: TJvCustomThreadDialogOptions read FDialogOptions write FDialogOptions;
property OnPressCancel: TJvThreadCancelEvent read FOnPressCancel write FOnPressCancel;
end;
TJvThreadShowMessageDlgEvent = procedure(const Msg: string; AType: TMsgDlgType;
AButtons: TMsgDlgButtons; HelpCtx: Longint; var DlgResult: Word) of object;
// This thread is a descendent of TThread but proposes a different
// behaviour with regard to being suspended or resumed.
// Indeed, the MSDN recommends not to use them and it was even noticed
// that using Suspend and Resume under Windows NT, 2K and XP led to weird
// errors such as being refused access to the thread, despite being its
// creator.
// So another mechanism has been implemented: the thread must be
// paused instead of suspended.
// Pausing the thread actually acquires a critical section which the Execute
// function must try to get before it calls InternalExecute.
// Hence, if the critical section was acquired before this try, the Execute
// function is stopped and the thread paused until another thread (the main
// thread in most cases) releases the critical section when setting
// Paused to false.
// Obviously, the Execute method in derived classes has to be cooperative
// and actually acquire and release the FPauseSection critical section via
// the appropriate protected methods
TJvPausableThread = class(TThread)
private
FPauseSection: TCriticalSection;
FPaused: Boolean;
procedure SetPaused(const Value: Boolean);
protected
procedure EnterUnpauseableSection;
procedure LeaveUnpauseableSection;
public
constructor Create(CreateSuspended: Boolean);
destructor Destroy; override;
property Paused: Boolean read FPaused write SetPaused;
end;
TJvBaseThread = class(TJvPausableThread)
private
FException: Exception;
FExceptionAddr: Pointer;
FInternalTerminate: Boolean;
FExecuteEvent: TJvNotifyParamsEvent;
FOnResumeDone: Boolean;
FExecuteIsActive: Boolean;
FFinished: Boolean;
FOnShowMessageDlgEvent: TJvThreadShowMessageDlgEvent;
FParams: Pointer;
FSender: TObject;
FSynchAButtons: TMsgDlgButtons;
FSynchAType: TMsgDlgType;
FSynchHelpCtx: Longint;
FSynchMessageDlgResult: Word;
FSynchMsg: string;
procedure ExceptionHandler;
protected
procedure InternalMessageDlg;
public
constructor Create(Sender: TObject; Event: TJvNotifyParamsEvent; Params: Pointer); virtual;
{$IFNDEF COMPILER14_UP}
procedure Resume; // There is no way to silence the compiler ("Resume" is deprecated)
{$ENDIF ~COMPILER14_UP}
procedure ResumeThread;
procedure Execute; override;
procedure Synchronize(Method: TThreadMethod);
function SynchMessageDlg(const Msg: string; AType: TMsgDlgType;
AButtons: TMsgDlgButtons; HelpCtx: Longint): Word;
property Container: TObject read FSender;
property ExecuteIsActive: Boolean read FExecuteIsActive;
property Finished: Boolean read FFinished;
property Terminated;
property Params: Pointer read FParams;
property ReturnValue;
property OnShowMessageDlgEvent: TJvThreadShowMessageDlgEvent
read FOnShowMessageDlgEvent write FOnShowMessageDlgEvent;
end;
TJvThread = class(TJvComponent)
private
FAfterCreateDialogForm: TJvCustomThreadDialogFormEvent;
FBeforeResume: TNotifyEvent;
FConnectedDataObject: TObject;
FDisalbeDialogShowDelayCounter: Integer;
FThreads: TThreadList;
FListLocker: TCriticalSection;
FLockedList: TList;
FExclusive: Boolean;
FMaxCount: Integer;
FRunOnCreate: Boolean;
FOnBegin: TNotifyEvent;
FOnExecute: TJvNotifyParamsEvent;
FOnFinish: TNotifyEvent;
FOnFinishAll: TNotifyEvent;
FFreeOnTerminate: Boolean;
FOnCancelExecute: TJvThreadCancelEvent;
FOnShowMessageDlgEvent: TJvThreadShowMessageDlgEvent;
FPriority: TThreadPriority;
FThreadDialog: TJvCustomThreadDialog;
FThreadDialogAllowed: Boolean;
FThreadDialogForm: TJvCustomThreadDialogForm;
procedure DoBegin;
procedure DoTerminate(Sender: TObject);
function GetCount: Integer;
function GetThreads(Index: Integer): TJvBaseThread;
function GetTerminated: Boolean; // in context of thread in list - for itself; in others - for all threads in list
procedure SetReturnValue(RetVal: Integer); // in context of thread in list - set return value (slower)
function GetReturnValue: Integer; // in context of thread in list - get return value (slower)
procedure CreateThreadDialogForm;
procedure CloseThreadDialogForm;
function GetConnectedDataComponent: TComponent;
function GetCurrentThread: TJvBaseThread;
procedure SetConnectedDataComponent(Value: TComponent);
procedure SetConnectedDataObject(Value: TObject);
procedure SetThreadDialog(const Value: TJvCustomThreadDialog);
procedure ShowThreadDialogForm;
protected
procedure InternalAfterCreateDialogForm(DialogForm: TJvCustomThreadDialogForm); virtual;
function GetOneThreadIsRunning: Boolean;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure CancelExecute; virtual;
function Execute(P: Pointer): TJvBaseThread;
procedure ExecuteAndWait(P: Pointer); // wait for all threads in list
procedure ExecuteThreadAndWait(P: Pointer); // wait only this thread
procedure ExecuteWithDialog(P: Pointer);
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure Synchronize(Method: TThreadMethod); // (slower)
function SynchMessageDlg(const Msg: string; AType: TMsgDlgType;
AButtons: TMsgDlgButtons; HelpCtx: Longint): Word;
procedure Lock; // for safe use of property Threads[]
procedure Unlock;
property ConnectedDataComponent: TComponent read GetConnectedDataComponent write SetConnectedDataComponent;
property ConnectedDataObject: TObject read FConnectedDataObject write SetConnectedDataObject;
property Count: Integer read GetCount;
property Threads[Index: Integer]: TJvBaseThread read GetThreads;
property LastThread: TJvBaseThread read GetCurrentThread; //GetLastThread;
property Terminated: Boolean read GetTerminated; // in context of thread in list - for itself; in others - for all threads in list
property ReturnValue: Integer read GetReturnValue write SetReturnValue; // in context of thread in list - set return value (slower)
property OneThreadIsRunning: Boolean read GetOneThreadIsRunning;
//1 Property to allow/disallow the thread dialog form
property ThreadDialogAllowed: Boolean read FThreadDialogAllowed write FThreadDialogAllowed default True;
property ThreadDialogForm: TJvCustomThreadDialogForm read FThreadDialogForm;
(*
function GetPriority(Thread: THandle): TThreadPriority;
procedure SetPriority(Thread: THandle; Priority: TThreadPriority);
{$IFDEF UNIX}
function GetPolicy(Thread: THandle): Integer;
procedure SetPolicy(Thread: THandle; Policy: Integer);
procedure SetPolicyAll(Policy: Integer);
{$ENDIF UNIX}
procedure QuitThread(Thread: THandle);
procedure Suspend(Thread: THandle); // should not be used
procedure Resume(Thread: THandle); overload;
*)
{$IFDEF UNIX}
procedure SetPolicy(Policy: Integer); // [not tested] in context of thread in list - for itself; in other contexts - for all threads in list
{$ENDIF UNIX}
//1 Disables the delayed showing of the thread dialog
procedure DisableDialogShowDelay;
//1 Enables the delayed showing of the thread dialog
procedure EnableDialogShowDelay;
//1 Is the delayed showing of the thread dialog disabled
function IsDialogShowDelayDisabled: Boolean;
procedure SetPriority(NewPriority: TThreadPriority); // in context of thread in list - for itself; in other contexts - for all threads in list
procedure Resume(BaseThread: TJvBaseThread); overload;
procedure Resume; overload; // resumes all threads including deferred (RunOnCreate=false)
procedure Suspend; // in context of thread in list - for itself; in other contexts - for all threads in list
procedure Terminate; // terminates all threads
procedure WaitFor; // wait for all threads
procedure RemoveZombie(BaseThread: TJvBaseThread); overload; // remove finished thread (where FreeOnTerminate was false)
procedure RemoveZombie; overload; // remove all finished threads (where FreeOnTerminate was false)
//1 //Combination of Terminate and WaitFor, optional RemoveZombie
procedure TerminateWaitFor(iRemoveZombies: Boolean = true);
published
property Exclusive: Boolean read FExclusive write FExclusive;
property MaxCount: Integer read FMaxCount write FMaxCount;
property RunOnCreate: Boolean read FRunOnCreate write FRunOnCreate;
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
property Priority: TThreadPriority read FPriority write FPriority default tpNormal;
property ThreadDialog: TJvCustomThreadDialog read FThreadDialog write SetThreadDialog;
property AfterCreateDialogForm: TJvCustomThreadDialogFormEvent
read FAfterCreateDialogForm write FAfterCreateDialogForm;
property BeforeResume: TNotifyEvent read FBeforeResume write FBeforeResume;
property OnBegin: TNotifyEvent read FOnBegin write FOnBegin;
property OnCancelExecute: TJvThreadCancelEvent read FOnCancelExecute write FOnCancelExecute;
property OnExecute: TJvNotifyParamsEvent read FOnExecute write FOnExecute;
property OnFinish: TNotifyEvent read FOnFinish write FOnFinish;
property OnFinishAll: TNotifyEvent read FOnFinishAll write FOnFinishAll;
property OnShowMessageDlgEvent: TJvThreadShowMessageDlgEvent read
FOnShowMessageDlgEvent write FOnShowMessageDlgEvent;
end;
// Cannot be synchronized to the MainThread (VCL)
// (rom) why are these in the interface section?
procedure Synchronize(Method: TNotifyEvent);
procedure SynchronizeParams(Method: TJvNotifyParamsEvent; P: Pointer);
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_39/run/JvThread.pas $';
Revision: '$Revision: 12580 $';
Date: '$Date: 2009-10-26 23:37:27 +0100 (lun., 26 oct. 2009) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
JvResources, JvJVCLUtils;
var
SyncMtx: THandle = 0;
procedure Synchronize(Method: TNotifyEvent);
begin
WaitForSingleObject(SyncMtx, INFINITE);
try
Method(nil);
finally
ReleaseMutex(SyncMtx);
end;
end;
procedure SynchronizeParams(Method: TJvNotifyParamsEvent; P: Pointer);
begin
WaitForSingleObject(SyncMtx, INFINITE);
try
Method(nil, P);
finally
ReleaseMutex(SyncMtx);
end;
end;
//=== { TJvCustomThreadDialogOptions } =======================================
constructor TJvCustomThreadDialogOptions.Create(AOwner: TJvCustomThreadDialog);
begin
inherited Create;
FOwner := AOwner;
FShowDialog := False;
FShowModal := True;
FShowDelay := 0;
end;
procedure TJvCustomThreadDialogOptions.SetShowDelay(const Value: Integer);
begin
FShowDelay := Value;
if FShowDelay < 0 then
FShowDelay := 0;
end;
procedure TJvCustomThreadDialogOptions.SetShowDialog(Value: Boolean);
begin
FShowDialog := Value;
end;
procedure TJvCustomThreadDialogOptions.SetShowModal(Value: Boolean);
begin
FShowModal := Value;
end;
//=== { TJvCustomThreadDialogForm } ==========================================
constructor TJvCustomThreadDialogForm.CreateNew(AOwner: TComponent; Dummy:
Integer = 0);
begin
inherited CreateNew(AOwner, Dummy);
FInternalTimerInterval := 500;
if AOwner is TJvThread then
FConnectedThread := TJvThread(AOwner)
else
raise EJVCLException.CreateRes(@RsENotATJvThread);
FSaveOnShow := inherited OnShow;
FSaveOnClose := inherited OnClose;
inherited OnShow := ReplaceFormShow;
inherited OnClose := ReplaceFormClose;
inherited OnCloseQuery := ReplaceFormCloseQuery;
FInternalTimer := TTimer.Create(Self);
FInternalTimer.OnTimer := OnInternalTimer;
FInternalTimer.Interval := FInternalTimerInterval;
FInternalShowDelay := 0;
FFormIsShown := False;
end;
constructor TJvCustomThreadDialogForm.CreateNewFormStyle(AOwner: TJvThread; FormStyle: TFormStyle;
Parent: TWinControl = nil);
begin
if FormStyle <> fsStayOnTop then
if Assigned(Parent) then
FParentHandle := Parent.Handle
else
FParentHandle := 0;
CreateNew(AOwner);
end;
destructor TJvCustomThreadDialogForm.Destroy;
begin
FreeAndNil(FInternalTimer);
inherited Destroy;
end;
procedure TJvCustomThreadDialogForm.CloseThreadForm;
begin
Hide;
if fsModal in FormState then
ModalResult := mrCancel
else
Close;
end;
procedure TJvCustomThreadDialogForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
if FParentHandle <> 0 then
Params.WndParent := FParentHandle;
end;
procedure TJvCustomThreadDialogForm.DefaultCancelBtnClick(Sender: TObject);
begin
if Assigned(FOnPressCancel) then
FOnPressCancel(FConnectedThread)
else
if Assigned(FConnectedThread) then
FConnectedThread.CancelExecute;
ModalResult := mrNone;
end;
function TJvCustomThreadDialogForm.GetConnectedDataComponent: TComponent;
begin
if Assigned(FConnectedDataObject) and (FConnectedDataObject is TComponent) then
Result := TComponent(ConnectedDataObject)
else
Result := nil;
end;
procedure TJvCustomThreadDialogForm.InitializeFormContents;
begin
end;
procedure TJvCustomThreadDialogForm.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if Operation = opRemove then
if AComponent = FConnectedDataObject then
ConnectedDataObject := nil;
end;
procedure TJvCustomThreadDialogForm.OnInternalTimer(Sender: TObject);
begin
if not (csDestroying in ComponentState) then
begin
if not Assigned(ConnectedThread) then
CloseThreadForm
else // connected component present
if ConnectedThread.Terminated or not ConnectedThread.OneThreadIsRunning then
begin
if FormIsShown then
CloseThreadForm;
end
else // not terminated
begin
if FInternalShowDelay > 0 then // Dialog is not shown until yet
FInternalShowDelay := FInternalShowDelay - FInternalTimerInterval
else
if not FormIsShown then
begin
if ConnectedThread.ThreadDialogAllowed and not ConnectedThread.IsDialogShowDelayDisabled then
begin
if DialogOptions.ShowModal then
ShowModal
else
Show;
end;
end
else
if ConnectedThread.ThreadDialogAllowed and FormIsShown then
UpdateFormContents;
end; // not terminated
end; // if not (csDestroying in ComponentState) then
end;
procedure TJvCustomThreadDialogForm.ReplaceFormClose(Sender: TObject; var Action: TCloseAction);
begin
FFormIsShown := False;
if Assigned(FInternalTimer) then
FInternalTimer.Enabled := False;
Action := caFree;
if Assigned(FOnClose) then
FOnClose(Sender, Action);
if Assigned(FSaveOnClose) then
FSaveOnClose(Sender, Action);
end;
procedure TJvCustomThreadDialogForm.ReplaceFormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if Assigned(FConnectedThread) then
CanClose := not FConnectedThread.OneThreadIsRunning
else
CanClose := True;
if CanClose then
if Assigned(FOnCloseQuery) then
FOnCloseQuery(Sender, CanClose);
end;
procedure TJvCustomThreadDialogForm.ReplaceFormShow(Sender: TObject);
begin
FFormIsShown := True;
InitializeFormContents;
UpdateFormContents;
FInternalTimer.Enabled := True;
if Assigned(FOnShow) then
FOnShow(Sender);
if Assigned(FSaveOnShow) then
FSaveOnShow(Sender);
end;
procedure TJvCustomThreadDialogForm.TransferDialogOptions;
begin
if Assigned(DialogOptions) then
fInternalShowDelay := DialogOptions.ShowDelay;
end;
procedure TJvCustomThreadDialogForm.UpdateFormContents;
begin
end;
procedure TJvCustomThreadDialogForm.SetConnectedDataComponent(Value:
TComponent);
begin
if Assigned(FConnectedDataObject) and (FConnectedDataObject is TComponent) then
TComponent(FConnectedDataObject).RemoveFreeNotification(self);
ConnectedDataObject := Value;
if Assigned(FConnectedDataObject) and (FConnectedDataObject is TComponent) then
TComponent(FConnectedDataObject).FreeNotification(self);
end;
procedure TJvCustomThreadDialogForm.SetConnectedDataObject(Value: TObject);
begin
FConnectedDataObject := Value;
end;
procedure TJvCustomThreadDialogForm.SetInternalTimerInterval(Value: Integer);
begin
if Value < 1 then
Value := 1;
FInternalTimerInterval := Value;
FInternalTimer.Interval := Value;
end;
procedure TJvCustomThreadDialogForm.SetOnClose(Value: TCloseEvent);
begin
FOnClose := Value;
end;
//=== { TJvCustomThreadDialog } ==============================================
constructor TJvCustomThreadDialog.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FDialogOptions := CreateDialogOptions;
end;
destructor TJvCustomThreadDialog.Destroy;
begin
FDialogOptions.Free;
inherited Destroy;
end;
//=== { TJvThread } ==========================================================
constructor TJvThread.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FRunOnCreate := True;
FExclusive := True;
FFreeOnTerminate := True;
FThreads := TThreadList.Create;
FListLocker := TCriticalSection.Create;
FPriority := tpNormal;
FThreadDialogAllowed := True;
FDisalbeDialogShowDelayCounter := 0;
end;
destructor TJvThread.Destroy;
begin
Terminate;
while OneThreadIsRunning do
begin
Sleep(1);
// Delphi 6+ uses an event and CheckSynchronize
CheckSynchronize; // TThread.OnTerminate is synchronized
end;
FThreads.Free;
FListLocker.Free;
inherited Destroy;
end;
procedure TJvThread.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if Operation = opRemove then
if AComponent = FThreadDialog then
FThreadDialog := nil
else
if AComponent = FThreadDialogForm then
FThreadDialogForm := nil
else
if AComponent = FConnectedDataObject then
ConnectedDataObject := nil;
end;
function TJvThread.Execute(P: Pointer): TJvBaseThread;
var
BaseThread: TJvBaseThread;
begin
BaseThread := nil;
if not ((Exclusive and OneThreadIsRunning) or ((FMaxCount > 0) and (Count >= FMaxCount))) and
Assigned(FOnExecute) then
begin
try
BaseThread := TJvBaseThread.Create(Self, FOnExecute, P);
BaseThread.FreeOnTerminate := FFreeOnTerminate;
BaseThread.OnShowMessageDlgEvent := OnShowMessageDlgEvent;
BaseThread.Priority := Priority;
BaseThread.OnTerminate := DoTerminate;
FThreads.Add(BaseThread);
DoBegin;
except
// We can't terminate right now due to discrepancy between old and recent versions of TThread
if Assigned(BaseThread) then
BaseThread.FInternalTerminate := True;
end;
if FRunOnCreate and Assigned(BaseThread) then
Resume(BaseThread);
end;
Result := BaseThread;
end;
procedure TJvThread.DoBegin;
begin
if Assigned(FOnBegin) then
FOnBegin(Self);
end;
procedure TJvThread.ExecuteAndWait(P: Pointer);
var
B: Boolean;
Thread: TJvBaseThread;
begin
B := FRunOnCreate;
FRunOnCreate := True;
try
Thread := Execute(P);
finally
FRunOnCreate := B;
end;
if Assigned(Thread) then
WaitFor; // all threads in list
end;
procedure TJvThread.ExecuteThreadAndWait(P: Pointer);
var
B: Boolean;
Thread: TJvBaseThread;
begin
B := FRunOnCreate;
FRunOnCreate := True;
try
Thread := Execute(P);
finally
FRunOnCreate := B;
end;
if Assigned(Thread) then
while (not Thread.Finished) do // wait for this thread
Application.HandleMessage;
end;
procedure TJvThread.Resume(BaseThread: TJvBaseThread);
var
B: Boolean;
begin
if Assigned(BaseThread) then
begin
CreateThreadDialogForm;
B := BaseThread.FOnResumeDone;
BaseThread.ResumeThread;
if (not B) and
(not BaseThread.FInternalTerminate) and
(not BaseThread.Finished) then
begin
ShowThreadDialogForm;
end;
end
else
Resume; // no target, resume all
end;
procedure TJvThread.Resume; // All
var
List: TList;
I: Integer;
Thread: TJvBaseThread;
begin
List := FThreads.LockList;
try
for I := 0 to List.Count - 1 do
begin
Thread := TJvBaseThread(List[I]);
while Thread.Suspended do
Resume(Thread);
end;
finally
FThreads.UnlockList;
end;
end;
procedure TJvThread.Suspend;
var
List: TList;
I: Integer;
Thread: TJvBaseThread;
begin
Thread := GetCurrentThread;
if Assigned(Thread) then
Thread.Suspended := True // suspend itself
else
begin
List := FThreads.LockList; // suspend all
try
for I := 0 to List.Count - 1 do
try // against "Access denied" for already finished threads
Thread := TJvBaseThread(List[I]);
if not Thread.Finished then // it's faster (prevents raising exceptions in most cases)
Thread.Suspended := True
except
end;
finally
FThreads.UnlockList;
end;
end;
end;
procedure TJvThread.Terminate;
var
List: TList;
I: Integer;
begin
List := FThreads.LockList;
try
for I := 0 to List.Count - 1 do
TJvBaseThread(List[I]).Terminate;
Resume; // All
finally
FThreads.UnlockList;
end;
end;
procedure TJvThread.CancelExecute;
begin
if Assigned(fOnCancelExecute) then
fOnCancelExecute (Self)
else
Terminate;
end;
procedure TJvThread.DoTerminate(Sender: TObject);
begin
TJvBaseThread(Sender).FExecuteIsActive := False;
if Assigned(FOnFinish) then
try
FOnFinish(Sender);
except
// DoTerminate is part of destructor; destructor should not raise exceptions
end;
if TJvBaseThread(Sender).FreeOnTerminate then
FThreads.Remove(Sender);
TJvBaseThread(Sender).FFinished := True;
if Count = 0 then
begin
CloseThreadDialogForm;
if Assigned(FOnFinishAll) then
try
FOnFinishAll(Self);
except
// DoTerminate is part of destructor; destructor should not raise exceptions
end;
end;
end;
procedure TJvThread.RemoveZombie(BaseThread: TJvBaseThread); // remove finished thread (where FreeOnTerminate was false)
begin
if Assigned(BaseThread) then
begin
if BaseThread.FFinished and (not BaseThread.FreeOnTerminate) then
begin
FThreads.Remove(BaseThread);
BaseThread.Free;
end;
end
else
RemoveZombie; // no target, do for all
end;
procedure TJvThread.RemoveZombie; // remove all finished threads (where FreeOnTerminate was false)
var
List: TList;
I: Integer;
Thread: TJvBaseThread;
begin
List := FThreads.LockList;
try
for I := List.Count - 1 downto 0 do
begin
Thread := TJvBaseThread(List[I]);
if Thread.FFinished and (not Thread.FreeOnTerminate) then
begin
FThreads.Remove(Thread);
Thread.Free;
end;
end;
finally
FThreads.UnlockList;
end;
end;
function TJvThread.GetTerminated: Boolean;
var
H: DWORD;
List: TList;
I: Integer;
Thread: TJvBaseThread;
begin
H := GetCurrentThreadID;
Result := True;
List:=FThreads.LockList;
try
for I := 0 to List.Count - 1 do
begin
Thread := TJvBaseThread(List[I]);
if Thread.ThreadID = H then
begin
Result := Thread.Terminated; // context of thread in list
Break;
end
else
Result := Result and Thread.Terminated; // context of all other threads
end;
finally
FThreads.UnlockList;
end;
end;
procedure TJvThread.WaitFor;
begin
while OneThreadIsRunning do
Application.HandleMessage;
end;
procedure TJvThread.SetReturnValue(RetVal: Integer);
var
Thread: TJvBaseThread;
begin
Thread := GetCurrentThread;
if Assigned(Thread) then
Thread.ReturnValue := RetVal;
end;
function TJvThread.GetReturnValue: Integer;
var
Thread: TJvBaseThread;
begin
Thread := GetCurrentThread;
if Assigned(Thread) then
Result := Thread.ReturnValue
else
Result := 0;
end;
function TJvThread.GetCount: Integer;
var
List: TList;
begin
List := FThreads.LockList;
try
Result := List.Count;
finally
FThreads.UnlockList;
end;
end;
function TJvThread.GetCurrentThread: TJvBaseThread;
var
H: DWORD;
List: TList;
I: Integer;
Thread: TJvBaseThread;
begin
Result := nil;
H := GetCurrentThreadID;
List := FThreads.LockList;
try
for I := 0 to List.Count - 1 do
begin
Thread := TJvBaseThread(List[I]);
if Thread.ThreadID = H then
begin
Result := Thread;
Break;
end;
end;
finally
FThreads.UnlockList;
end;
end;
function TJvThread.GetOneThreadIsRunning: Boolean;
var
I: Integer;
List: TList;
begin
Result := False;
List := FThreads.LockList;
try
for I := 0 to List.Count - 1 do
begin
Result := not TJvBaseThread(List[I]).Finished;
if Result then
Break;
end;
finally
FThreads.UnlockList;
end;
end;
procedure TJvThread.Lock; // for safe use of property Threads[]
begin
FListLocker.Acquire;
try
if not Assigned(FLockedList) then
FLockedList := FThreads.LockList;
except
FListLocker.Release;
raise;
end;
end;
function TJvThread.GetThreads(Index: Integer): TJvBaseThread;
begin
FListLocker.Acquire;
try
if Assigned(FLockedList) then
Result := TJvBaseThread(FLockedList[Index])
else
Result := nil;
finally
FListLocker.Release;
end;
end;
procedure TJvThread.Unlock;
begin
try
if Assigned(FLockedList) then
begin
FThreads.UnlockList;
FLockedList := nil;
end;
finally
FListLocker.Release;
end;
end;
procedure TJvThread.Synchronize(Method: TThreadMethod);
var
Thread: TJvBaseThread;
begin
DisableDialogShowDelay;
try
Thread := GetCurrentThread;
if Assigned(Thread) then
Thread.Synchronize(Method)
else
Method;
finally
EnableDialogShowDelay;
end;
end;
function TJvThread.SynchMessageDlg(const Msg: string; AType: TMsgDlgType;
AButtons: TMsgDlgButtons; HelpCtx: Longint): Word;
var
Thread: TJvBaseThread;
begin
DisableDialogShowDelay;
try
Thread := GetCurrentThread;
if Assigned(Thread) then
Result := Thread.SynchMessageDlg(Msg, AType, AButtons, HelpCtx)
else
if Assigned(OnShowMessageDlgEvent) then
OnShowMessageDlgEvent(Msg, AType,
AButtons, HelpCtx, Result)
else
Result := MessageDlg(Msg, AType, AButtons, HelpCtx);
finally
EnableDialogShowDelay;
end;
end;
// new
{$IFDEF UNIX}
// not tested
procedure TJvThread.SetPolicy(Policy: Integer);
var
List: TList;
Thread: TJvBaseThread;
I: Integer;
begin
List := FThreads.LockList;
try
Thread := GetCurrentThread;
if Assigned(Thread) then
SetThreadPolicy(Thread.Handle, Policy) // context of thread in list
else
for I := 0 to List.Count - 1 do // context of all other threads
SetThreadPolicy(TJvBaseThread(List[I]).Handle, Policy);
end;
finally
FThreads.UnlockList;
end;
end;
{$ENDIF UNIX}
procedure TJvThread.SetPriority(NewPriority: TThreadPriority);
var
List: TList;
Thread: TJvBaseThread;
I: Integer;
begin
List := FThreads.LockList;
try
Thread := GetCurrentThread;
if Assigned(Thread) then
Thread.Priority := NewPriority // context of thread in list
else
begin
for I := 0 to List.Count - 1 do // context of all other threads
TJvBaseThread(List[I]).Priority := NewPriority;
Priority := NewPriority;
end;
finally
FThreads.UnlockList;
end;
end;
procedure TJvThread.CreateThreadDialogForm;
begin
if Assigned(ThreadDialog) and not Assigned(FThreadDialogForm) then
begin
FThreadDialogForm := ThreadDialog.CreateThreadDialogForm(Self);
if Assigned(FThreadDialogForm) then
begin
FreeNotification(FThreadDialogForm);
FThreadDialogForm.ConnectedDataObject := ConnectedDataObject;
FThreadDialogForm.TransferDialogOptions;
InternalAfterCreateDialogForm(FThreadDialogForm);
end;
end;
end;
procedure TJvThread.DisableDialogShowDelay;
begin
Inc(FDisalbeDialogShowDelayCounter);
end;
procedure TJvThread.EnableDialogShowDelay;
begin
Dec(FDisalbeDialogShowDelayCounter);
end;
procedure TJvThread.ExecuteWithDialog(P: Pointer);
begin
if Assigned(ThreadDialog) and ThreadDialog.DialogOptions.ShowDialog and
ThreadDialog.DialogOptions.ShowModal then
ExecuteAndWait(P)
else
Execute(P);
end;
procedure TJvThread.CloseThreadDialogForm;
begin
if Assigned(ThreadDialogForm) then
begin
while Assigned(ThreadDialogForm) AND ThreadDialogForm.Visible do
begin
ThreadDialogForm.CloseThreadForm;
Application.HandleMessage;
end;
end;
end;
function TJvThread.GetConnectedDataComponent: TComponent;
begin
if Assigned(FConnectedDataObject) and (FConnectedDataObject is TComponent) then
Result := TComponent(ConnectedDataObject)
else
Result := nil;
end;
procedure TJvThread.InternalAfterCreateDialogForm(DialogForm: TJvCustomThreadDialogForm);
begin
if Assigned(FAfterCreateDialogForm) then
FAfterCreateDialogForm(DialogForm);
end;
function TJvThread.IsDialogShowDelayDisabled: Boolean;
begin
Result := FDisalbeDialogShowDelayCounter > 0;
end;
procedure TJvThread.SetConnectedDataComponent(Value: TComponent);
begin
if Assigned(FConnectedDataObject) and (FConnectedDataObject is TComponent) then
TComponent(FConnectedDataObject).RemoveFreeNotification(self);
ConnectedDataObject := Value;
if Assigned(FConnectedDataObject) and (FConnectedDataObject is TComponent) then
TComponent(FConnectedDataObject).FreeNotification(self);
end;
procedure TJvThread.SetConnectedDataObject(Value: TObject);
begin
FConnectedDataObject := Value;
if Assigned(FThreadDialogForm) then
FThreadDialogForm.ConnectedDataObject := Value;
end;
procedure TJvThread.SetThreadDialog(const Value: TJvCustomThreadDialog);
begin
ReplaceComponentReference (Self, Value, TComponent(FThreadDialog));
end;
procedure TJvThread.ShowThreadDialogForm;
begin
if Assigned (ThreadDialog) and Assigned(FThreadDialogForm) then
if ThreadDialog.DialogOptions.ShowDelay <= 0 then
begin
if ThreadDialog.DialogOptions.ShowModal then
FThreadDialogForm.ShowModal
else
FThreadDialogForm.Show;
end;
end;
procedure TJvThread.TerminateWaitFor(iRemoveZombies: Boolean = true);
begin
Terminate;
WaitFor;
if iRemoveZombies then
RemoveZombie;
end;
//=== { TJvBaseThread } ======================================================
constructor TJvBaseThread.Create(Sender: TObject; Event: TJvNotifyParamsEvent; Params: Pointer);
begin
inherited Create(True);
FSender := Sender;
FExecuteEvent := Event;
FParams := Params;
end;
procedure TJvBaseThread.ExceptionHandler;
begin
ShowException(FException, FExceptionAddr);
end;
procedure TJvBaseThread.ResumeThread;
begin
if not FOnResumeDone then
begin
// the first resume (perhaps deferred)
FOnResumeDone := True;
if (FSender is TJvThread) and Assigned(TJvThread(FSender).BeforeResume) then
try
TJvThread(FSender).BeforeResume(Self);
except
// Self.Terminate;
// We can't terminate right now due to discrepancy between old and recent versions TThread
FInternalTerminate := True;
end;
FExecuteIsActive := True;
end;
{$WARNINGS OFF}
inherited Resume; // after suspend too
{$WARNINGS ON}
end;
{$IFNDEF COMPILER14_UP}
procedure TJvBaseThread.Resume;
begin
ResumeThread;
end;
{$ENDIF ~COMPILER14_UP}
procedure TJvBaseThread.Execute;
begin
try
FExecuteIsActive := True;
if FInternalTerminate then
Terminate;
FExecuteEvent(Self, FParams);
except
on E: Exception do
begin
FException := E;
FExceptionAddr := ExceptAddr;
Synchronize(ExceptionHandler);
end;
end;
end;
procedure TJvBaseThread.Synchronize(Method: TThreadMethod);
begin
inherited Synchronize(Method);
end;
procedure TJvBaseThread.InternalMessageDlg;
begin
if Assigned(OnShowMessageDlgEvent) then
OnShowMessageDlgEvent(FSynchMsg, FSynchAType,
FSynchAButtons, FSynchHelpCtx, FSynchMessageDlgResult)
else
FSynchMessageDlgResult := MessageDlg(FSynchMsg, FSynchAType, FSynchAButtons, FSynchHelpCtx);
end;
function TJvBaseThread.SynchMessageDlg(const Msg: string; AType: TMsgDlgType;
AButtons: TMsgDlgButtons; HelpCtx: Longint): Word;
begin
FSynchMsg := Msg;
FSynchAType := AType;
FSynchAButtons := AButtons;
FSynchHelpCtx := HelpCtx;
Self.Synchronize(InternalMessageDlg);
Result := FSynchMessageDlgResult;
end;
{ TJvPausableThread }
constructor TJvPausableThread.Create(CreateSuspended: Boolean);
begin
FPauseSection := TCriticalSection.Create;
inherited Create(CreateSuspended);
end;
destructor TJvPausableThread.Destroy;
begin
if Paused then
begin
Terminate;
Paused := False;
end;
inherited Destroy;
FPauseSection.Free;
end;
procedure TJvPausableThread.EnterUnpauseableSection;
begin
FPauseSection.Acquire;
end;
procedure TJvPausableThread.LeaveUnpauseableSection;
begin
FPauseSection.Release;
end;
procedure TJvPausableThread.SetPaused(const Value: Boolean);
begin
if FPaused <> Value then
begin
// store the Value
FPaused := Value;
if FPaused then
FPauseSection.Acquire
else
FPauseSection.Release;
end;
// If the thread was created "Suspended", then we must start it
if Suspended and not Paused then
Suspended := False;
end;
initialization
{$IFDEF UNITVERSIONING}
RegisterUnitVersion(HInstance, UnitVersioning);
{$ENDIF UNITVERSIONING}
SyncMtx := CreateMutex(nil, False, 'VCLJvThreadMutex');
finalization
CloseHandle(SyncMtx);
SyncMtx := 0;
{$IFDEF UNITVERSIONING}
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.