From 6d0828ef97996792ade7830f17fb322b4b7b853b Mon Sep 17 00:00:00 2001 From: Volodymyr Dombrovskyi <5788605+dombrovsky@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:02:20 -0600 Subject: [PATCH 1/3] Add sourcedoc to core types --- ...kFlowServiceCollectionExtensionsFixture.cs | 2 +- ...kFlowServiceCollectionExtensionsFixture.cs | 2 +- TaskFlow/CurrentThreadTaskFlow.cs | 78 ++++++++ TaskFlow/DedicatedThreadTaskFlow.cs | 68 +++++++ TaskFlow/ITaskFlow.cs | 38 ++++ TaskFlow/ITaskFlowInfo.cs | 22 +++ TaskFlow/ITaskScheduler.cs | 46 +++++ TaskFlow/TaskFlow.cs | 104 ++++++++++ TaskFlow/TaskFlowBase.cs | 182 ++++++++++++++++++ TaskFlow/TaskFlowOptions.cs | 74 ++++++- TaskFlow/TaskFlowSynchronizationContext.cs | 87 +++++++++ TaskFlow/ThreadTaskFlow.cs | 134 +++++++++++++ 12 files changed, 833 insertions(+), 4 deletions(-) diff --git a/TaskFlow.Extensions.Microsoft.DependencyInjection.Tests/CustomTaskFlowServiceCollectionExtensionsFixture.cs b/TaskFlow.Extensions.Microsoft.DependencyInjection.Tests/CustomTaskFlowServiceCollectionExtensionsFixture.cs index 7bcc59a..44f4c74 100644 --- a/TaskFlow.Extensions.Microsoft.DependencyInjection.Tests/CustomTaskFlowServiceCollectionExtensionsFixture.cs +++ b/TaskFlow.Extensions.Microsoft.DependencyInjection.Tests/CustomTaskFlowServiceCollectionExtensionsFixture.cs @@ -6,7 +6,7 @@ namespace TaskFlow.Extensions.Microsoft.DependencyInjection.Tests using System.Threading.Tasks.Flow; [TestFixture] - public sealed class CustomTaskFlowServiceCollectionExtensionsFixture + internal sealed class CustomTaskFlowServiceCollectionExtensionsFixture { [Test] public void AddTaskFlow_ShouldRegisterTaskFlowInfoAndTaskScheduler() diff --git a/TaskFlow.Extensions.Microsoft.DependencyInjection.Tests/DefaultTaskFlowServiceCollectionExtensionsFixture.cs b/TaskFlow.Extensions.Microsoft.DependencyInjection.Tests/DefaultTaskFlowServiceCollectionExtensionsFixture.cs index bff277e..a06206f 100644 --- a/TaskFlow.Extensions.Microsoft.DependencyInjection.Tests/DefaultTaskFlowServiceCollectionExtensionsFixture.cs +++ b/TaskFlow.Extensions.Microsoft.DependencyInjection.Tests/DefaultTaskFlowServiceCollectionExtensionsFixture.cs @@ -5,7 +5,7 @@ namespace TaskFlow.Extensions.Microsoft.DependencyInjection.Tests using System.Threading.Tasks.Flow; [TestFixture] - public sealed class DefaultTaskFlowServiceCollectionExtensionsFixture + internal sealed class DefaultTaskFlowServiceCollectionExtensionsFixture { [Test] public void AddTaskFlow_ShouldRegisterTaskFlowInfoAndTaskScheduler() diff --git a/TaskFlow/CurrentThreadTaskFlow.cs b/TaskFlow/CurrentThreadTaskFlow.cs index 56f2079..3b6084e 100644 --- a/TaskFlow/CurrentThreadTaskFlow.cs +++ b/TaskFlow/CurrentThreadTaskFlow.cs @@ -1,21 +1,99 @@ namespace System.Threading.Tasks.Flow { + /// + /// A task flow implementation that executes tasks on the thread that calls the method. + /// + /// + /// + /// The class provides a task scheduling mechanism that + /// executes tasks on a specific thread provided by the caller. Unlike , + /// which creates its own thread, this class allows you to use an existing thread for task execution. + /// + /// + /// This implementation is useful when you want to execute tasks on a specific thread, such as: + /// + /// + /// A UI thread in a desktop application + /// A worker thread that you manage externally + /// A thread with specific properties or priority settings + /// + /// + /// Note that the method must be called to start the task flow, and it + /// will block the calling thread until the task flow is disposed. + /// + /// + /// Basic usage with a background thread: + /// + /// var taskFlow = new CurrentThreadTaskFlow(); + /// + /// // Start the task flow on a background thread + /// var thread = new Thread(() => taskFlow.Run()) { IsBackground = true }; + /// thread.Start(); + /// + /// // Enqueue tasks for execution on the background thread + /// var task1 = taskFlow.Enqueue(() => Console.WriteLine("Task 1")); + /// var task2 = taskFlow.Enqueue(() => Console.WriteLine("Task 2")); + /// + /// // Wait for tasks to complete and dispose the task flow + /// await Task.WhenAll(task1, task2); + /// await taskFlow.DisposeAsync(); + /// + /// + /// public sealed class CurrentThreadTaskFlow : ThreadTaskFlow { private int _managedThreadId; + /// + /// Initializes a new instance of the class with default options. + /// + /// + /// This constructor uses the default options from . + /// The task flow will not start processing tasks until the method is called. + /// public CurrentThreadTaskFlow() : this(TaskFlowOptions.Default) { } + /// + /// Initializes a new instance of the class with the specified options. + /// + /// The options that configure the behavior of this task flow. + /// Thrown when is null. + /// + /// The task flow will not start processing tasks until the method is called. + /// public CurrentThreadTaskFlow(TaskFlowOptions options) : base(options) { } + /// + /// Gets the managed thread ID of the thread that is executing tasks in this task flow. + /// + /// + /// This property returns the thread ID of the thread that called the method. + /// If the method has not been called yet, the thread ID is not defined. + /// public override int ThreadId => _managedThreadId; + /// + /// Starts the task flow execution on the current thread and blocks until the task flow is disposed. + /// + /// + /// + /// This method must be called to start the task flow and begin processing tasks. + /// It will block the calling thread until the task flow is disposed. + /// + /// + /// Tasks enqueued before calling this method will be processed once it is called. + /// Tasks enqueued after calling this method will be processed as they are received. + /// + /// + /// To stop the execution, dispose the task flow from another thread or by using a cancellation token. + /// + /// public void Run() { Starting(); diff --git a/TaskFlow/DedicatedThreadTaskFlow.cs b/TaskFlow/DedicatedThreadTaskFlow.cs index 1cff49a..8ab1805 100644 --- a/TaskFlow/DedicatedThreadTaskFlow.cs +++ b/TaskFlow/DedicatedThreadTaskFlow.cs @@ -1,14 +1,76 @@ namespace System.Threading.Tasks.Flow { + /// + /// A task flow implementation that executes tasks on a dedicated background thread. + /// + /// + /// + /// The class provides a task scheduling mechanism that + /// executes tasks on a dedicated background thread created specifically for this task flow. + /// The thread is automatically started during construction and continues running until the task flow is disposed. + /// + /// + /// This implementation is useful when you want to: + /// + /// + /// Execute tasks in a background thread that doesn't block the main application thread + /// Process tasks sequentially in a dedicated thread + /// Isolate task execution from other parts of the application + /// + /// + /// The dedicated thread is created as a background thread, which means it will not prevent + /// the application from exiting if all foreground threads have terminated. + /// + /// + /// Basic usage: + /// + /// // Create a task flow with a dedicated thread named "MyBackgroundThread" + /// using var taskFlow = new DedicatedThreadTaskFlow("MyBackgroundThread"); + /// + /// // Enqueue tasks for execution on the dedicated thread + /// var task1 = taskFlow.Enqueue(() => Console.WriteLine("Task 1")); + /// var task2 = taskFlow.Enqueue(() => Console.WriteLine("Task 2")); + /// + /// // Wait for tasks to complete + /// await Task.WhenAll(task1, task2); + /// + /// + /// public sealed class DedicatedThreadTaskFlow : ThreadTaskFlow { private readonly Thread _thread; + /// + /// Initializes a new instance of the class with default options and an optional name. + /// + /// Optional name for the dedicated thread. If null or empty, uses the class name. + /// + /// + /// This constructor uses the default options from . + /// + /// + /// The task flow immediately creates and starts a dedicated background thread for processing tasks. + /// + /// public DedicatedThreadTaskFlow(string? name = default) : this(TaskFlowOptions.Default, name) { } + /// + /// Initializes a new instance of the class with the specified options and an optional name. + /// + /// The options that configure the behavior of this task flow. + /// Optional name for the dedicated thread. If null or empty, uses the class name. + /// Thrown when is null. + /// + /// + /// The task flow immediately creates and starts a dedicated background thread for processing tasks. + /// + /// + /// The thread name is useful for debugging and thread identification in thread dumps or profiling tools. + /// + /// public DedicatedThreadTaskFlow(TaskFlowOptions options, string? name = default) : base(options) { @@ -22,6 +84,12 @@ public DedicatedThreadTaskFlow(TaskFlowOptions options, string? name = default) _thread.Start(null); } + /// + /// Gets the managed thread ID of the dedicated thread that is executing tasks in this task flow. + /// + /// + /// This property returns the thread ID of the dedicated thread created for this task flow. + /// public override int ThreadId => _thread.ManagedThreadId; } } \ No newline at end of file diff --git a/TaskFlow/ITaskFlow.cs b/TaskFlow/ITaskFlow.cs index 9fcbd63..bf98284 100644 --- a/TaskFlow/ITaskFlow.cs +++ b/TaskFlow/ITaskFlow.cs @@ -1,7 +1,45 @@ namespace System.Threading.Tasks.Flow { + /// + /// Defines a unified interface for task flow execution that combines scheduling, information, and disposal capabilities. + /// + /// + /// + /// The interface combines multiple interfaces to provide a complete API for task management: + /// + /// + /// - For enqueueing tasks for execution + /// - For accessing information about the task flow + /// - For asynchronous cleanup of resources + /// - For synchronous cleanup of resources + /// + /// + /// Implementations of this interface are responsible for: + /// + /// + /// Managing the execution of tasks in a controlled manner + /// Ensuring proper task cancellation during disposal + /// Providing configuration options through the property + /// Supporting both synchronous and asynchronous disposal patterns + /// + /// public interface ITaskFlow : ITaskScheduler, ITaskFlowInfo, IAsyncDisposable, IDisposable { + /// + /// Synchronously disposes the task flow with a specified timeout. + /// + /// The maximum time to wait for task completion. + /// true if all tasks completed within the specified timeout; otherwise, false. + /// + /// + /// This method attempts to dispose of the task flow asynchronously but with a time limit. + /// If the specified timeout is reached before all tasks complete, the method returns false, + /// but resources are still properly disposed. + /// + /// + /// If tasks don't respond to cancellation, they may continue running after this method returns. + /// + /// bool Dispose(TimeSpan timeout); } } \ No newline at end of file diff --git a/TaskFlow/ITaskFlowInfo.cs b/TaskFlow/ITaskFlowInfo.cs index 6fe0975..b284d64 100644 --- a/TaskFlow/ITaskFlowInfo.cs +++ b/TaskFlow/ITaskFlowInfo.cs @@ -1,7 +1,29 @@ namespace System.Threading.Tasks.Flow { + /// + /// Defines a contract for accessing information about a task flow's configuration. + /// + /// + /// + /// The interface provides a way to access configuration + /// information about a task flow without exposing the ability to schedule tasks or + /// manage the task flow's lifecycle. + /// + /// + /// This interface is useful for components that need to read task flow configuration + /// but should not have the ability to enqueue tasks or dispose the task flow. + /// + /// public interface ITaskFlowInfo { + /// + /// Gets the options that configure the behavior of the task flow. + /// + /// + /// The options object contains configuration settings that affect the behavior + /// of the task flow, such as the task scheduler to use and timeout settings + /// for synchronous disposal operations. + /// TaskFlowOptions Options { get; } } } \ No newline at end of file diff --git a/TaskFlow/ITaskScheduler.cs b/TaskFlow/ITaskScheduler.cs index 554c093..5df81c6 100644 --- a/TaskFlow/ITaskScheduler.cs +++ b/TaskFlow/ITaskScheduler.cs @@ -1,7 +1,53 @@ namespace System.Threading.Tasks.Flow { + /// + /// Defines a contract for scheduling tasks for execution in a controlled manner. + /// + /// + /// + /// The interface provides a standardized way to enqueue tasks + /// for execution according to the implementation's scheduling strategy. + /// + /// + /// Implementations may enforce different execution patterns such as: + /// + /// + /// Sequential execution (one task at a time) + /// Parallel execution with controlled concurrency + /// Priority-based scheduling + /// Custom execution strategies + /// + /// public interface ITaskScheduler { + /// + /// Enqueues a task function for execution according to the scheduler's strategy. + /// + /// The type of result produced by the task. + /// The function to execute that returns a . + /// An optional state object that is passed to the . + /// A cancellation token that can be used to cancel the operation. + /// A representing the result of the enqueued task. + /// + /// + /// The execution behavior of this method depends on the specific implementation of the scheduler. + /// Tasks may be executed immediately, queued for sequential execution, or handled according to + /// other scheduling strategies. + /// + /// + /// The returned task completes when the enqueued function completes, and will propagate any + /// exceptions thrown by the function. + /// + /// + /// Implementations should ensure that: + /// + /// + /// The provided cancellation token is honored + /// Exceptions from the task function are propagated to the returned task + /// Resources are properly managed when the scheduler is disposed + /// + /// + /// Thrown if the scheduler has been disposed. Task Enqueue(Func> taskFunc, object? state, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/TaskFlow/TaskFlow.cs b/TaskFlow/TaskFlow.cs index fb8ab45..4e59b22 100644 --- a/TaskFlow/TaskFlow.cs +++ b/TaskFlow/TaskFlow.cs @@ -3,15 +3,58 @@ namespace System.Threading.Tasks.Flow using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks.Flow.Annotations; + /// + /// Represents a task flow implementation that executes tasks sequentially in order. + /// + /// + /// + /// The class provides a task scheduling mechanism that guarantees + /// sequential execution of tasks in the order they are enqueued. Each task will only start + /// after the previous task has completed. + /// + /// + /// This implementation is useful when you need to ensure that tasks are executed in a strict + /// sequential order without concurrency. + /// + /// + /// Tasks are executed on the thread pool or using the custom + /// specified in the . + /// + /// + /// Basic usage: + /// + /// using var taskFlow = new TaskFlow(); + /// + /// // Enqueue multiple tasks + /// var task1 = taskFlow.Enqueue(() => Console.WriteLine("Task 1")); + /// var task2 = taskFlow.Enqueue(() => Console.WriteLine("Task 2")); + /// var task3 = taskFlow.Enqueue(() => Console.WriteLine("Task 3")); + /// + /// // Wait for all tasks to complete + /// await Task.WhenAll(task1, task2, task3); + /// + /// + /// public sealed partial class TaskFlow : TaskFlowBase { private Task _task; + /// + /// Initializes a new instance of the class with default options. + /// + /// + /// This constructor uses the default options from . + /// public TaskFlow() : this(TaskFlowOptions.Default) { } + /// + /// Initializes a new instance of the class with the specified options. + /// + /// The options that configure the behavior of this task flow. + /// Thrown when is null. public TaskFlow(TaskFlowOptions options) : base(options) { @@ -19,6 +62,37 @@ public TaskFlow(TaskFlowOptions options) Ready(); } + /// + /// Enqueues a task function for sequential execution after all previously enqueued tasks have completed. + /// + /// The type of result produced by the task. + /// The function to execute that returns a . + /// An optional state object that is passed to the . + /// A cancellation token that can be used to cancel the operation. + /// A representing the result of the enqueued task. + /// Thrown when is null. + /// Thrown if the task flow has been disposed. + /// + /// + /// This implementation ensures that tasks are executed sequentially in the order they are enqueued. + /// Each task will only start after the previous task has completed. + /// + /// + /// The task is scheduled using the specified in the . + /// + /// + /// Cancellation can occur in two ways: + /// + /// + /// Through the provided + /// Through the disposal of the instance + /// + /// + /// If a previous task fails with an exception, the exception is suppressed for the continuity + /// of the task flow, but the task's exception can still be observed by awaiting the task returned + /// from the original call. + /// + /// public override Task Enqueue(Func> taskFunc, object? state, CancellationToken cancellationToken) { Argument.NotNull(taskFunc); @@ -42,11 +116,27 @@ static void EmptyContinuationAction(Task obj) } } + /// + /// Returns a task that represents the initialization of this task flow. + /// + /// A that represents the initialization process. + /// + /// This implementation always returns a completed task since the + /// is immediately ready upon construction. + /// protected override Task GetInitializationTask() { return Task.CompletedTask; } + /// + /// Returns a task that represents the completion of all enqueued tasks. + /// + /// A that completes when all enqueued tasks have completed. + /// + /// This implementation returns the most recently scheduled task, which represents + /// the completion of all previous tasks due to the sequential execution model. + /// protected override Task GetCompletionTask() { lock (ThisLock) @@ -55,6 +145,20 @@ protected override Task GetCompletionTask() } } + /// + /// Executes the task function after the previous task has completed. + /// + /// The type of result produced by the task. + /// The function to execute that returns a . + /// An optional state object that is passed to the . + /// The previous task that must complete before this task can start. + /// A cancellation token that can be used to cancel the operation. + /// A representing the result of the task. + /// + /// This method ensures sequential execution by awaiting the completion of the previous task + /// before executing the current task function. Exceptions from the previous task are caught + /// and suppressed to ensure the continuity of the task flow. + /// [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Previous task exception should be observed on a task returned from Enqueue method")] private async Task RunAfterPrevious(Func> taskFunc, object? state, Task previousTask, CancellationToken cancellationToken) { diff --git a/TaskFlow/TaskFlowBase.cs b/TaskFlow/TaskFlowBase.cs index 68d555e..5976065 100644 --- a/TaskFlow/TaskFlowBase.cs +++ b/TaskFlow/TaskFlowBase.cs @@ -4,6 +4,27 @@ namespace System.Threading.Tasks.Flow using System.Threading.Tasks.Flow.Annotations; using System.Threading.Tasks.Flow.Internal; + /// + /// Base abstract class for implementing task flow execution mechanisms. + /// Provides a foundation for creating systems that schedule and execute tasks in a controlled manner. + /// + /// + /// + /// TaskFlowBase implements the core functionality required by the interface, + /// providing a standardized approach to task execution and lifecycle management. + /// + /// + /// Derived classes are responsible for implementing specific task scheduling strategies + /// through the abstract method. + /// + /// + /// The class handles proper disposal patterns, including: + /// - Cancellation of pending tasks during disposal + /// - Waiting for completion of running tasks + /// - Proper resource cleanup + /// - Thread-safe state management + /// + /// public abstract class TaskFlowBase : ITaskFlow { private readonly TaskFlowOptions _options; @@ -13,6 +34,11 @@ public abstract class TaskFlowBase : ITaskFlow private TaskFlowState _state; + /// + /// Initializes a new instance of the class with the specified options. + /// + /// The options that configure the behavior of this task flow. + /// Thrown when is null. protected TaskFlowBase(TaskFlowOptions options) { Argument.NotNull(options); @@ -23,12 +49,39 @@ protected TaskFlowBase(TaskFlowOptions options) _state = TaskFlowState.NotStarted; } + /// + /// Gets the options that configure the behavior of this task flow. + /// public TaskFlowOptions Options => _options; + /// + /// Gets the cancellation token that is triggered when the task flow is being disposed. + /// This token can be used to cancel pending operations during disposal. + /// protected CancellationToken CompletionToken => _disposeCancellationTokenSource.Token; + /// + /// Gets the synchronization object that should be used for thread-safety when accessing shared state. + /// protected object ThisLock => _lockObject; + /// + /// Asynchronously releases all resources used by this instance. + /// + /// + /// + /// This method: + /// 1. Waits for pending initialization if the task flow was starting + /// 2. Cancels all pending operations + /// 3. Waits for all running operations to complete + /// 4. Cleans up resources + /// + /// + /// If tasks are designed to honor cancellation tokens, they will be cancelled during disposal. + /// Otherwise, the disposal process will wait for their completion before finishing. + /// + /// + /// A representing the asynchronous disposal operation. public async ValueTask DisposeAsync() { await DisposeAsyncCore().ConfigureAwait(false); @@ -36,6 +89,13 @@ public async ValueTask DisposeAsync() GC.SuppressFinalize(this); } + /// + /// Synchronously releases all resources used by this instance. + /// + /// + /// This method uses the to limit + /// the maximum wait time for task completion during disposal. + /// [SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "GC.SuppressFinalize called in Dispose(TimeSpan)")] [SuppressMessage("Design", "CA1063:Implement IDisposable Correctly", Justification = "DisposeUnmanagedResources method")] public void Dispose() @@ -43,6 +103,21 @@ public void Dispose() Dispose(_options.SynchronousDisposeTimeout); } + /// + /// Synchronously releases all resources used by this instance with a specified timeout. + /// + /// The maximum time to wait for task completion. + /// true if all tasks completed within the specified timeout; otherwise, false. + /// + /// + /// This method attempts to dispose of the task flow asynchronously but with a time limit. + /// If the specified timeout is reached before all tasks complete, the method returns false, + /// but resources are still properly disposed. + /// + /// + /// If tasks don't respond to cancellation, they may continue running after this method returns. + /// + /// [SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "It is also Dispose")] public bool Dispose(TimeSpan timeout) { @@ -52,20 +127,70 @@ public bool Dispose(TimeSpan timeout) return result; } + /// + /// Enqueues a task function for execution according to the task flow's scheduling strategy. + /// + /// The type of result produced by the task. + /// The function to execute that returns a . + /// An optional state object that is passed to the . + /// A cancellation token that can be used to cancel the operation. + /// A representing the result of the enqueued task. + /// + /// + /// The implementation of this method in derived classes defines the specific scheduling behavior. + /// + /// + /// Important considerations for implementations: + /// - The method should be thread-safe + /// - It should check if the instance is disposed using before scheduling work + /// - Tasks should honor the provided cancellation token as well as + /// + /// + /// Thrown if the task flow has been disposed. public abstract Task Enqueue(Func> taskFunc, object? state, CancellationToken cancellationToken); + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// + /// true if the method is being called from or ; + /// false if it's being called from a finalizer. + /// protected virtual void Dispose(bool disposing) { } + /// + /// Called during the disposal process before waiting for task completion. + /// Derived classes can override this to perform custom actions before waiting for running tasks. + /// protected virtual void OnDisposeBeforeWaitForCompletion() { } + /// + /// Called during the disposal process after waiting for task completion. + /// Derived classes can override this to perform custom cleanup after all tasks have completed. + /// protected virtual void OnDisposeAfterWaitForCompletion() { } + /// + /// Core implementation of the asynchronous disposal pattern. + /// + /// A representing the asynchronous disposal operation. + /// + /// This method implements a robust disposal pattern that: + /// + /// Ensures thread-safety during disposal + /// Waits for initialization to complete if the task flow was starting + /// Sets the state to disposing to prevent new tasks from being enqueued + /// Cancels all pending operations + /// Waits for all running operations to complete + /// Performs proper resource cleanup + /// + /// [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Should be observed on a tasks returned from Enqueue method")] protected virtual async ValueTask DisposeAsyncCore() { @@ -113,6 +238,14 @@ protected virtual async ValueTask DisposeAsyncCore() } } + /// + /// Sets the state of the task flow to . + /// + /// + /// This method should be called by derived classes when the task flow is fully initialized + /// and ready to accept and process tasks. It transitions the task flow from the + /// or state to the state. + /// protected void Ready() { lock (_lockObject) @@ -121,6 +254,14 @@ protected void Ready() } } + /// + /// Sets the state of the task flow to . + /// + /// + /// This method should be called by derived classes when the task flow has begun its initialization + /// process but is not yet ready to process tasks. It transitions the task flow from the + /// state to the state. + /// protected void Starting() { lock (_lockObject) @@ -129,6 +270,11 @@ protected void Starting() } } + /// + /// Checks if the task flow has been disposed and throws an if it has. + /// + /// Thrown if the task flow is in the + /// or state. protected void CheckDisposed() { #if NET7_0_OR_GREATER @@ -141,20 +287,56 @@ protected void CheckDisposed() #endif } + /// + /// Returns a task that represents the initialization of this task flow. + /// + /// A that represents the initialization process. + /// + /// This method is called during disposal to ensure that initialization completes before + /// waiting for running tasks to complete. Derived classes should return a task that completes + /// when the task flow is fully initialized or is ready to safely handle disposal. + /// protected abstract Task GetInitializationTask(); + /// + /// Returns a task that represents the completion of all enqueued tasks. + /// + /// A that completes when all enqueued tasks have completed. + /// + /// This method is called during disposal to wait for all currently executing and enqueued + /// tasks to complete. Derived classes should implement this to return a task that completes + /// only when all work items have been processed. + /// protected abstract Task GetCompletionTask(); + /// + /// Represents the possible states of a task flow instance. + /// protected enum TaskFlowState { + /// + /// The task flow has been created but not yet started. + /// NotStarted = 0, + /// + /// The task flow has begun initialization but is not fully ready. + /// Starting, + /// + /// The task flow is initialized and ready to accept and process tasks. + /// Running, + /// + /// The task flow is in the process of shutting down and disposing resources. + /// Disposing, + /// + /// The task flow has been fully disposed and can no longer be used. + /// Disposed, } } diff --git a/TaskFlow/TaskFlowOptions.cs b/TaskFlow/TaskFlowOptions.cs index 5f46f23..18ba9eb 100644 --- a/TaskFlow/TaskFlowOptions.cs +++ b/TaskFlow/TaskFlowOptions.cs @@ -1,17 +1,87 @@ namespace System.Threading.Tasks.Flow { + /// + /// Represents configuration options for task flow instances. + /// + /// + /// + /// TaskFlowOptions is an immutable record that provides configuration settings for + /// controlling the behavior of task flow implementations. + /// + /// + /// A default instance with sensible default values is available via the property. + /// Custom instances can be created using object initializer syntax with the init-only properties. + /// + /// + /// Creating custom options: + /// + /// var options = new TaskFlowOptions + /// { + /// TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(), + /// SynchronousDisposeTimeout = TimeSpan.FromSeconds(5) + /// }; + /// + /// var taskFlow = new TaskFlow(options); + /// + /// + /// public sealed record TaskFlowOptions { + /// + /// Gets or sets the default options used when no specific options are provided. + /// + /// + /// + /// The default instance has the following settings: + /// + /// + /// set to + /// set to + /// + /// + /// This property can be modified to change the default settings used throughout the application. + /// + /// public static TaskFlowOptions Default { get; set; } = new TaskFlowOptions(); + /// + /// Gets the task scheduler that will be used to schedule task execution. + /// + /// + /// + /// The task scheduler determines how and where tasks are executed. + /// + /// + /// Common options include: + /// + /// + /// - Uses the .NET thread pool (default) + /// - Executes tasks on the specific context + /// + /// public TaskScheduler TaskScheduler { get; init; } = TaskScheduler.Default; /// /// Gets maximum time to wait on synchronous call. - /// Default value is . /// /// - /// Does not affect asynchronous call. + /// + /// This timeout is only used for synchronous disposal operations through + /// or . + /// + /// + /// When a task flow is disposed synchronously, this value determines how long to wait for + /// all pending tasks to complete before returning from the disposal operation. + /// + /// + /// The default value is , which means the disposal + /// will wait indefinitely for tasks to complete. + /// + /// + /// This setting does not affect asynchronous disposal through , + + /// which always waits for full completion. + /// /// public TimeSpan SynchronousDisposeTimeout { get; init; } = Timeout.InfiniteTimeSpan; } diff --git a/TaskFlow/TaskFlowSynchronizationContext.cs b/TaskFlow/TaskFlowSynchronizationContext.cs index afdc512..8251014 100644 --- a/TaskFlow/TaskFlowSynchronizationContext.cs +++ b/TaskFlow/TaskFlowSynchronizationContext.cs @@ -3,10 +3,57 @@ namespace System.Threading.Tasks.Flow using System.Threading.Tasks.Flow.Annotations; using System.Threading.Tasks.Flow.Internal; + /// + /// Provides a implementation that executes callbacks using a . + /// + /// + /// + /// The class enables integration between the .NET synchronization context + /// model and the TaskFlow task scheduling system. This allows code that expects to run on a specific + /// synchronization context to execute using a task scheduler. + /// + /// + /// This is particularly useful for: + /// + /// + /// Supporting async/await operations that need to return to a specific context + /// Enabling TaskFlow to be used with code that relies on synchronization contexts + /// Creating a sequential execution environment for asynchronous operations + /// + /// + /// Using TaskFlowSynchronizationContext to execute code on a TaskFlow: + /// + /// using var taskFlow = new TaskFlow(); + /// var originalContext = SynchronizationContext.Current; + /// + /// try + /// { + /// // Set the TaskFlow as the current synchronization context + /// SynchronizationContext.SetSynchronizationContext( + /// TaskFlowSynchronizationContext.For(taskFlow)); + /// + /// // Async operations will now return to the TaskFlow context + /// await DoSomethingAsync(); + /// + /// // Code here runs on the TaskFlow + /// } + /// finally + /// { + /// // Restore the original context + /// SynchronizationContext.SetSynchronizationContext(originalContext); + /// } + /// + /// + /// public sealed class TaskFlowSynchronizationContext : SynchronizationContext { private readonly ITaskScheduler _taskScheduler; + /// + /// Initializes a new instance of the class with the specified task scheduler. + /// + /// The task scheduler to use for executing callbacks. + /// Thrown when is null. public TaskFlowSynchronizationContext(ITaskScheduler taskScheduler) { Argument.NotNull(taskScheduler); @@ -14,11 +61,36 @@ public TaskFlowSynchronizationContext(ITaskScheduler taskScheduler) _taskScheduler = taskScheduler; } + /// + /// Creates a new that executes callbacks using the specified task scheduler. + /// + /// The task scheduler to use for executing callbacks. + /// A that uses the specified task scheduler. + /// Thrown when is null. + /// + /// This is a convenience factory method for creating a new + /// instance that returns it as a base type. + /// public static SynchronizationContext For(ITaskScheduler taskScheduler) { return new TaskFlowSynchronizationContext(taskScheduler); } + /// + /// Synchronously executes the specified delegate on the task scheduler. + /// + /// The delegate to execute. + /// The state object to pass to the delegate. + /// Thrown when is null. + /// + /// + /// This method blocks the calling thread until the delegate completes execution. + /// + /// + /// The delegate is executed using the task scheduler associated with this context, + /// which ensures proper integration with the TaskFlow execution model. + /// + /// public override void Send(SendOrPostCallback d, object? state) { Argument.NotNull(d); @@ -26,6 +98,21 @@ public override void Send(SendOrPostCallback d, object? state) _taskScheduler.Enqueue(() => d(state)).Await(); } + /// + /// Asynchronously executes the specified delegate on the task scheduler. + /// + /// The delegate to execute. + /// The state object to pass to the delegate. + /// Thrown when is null. + /// + /// + /// This method does not block the calling thread and returns immediately. + /// + /// + /// The delegate is queued for execution using the task scheduler associated with this context, + /// which ensures proper integration with the TaskFlow execution model. + /// + /// public override void Post(SendOrPostCallback d, object? state) { Argument.NotNull(d); diff --git a/TaskFlow/ThreadTaskFlow.cs b/TaskFlow/ThreadTaskFlow.cs index 98770e0..b270411 100644 --- a/TaskFlow/ThreadTaskFlow.cs +++ b/TaskFlow/ThreadTaskFlow.cs @@ -6,6 +6,25 @@ namespace System.Threading.Tasks.Flow using System.Threading.Tasks.Flow.Annotations; using System.Threading.Tasks.Flow.Internal; + /// + /// Provides a base implementation for task flows that execute tasks on a dedicated thread. + /// + /// + /// + /// The class serves as a base for task flow implementations that + /// execute tasks on a specific thread. It handles the core mechanics of: + /// + /// + /// Enqueueing tasks for execution + /// Managing task execution on a designated thread + /// Handling synchronization context switching + /// Proper cleanup and cancellation during disposal + /// + /// + /// Derived classes must specify how the execution thread is obtained and managed by + /// implementing the property and initializing the thread execution. + /// + /// public abstract class ThreadTaskFlow : TaskFlowBase { [SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "OnDisposeAfterWaitForCompletion")] @@ -14,6 +33,16 @@ public abstract class ThreadTaskFlow : TaskFlowBase private readonly TaskCompletionSource _threadCompletionTaskSource; private readonly TaskFlowSynchronizationContext _synchronizationContext; + /// + /// Initializes a new instance of the class with the specified options. + /// + /// The options that configure the behavior of this task flow. + /// Thrown when is null. + /// + /// The constructor initializes the internal infrastructure for task execution, + /// but derived classes are responsible for starting the execution thread + /// and calling the method. + /// protected ThreadTaskFlow(TaskFlowOptions options) : base(options) { @@ -23,8 +52,36 @@ protected ThreadTaskFlow(TaskFlowOptions options) _synchronizationContext = new TaskFlowSynchronizationContext(this); } + /// + /// Gets the managed thread ID of the thread used for task execution. + /// + /// + /// This property should return the thread ID of the thread that is executing + /// the tasks enqueued in this task flow. The implementation depends on how + /// the execution thread is created or obtained in the derived class. + /// public abstract int ThreadId { get; } + /// + /// Enqueues a task function for execution on the designated thread. + /// + /// The type of result produced by the task. + /// The function to execute that returns a . + /// An optional state object that is passed to the . + /// A cancellation token that can be used to cancel the operation. + /// A representing the result of the enqueued task. + /// Thrown when is null. + /// Thrown if the task flow has been disposed. + /// + /// + /// This implementation adds the task to a blocking collection that is processed by the execution thread. + /// The task will be executed in the order it was enqueued, on the thread specified by . + /// + /// + /// The task function will execute with a synchronization context that ensures that any asynchronous + /// continuations within the task function will also be marshaled back to the same thread. + /// + /// public override async Task Enqueue(Func> taskFunc, object? state, CancellationToken cancellationToken) { Argument.NotNull(taskFunc); @@ -45,26 +102,63 @@ public override async Task Enqueue(Func + /// Called during the disposal process before waiting for task completion. + /// Marks the blocking collection as complete for adding, which signals the execution thread to finish. + /// protected override void OnDisposeBeforeWaitForCompletion() { _blockingCollection.CompleteAdding(); } + /// + /// Called during the disposal process after waiting for task completion. + /// Disposes the blocking collection used for task execution. + /// protected override void OnDisposeAfterWaitForCompletion() { _blockingCollection.Dispose(); } + /// + /// Returns a task that represents the initialization of this task flow. + /// + /// A that completes when the execution thread has started. + /// + /// This task completes when the execution thread has started and is ready to process tasks. + /// protected override Task GetInitializationTask() { return _threadStartTaskSource.Task; } + /// + /// Returns a task that represents the completion of all enqueued tasks. + /// + /// A that completes when the execution thread has terminated. + /// + /// This task completes when the execution thread has processed all tasks and terminated. + /// protected override Task GetCompletionTask() { return _threadCompletionTaskSource.Task; } + /// + /// Starts the execution thread that processes tasks from the queue. + /// + /// An unused parameter (provided for compatibility with thread start delegates). + /// + /// + /// This method should be called by derived classes to start the task processing loop. + /// It sets the task flow state to and begins + /// processing tasks from the queue. + /// + /// + /// The method will continue processing tasks until cancellation is requested or + /// the task flow is disposed. + /// + /// protected void ThreadStart(object? _) { Ready(); @@ -97,6 +191,13 @@ protected void ThreadStart(object? _) } } + /// + /// Represents a task that has been enqueued for execution. + /// + /// + /// This class encapsulates the task function, state, and completion source for an enqueued task. + /// It handles the execution of the task and propagation of results and exceptions. + /// private sealed class ExecutionItem { private readonly Func> _taskFunc; @@ -105,6 +206,13 @@ private sealed class ExecutionItem private readonly CancellationToken _cancellationToken; private readonly TaskCompletionSource _taskCompletionSource; + /// + /// Initializes a new instance of the class. + /// + /// The function to execute. + /// The state object to pass to the function. + /// The synchronization context to use for continuations. + /// A cancellation token that can be used to cancel the operation. public ExecutionItem( Func> taskFunc, object? state, @@ -118,12 +226,21 @@ public ExecutionItem( _taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } + /// + /// Gets a task that represents the completion of this execution item with the expected result type. + /// + /// The expected result type. + /// A task that completes with the result of the execution item. public async Task GetTypedTask() { var result = await _taskCompletionSource.Task.ConfigureAwait(false); return (T)result!; } + /// + /// Executes the task function and handles the result. + /// + /// A cancellation token that can be used to cancel the operation. [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception propagated to TaskCompletionSource")] [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "linkedCts disposed after awaiter is done")] public void Execute(CancellationToken cancellationToken) @@ -181,17 +298,29 @@ void HandleAwaiterCompletion(TaskAwaiter awaiter) } } + /// + /// Cancels the execution of this item. + /// + /// The cancellation token that caused the cancellation. public void Cancel(CancellationToken token) { _taskCompletionSource.TrySetCanceled(token); } } + /// + /// A synchronization context that uses a blocking collection to marshal calls to a specific thread. + /// private sealed class BlockingCollectionSynchronizationContext : SynchronizationContext { private readonly BlockingCollection<(SendOrPostCallback Callback, object? State)> _blockingCollection; private readonly SynchronizationContext? _fallbackSynchronizationContext; + /// + /// Initializes a new instance of the class. + /// + /// The blocking collection to use for marshaling calls. + /// A fallback synchronization context to use if the blocking collection is completed. public BlockingCollectionSynchronizationContext( BlockingCollection<(SendOrPostCallback Callback, object? State)> blockingCollection, SynchronizationContext? fallbackSynchronizationContext) @@ -200,6 +329,11 @@ public BlockingCollectionSynchronizationContext( _fallbackSynchronizationContext = fallbackSynchronizationContext; } + /// + /// Dispatches an asynchronous message to the synchronization context. + /// + /// The delegate to call. + /// The object passed to the delegate. public override void Post(SendOrPostCallback d, object? state) { if (_blockingCollection.IsAddingCompleted) From 030ead42a4e95ccda4b3b086eb93fc3e9ea71154 Mon Sep 17 00:00:00 2001 From: Volodymyr Dombrovskyi <5788605+dombrovsky@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:02:36 -0600 Subject: [PATCH 2/3] readme.md draft --- README.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fca080..65a85fe 100644 --- a/README.md +++ b/README.md @@ -1 +1,82 @@ -# TaskFlow \ No newline at end of file +# TaskFlow + +**TaskFlow** is a high-performance, sequential task orchestration library for .NET. It provides thread-safe, controlled execution of asynchronous tasks with robust resource management, making it ideal for scenarios requiring serialized task execution, thread affinity, and clean disposal patterns. + +[![NuGet](https://img.shields.io/nuget/v/TaskFlow.svg)](https://www.nuget.org/packages/TaskFlow/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +--- + +## Why TaskFlow? + +TaskFlow is designed to address common challenges in asynchronous programming, such as: + +- **Serialized Task Execution:** Ensures tasks are executed in the exact order they are enqueued. +- **Thread Affinity:** Supports execution on a dedicated thread, the current thread, or the thread pool. +- **Robust Disposal:** Guarantees all enqueued tasks complete before disposal finishes. +- **Cancellation Support:** Tasks are executed even when canceled, ensuring predictable execution order. +- **Synchronization Context Awareness:** Async/await inside enqueued delegates can capture and execute on the same `SynchronizationContext`. +- **Error Handling:** Provides rich error handling capabilities with custom exception handlers. +- **Dependency Injection Integration:** Seamlessly integrates with Microsoft.Extensions.DependencyInjection. + +--- + +## Key Features + +### 1. Sequential Task Execution +- Tasks are executed in the order they are enqueued, with no concurrency unless explicitly configured. +- Ideal for scenarios like database access, file operations, or API rate limiting. + +### 2. Thread Affinity +- Run tasks on a dedicated thread, the current thread, or the thread pool. +- Useful for UI thread orchestration or isolating task execution. + +### 3. Robust Resource Management +- `Dispose`/`DisposeAsync` ensures all tasks complete before releasing resources. +- Configurable timeouts for synchronous disposal. + +### 4. Cancellation and Error Handling +- Tasks respect `CancellationToken` for cooperative cancellation. +- Register custom exception handlers for specific exception types or conditions. + +### 5. Extensibility +- Extend `TaskFlowBase` to create custom task flow implementations. +- Use extensions like `TaskFlow.Extensions.Time` for throttling and scheduling. + +--- + +## Getting Started + +### Installation + +Add the core package: +dotnet add package TaskFlow +For dependency injection support: +dotnet add package TaskFlow.Microsoft.Extensions.DependencyInjection +For time-based extensions (e.g., throttling): +dotnet add package TaskFlow.Extensions.Time +### Basic Usage +using var taskFlow = new TaskFlow(); + +// Enqueue tasks for sequential execution +var task1 = taskFlow.Enqueue(() => Console.WriteLine("Task 1")); +var task2 = taskFlow.Enqueue(async () => await Task.Delay(100)); + +await Task.WhenAll(task1, task2); +--- + +## Extensions + +### Microsoft.Extensions.DependencyInjection + +Integrate TaskFlow with your DI container: +services.AddTaskFlow(); +### TaskFlow.Extensions.Time + +Add throttling and scheduling capabilities: +var throttledScheduler = taskScheduler.Throttle(5, TimeSpan.FromSeconds(1)); +--- + +## License + +This library is licensed under the [MIT License](LICENSE). \ No newline at end of file From 5aede129d5f3421aa78cf6ed06702c1bedde7c18 Mon Sep 17 00:00:00 2001 From: Volodymyr Dombrovskyi <5788605+dombrovsky@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:29:03 -0600 Subject: [PATCH 3/3] readme.md draft --- README.md | 70 +++++++++++++++++++------------------------------------ 1 file changed, 24 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 65a85fe..b5df81d 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,36 @@ # TaskFlow -**TaskFlow** is a high-performance, sequential task orchestration library for .NET. It provides thread-safe, controlled execution of asynchronous tasks with robust resource management, making it ideal for scenarios requiring serialized task execution, thread affinity, and clean disposal patterns. +**TaskFlow** is a robust, high-performance, extensible, and thread-safe library for orchestrating and controlling the execution of asynchronous tasks in .NET. It provides advanced patterns for sequential task execution, resource management, and cancellation, making it ideal for scenarios where you need more than just `SemaphoreSlim` or basic Task chaining. [![NuGet](https://img.shields.io/nuget/v/TaskFlow.svg)](https://www.nuget.org/packages/TaskFlow/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) --- -## Why TaskFlow? - -TaskFlow is designed to address common challenges in asynchronous programming, such as: - -- **Serialized Task Execution:** Ensures tasks are executed in the exact order they are enqueued. -- **Thread Affinity:** Supports execution on a dedicated thread, the current thread, or the thread pool. -- **Robust Disposal:** Guarantees all enqueued tasks complete before disposal finishes. -- **Cancellation Support:** Tasks are executed even when canceled, ensuring predictable execution order. -- **Synchronization Context Awareness:** Async/await inside enqueued delegates can capture and execute on the same `SynchronizationContext`. -- **Error Handling:** Provides rich error handling capabilities with custom exception handlers. -- **Dependency Injection Integration:** Seamlessly integrates with Microsoft.Extensions.DependencyInjection. - ---- - ## Key Features -### 1. Sequential Task Execution -- Tasks are executed in the order they are enqueued, with no concurrency unless explicitly configured. -- Ideal for scenarios like database access, file operations, or API rate limiting. +- **Sequential Task Execution:** Guarantee that tasks are executed in the order they are enqueued, with no concurrency unless explicitly configured. +- **Thread Affinity:** Run tasks on a dedicated thread, the current thread, or the thread pool, with full control over execution context. +- **Robust Disposal:** Dispose/DisposeAsync will only complete after all enqueued tasks have finished, ensuring clean shutdowns. This makes it ideal for managing fire-and-forget tasks by binding their lifetime to a specific scope. +- **Cancellation Support:** All enqueued task functions are executed, even if canceled before execution, ensuring predictable execution order. +- **SynchronizationContext Awareness:** Async/await inside enqueued delegates will execute continuations on the same `TaskFlow` if a `SynchronizationContext` is captured. +- **Extensibility:** Extend `TaskFlowBase` to create custom task flow implementations or use extension methods and wrappers to enhance functionality, such as throttling, error handling, or scoped cancellation. +- **Clean Task Pipeline Definition:** Define task pipelines separately from execution logic using extension methods from `System.Threading.Tasks.Flow.Extensions`, enabling better segregation of responsibilities and cleaner code. +- **Dependency Injection Integration:** Extensions for `Microsoft.Extensions.DependencyInjection` for easy registration and scoping. -### 2. Thread Affinity -- Run tasks on a dedicated thread, the current thread, or the thread pool. -- Useful for UI thread orchestration or isolating task execution. +--- -### 3. Robust Resource Management -- `Dispose`/`DisposeAsync` ensures all tasks complete before releasing resources. -- Configurable timeouts for synchronous disposal. +## When Should You Use TaskFlow? -### 4. Cancellation and Error Handling -- Tasks respect `CancellationToken` for cooperative cancellation. -- Register custom exception handlers for specific exception types or conditions. +TaskFlow is ideal for scenarios where you need: -### 5. Extensibility -- Extend `TaskFlowBase` to create custom task flow implementations. -- Use extensions like `TaskFlow.Extensions.Time` for throttling and scheduling. +- **Serialized access to a resource** (e.g., database, file, hardware) from multiple async operations. +- **Order-preserving task execution** (e.g., message processing, event handling). +- **Thread affinity** (e.g., UI thread, dedicated worker thread). +- **Graceful shutdown** with guaranteed completion of all in-flight work. +- **Advanced error handling and cancellation patterns.** +- **Fire-and-forget task lifetime management:** Bind fire-and-forget operations to a scope by disposing the `TaskFlow` instance, ensuring proper cleanup and resource management. +- **Segregation of responsibilities:** Use extension methods to define task pipelines separately from execution logic, improving maintainability and readability. --- @@ -50,33 +39,22 @@ TaskFlow is designed to address common challenges in asynchronous programming, s ### Installation Add the core package: -dotnet add package TaskFlow +`dotnet add package TaskFlow` + For dependency injection support: -dotnet add package TaskFlow.Microsoft.Extensions.DependencyInjection -For time-based extensions (e.g., throttling): -dotnet add package TaskFlow.Extensions.Time +`dotnet add package TaskFlow.Microsoft.Extensions.DependencyInjection` ### Basic Usage +```csharp using var taskFlow = new TaskFlow(); // Enqueue tasks for sequential execution var task1 = taskFlow.Enqueue(() => Console.WriteLine("Task 1")); var task2 = taskFlow.Enqueue(async () => await Task.Delay(100)); - -await Task.WhenAll(task1, task2); +``` --- ## Extensions -### Microsoft.Extensions.DependencyInjection - -Integrate TaskFlow with your DI container: -services.AddTaskFlow(); -### TaskFlow.Extensions.Time - -Add throttling and scheduling capabilities: -var throttledScheduler = taskScheduler.Throttle(5, TimeSpan.FromSeconds(1)); ---- - ## License This library is licensed under the [MIT License](LICENSE). \ No newline at end of file