TaskManager refactoring

This commit is contained in:
Michael Schimmel
2025-06-25 15:00:51 +02:00
parent e2a262bc5a
commit 10b653e16f
13 changed files with 183 additions and 255 deletions
+16 -48
View File
@@ -11,37 +11,22 @@ uses
Myc.Signals;
type
IMycThreadPool = interface(TTaskManager.IMycTaskManager)
IThreadPool = interface(TTaskManager.ITaskManager)
{$region 'property access'}
// Retrieves the number of worker threads.
function GetThreadCount: Integer;
{$endregion}
// Creates and starts a new, independent thread executing Proc.
// Returns IMycState to await thread completion.
// Exceptions within Proc are caught; the first one is stored
// and can be re-raised in the main thread via WaitFor or Teardown.
function CreateThread(const Proc: TProc): TState.IState;
// Returns True if called from the main application thread.
function InMainThread: Boolean;
// Returns True if called from one of the factory's worker threads.
function InWorkerThread: Boolean;
// Schedules Job for execution by a worker thread.
// If StartCount > 0, Job is deferred until Notify is called StartCount times on the returned IMycSubscriber.
// If StartCount <= 0, Job is enqueued immediately.
// Returns IMycSubscriber for deferred jobs, or TMycLatch.Null for immediate jobs or if Job is nil.
// Exceptions during Job execution are caught; the first one is stored
// and can be re-raised in the main thread via WaitFor or Teardown.
// Raises ETaskException if the factory is already terminated.
function Run(Job: TProc): TSignal.ISubscriber;
procedure EnqueueJob(const Job: TProc);
// Shuts down the task factory: terminates worker threads and cleans up resources.
// Before shutdown, any first stored exception from a worker thread (from Run or CreateThread tasks managed by this factory)
// Before shutdown, any first stored exception from a worker thread (from CreateThread tasks managed by this factory)
// will be re-raised in the calling (main) thread.
procedure Teardown;
@@ -49,7 +34,7 @@ type
property ThreadCount: Integer read GetThreadCount;
end;
TMycTaskFactory = class(TInterfacedObject, TTaskManager.IMycTaskManager, IMycThreadPool)
TMycTaskFactory = class(TInterfacedObject, TTaskManager.ITaskManager, IThreadPool)
type
ETaskException = class(Exception)
end;
@@ -75,10 +60,9 @@ type
constructor Create;
destructor Destroy; override;
class function CreateAnonymousThread(const DbgName: String; const Proc: TProc): TThread;
function CreateThread(const Proc: TProc): TState.IState;
procedure CreateThread(const Proc: TProc);
procedure HandleException;
procedure EnqueueJob(const Job: TProc);
function Run(Job: TProc): TSignal.ISubscriber;
procedure RunTask(const Gate: TState; const Proc: TProc);
procedure WaitFor(const State: TState);
procedure Teardown;
@@ -188,41 +172,37 @@ end;
procedure TMycTaskFactory.RunTask(const Gate: TState; const Proc: TProc);
begin
if not Assigned(Proc) then
exit;
if FTerminated <> 0 then
raise ETaskException.Create('Task factory terminated');
if Gate.IsSet then
begin
EnqueueJob(Proc);
end
else
begin
// The job will run when Gate notifies the subscriber returned by Run.
Gate.Signal.Subscribe(Run(Proc));
// Create a pending job
Gate.Signal.Subscribe(TMycPendingJob.Create(Self, Proc));
end;
end;
function TMycTaskFactory.CreateThread(const Proc: TProc): TState.IState;
var
res: TLatch.ILatch;
capturedProc: TProc;
procedure TMycTaskFactory.CreateThread(const Proc: TProc);
begin
// Create a latch that will be signaled when the proc finishes
res := TLatch.CreateLatch(1);
capturedProc := Proc; // Capture Proc for the anonymous method
var cProc := Proc; // Capture Proc for the anonymous method
CreateAnonymousThread(
'Thread',
procedure
begin
try
capturedProc(); // Execute the provided procedure
cProc(); // Execute the provided procedure
finally
capturedProc := nil; // Clear the captured proc
res.Notify; // Signal completion via the latch's Notify method
cProc := nil; // Clear the captured proc
end;
end)
.Start;
// Return the state interface of the latch
exit(res.State);
end;
procedure TMycTaskFactory.EnqueueJob(const Job: TProc);
@@ -287,18 +267,6 @@ begin
exit(false); // Not found in worker thread list
end;
function TMycTaskFactory.Run(Job: TProc): TSignal.ISubscriber;
begin
if FTerminated <> 0 then
raise ETaskException.Create('Task factory terminated');
if not Assigned(Job) then
exit(TLatch.Null); // Already correct, uses direct static property on TMycLatch
// Create a pending job
Result := TMycPendingJob.Create(Self, Job);
end;
procedure TMycTaskFactory.Teardown;
var
i: Integer;