mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
232 lines
6.5 KiB
ObjectPascal
232 lines
6.5 KiB
ObjectPascal
unit VirtualTrees.WorkerThread;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Classes,
|
|
VirtualTrees;
|
|
|
|
type
|
|
// internal worker thread
|
|
TWorkerThread = class(TThread)
|
|
private
|
|
FCurrentTree: TBaseVirtualTree;
|
|
FWaiterList: TThreadList;
|
|
FRefCount: Integer;
|
|
class procedure EnsureCreated();
|
|
class procedure Dispose(CanBlock: Boolean);
|
|
procedure WaitForValidationTermination(Tree: TBaseVirtualTree);
|
|
protected
|
|
procedure Execute; override;
|
|
public
|
|
constructor Create();
|
|
destructor Destroy; override;
|
|
|
|
/// For lifeteime management of the TWorkerThread
|
|
class procedure AddThreadReference;
|
|
class procedure ReleaseThreadReference(ACanBlock: Boolean = False);
|
|
|
|
class procedure AddTree(Tree: TBaseVirtualTree);
|
|
class procedure RemoveTree(Tree: TBaseVirtualTree);
|
|
|
|
property CurrentTree: TBaseVirtualTree read FCurrentTree;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
Winapi.Windows,
|
|
System.Types,
|
|
System.SysUtils;
|
|
|
|
type
|
|
TBaseVirtualTreeCracker = class(TBaseVirtualTree)
|
|
end;
|
|
|
|
var
|
|
WorkerThread: TWorkerThread = nil;
|
|
WorkEvent: THandle;
|
|
//----------------- TWorkerThread --------------------------------------------------------------------------------------
|
|
|
|
class procedure TWorkerThread.EnsureCreated();
|
|
begin
|
|
if not Assigned(WorkerThread) then
|
|
begin
|
|
// Create an event used to trigger our worker thread when something is to do.
|
|
WorkEvent := CreateEvent(nil, False, False, nil);
|
|
if WorkEvent = 0 then
|
|
RaiseLastOSError;
|
|
|
|
// Create worker thread, initialize it and send it to its wait loop.
|
|
WorkerThread := TWorkerThread.Create();
|
|
end;
|
|
end;
|
|
|
|
class procedure TWorkerThread.Dispose;
|
|
var
|
|
LRef: TThread;
|
|
begin
|
|
WorkerThread.FreeOnTerminate := not CanBlock;
|
|
WorkerThread.Terminate();
|
|
SetEvent(WorkEvent);
|
|
LRef := WorkerThread;
|
|
WorkerThread := nil; //Will be freed usinf TThreaf.FreeOnTerminate
|
|
CloseHandle(WorkEvent);
|
|
if CanBlock then
|
|
LRef.Free;
|
|
end;
|
|
|
|
|
|
class procedure TWorkerThread.AddThreadReference;
|
|
begin
|
|
TWorkerThread.EnsureCreated();
|
|
InterlockedIncrement(WorkerThread.FRefCount);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean);
|
|
begin
|
|
if Assigned(WorkerThread) then
|
|
begin
|
|
if InterlockedDecrement(WorkerThread.FRefCount) = 0 then
|
|
begin
|
|
WorkerThread.Dispose(ACanBlock);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
constructor TWorkerThread.Create();
|
|
|
|
begin
|
|
inherited Create(False);
|
|
FreeOnTerminate := True;
|
|
FWaiterList := TThreadList.Create;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
destructor TWorkerThread.Destroy;
|
|
|
|
begin
|
|
// First let the ancestor stop the thread before freeing our resources.
|
|
inherited;
|
|
FWaiterList.Free;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TWorkerThread.WaitForValidationTermination(Tree: TBaseVirtualTree);
|
|
begin
|
|
// Wait for any references to this tree to be released.
|
|
while FCurrentTree = Tree do
|
|
begin
|
|
Sleep(1);
|
|
if (toVariableNodeHeight in TBaseVirtualTreeCracker(Tree).TreeOptions.MiscOptions) then
|
|
CheckSynchronize(); // We need to call CheckSynchronize here because we are using TThread.Synchronize in TBaseVirtualTree.MeasureItemHeight()
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TWorkerThread.Execute();
|
|
|
|
// Does some background tasks, like validating tree caches.
|
|
|
|
var
|
|
EnterStates: TVirtualTreeStates;
|
|
lCurrentTree: TBaseVirtualTree;
|
|
|
|
begin
|
|
TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread');
|
|
while not Terminated do
|
|
begin
|
|
WaitForSingleObject(WorkEvent, INFINITE);
|
|
if Terminated then
|
|
exit;
|
|
|
|
// Get the next waiting tree.
|
|
with FWaiterList.LockList do
|
|
try
|
|
if Count > 0 then
|
|
begin
|
|
lCurrentTree := Items[0];
|
|
// Remove this tree from waiter list.
|
|
Delete(0);
|
|
// If there is yet another tree to work on then set the work event to keep looping.
|
|
if Count > 0 then
|
|
SetEvent(WorkEvent);
|
|
end
|
|
else
|
|
lCurrentTree := nil;
|
|
finally
|
|
FWaiterList.UnlockList;
|
|
end;
|
|
|
|
// Something to do?
|
|
if Assigned(lCurrentTree) then
|
|
begin
|
|
try
|
|
TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]);
|
|
FCurrentTree := lCurrentTree;
|
|
EnterStates := [];
|
|
if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then
|
|
EnterStates := [tsUseCache];
|
|
|
|
finally
|
|
FCurrentTree := nil; //Clear variable to prevent deadlock in CancelValidation. See #434
|
|
TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]);
|
|
Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds);
|
|
end;
|
|
end;
|
|
end;//while
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
class procedure TWorkerThread.AddTree(Tree: TBaseVirtualTree);
|
|
|
|
begin
|
|
Assert(Assigned(Tree), 'Tree must not be nil.');
|
|
TWorkerThread.EnsureCreated();
|
|
|
|
// Remove validation stop flag, just in case it is still set.
|
|
TBaseVirtualTreeCracker(Tree).DoStateChange([], [tsStopValidation]);
|
|
with WorkerThread.FWaiterList.LockList do
|
|
try
|
|
if IndexOf(Tree) = -1 then
|
|
Add(Tree);
|
|
finally
|
|
WorkerThread.FWaiterList.UnlockList;
|
|
end;
|
|
|
|
SetEvent(WorkEvent);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
class procedure TWorkerThread.RemoveTree(Tree: TBaseVirtualTree);
|
|
|
|
begin
|
|
if not Assigned(WorkerThread) then
|
|
exit;
|
|
Assert(Assigned(Tree), 'Tree must not be nil.');
|
|
|
|
with WorkerThread.FWaiterList.LockList do
|
|
try
|
|
Remove(Tree);
|
|
finally
|
|
WorkerThread.FWaiterList.UnlockList; // Seen several AVs in this line, was called from TWorkerThrea.Destroy. Joachim Marder.
|
|
end;
|
|
WorkerThread.WaitForValidationTermination(Tree);
|
|
end;
|
|
|
|
|
|
end.
|