Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,60 @@
# TaskFlow
# TaskFlow

**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)

---

## Key Features

- **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.

---

## When Should You Use TaskFlow?

TaskFlow is ideal for scenarios where you need:

- **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.

---

## Getting Started

### Installation

Add the core package:
`dotnet add package TaskFlow`

For dependency injection support:
`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));
```
---

## Extensions

## License

This library is licensed under the [MIT License](LICENSE).
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
78 changes: 78 additions & 0 deletions TaskFlow/CurrentThreadTaskFlow.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,99 @@
namespace System.Threading.Tasks.Flow
{
/// <summary>
/// A task flow implementation that executes tasks on the thread that calls the <see cref="Run"/> method.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="CurrentThreadTaskFlow"/> class provides a task scheduling mechanism that
/// executes tasks on a specific thread provided by the caller. Unlike <see cref="DedicatedThreadTaskFlow"/>,
/// which creates its own thread, this class allows you to use an existing thread for task execution.
/// </para>
/// <para>
/// This implementation is useful when you want to execute tasks on a specific thread, such as:
/// </para>
/// <list type="bullet">
/// <item>A UI thread in a desktop application</item>
/// <item>A worker thread that you manage externally</item>
/// <item>A thread with specific properties or priority settings</item>
/// </list>
/// <para>
/// Note that the <see cref="Run"/> method must be called to start the task flow, and it
/// will block the calling thread until the task flow is disposed.
/// </para>
/// <example>
/// Basic usage with a background thread:
/// <code>
/// 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();
/// </code>
/// </example>
/// </remarks>
public sealed class CurrentThreadTaskFlow : ThreadTaskFlow
{
private int _managedThreadId;

/// <summary>
/// Initializes a new instance of the <see cref="CurrentThreadTaskFlow"/> class with default options.
/// </summary>
/// <remarks>
/// This constructor uses the default options from <see cref="TaskFlowOptions.Default"/>.
/// The task flow will not start processing tasks until the <see cref="Run"/> method is called.
/// </remarks>
public CurrentThreadTaskFlow()
: this(TaskFlowOptions.Default)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="CurrentThreadTaskFlow"/> class with the specified options.
/// </summary>
/// <param name="options">The options that configure the behavior of this task flow.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is null.</exception>
/// <remarks>
/// The task flow will not start processing tasks until the <see cref="Run"/> method is called.
/// </remarks>
public CurrentThreadTaskFlow(TaskFlowOptions options)
: base(options)
{
}

/// <summary>
/// Gets the managed thread ID of the thread that is executing tasks in this task flow.
/// </summary>
/// <remarks>
/// This property returns the thread ID of the thread that called the <see cref="Run"/> method.
/// If the <see cref="Run"/> method has not been called yet, the thread ID is not defined.
/// </remarks>
public override int ThreadId => _managedThreadId;

/// <summary>
/// Starts the task flow execution on the current thread and blocks until the task flow is disposed.
/// </summary>
/// <remarks>
/// <para>
/// 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.
/// </para>
/// <para>
/// 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.
/// </para>
/// <para>
/// To stop the execution, dispose the task flow from another thread or by using a cancellation token.
/// </para>
/// </remarks>
public void Run()
{
Starting();
Expand Down
68 changes: 68 additions & 0 deletions TaskFlow/DedicatedThreadTaskFlow.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,76 @@
namespace System.Threading.Tasks.Flow
{
/// <summary>
/// A task flow implementation that executes tasks on a dedicated background thread.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="DedicatedThreadTaskFlow"/> 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.
/// </para>
/// <para>
/// This implementation is useful when you want to:
/// </para>
/// <list type="bullet">
/// <item>Execute tasks in a background thread that doesn't block the main application thread</item>
/// <item>Process tasks sequentially in a dedicated thread</item>
/// <item>Isolate task execution from other parts of the application</item>
/// </list>
/// <para>
/// 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.
/// </para>
/// <example>
/// Basic usage:
/// <code>
/// // 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);
/// </code>
/// </example>
/// </remarks>
public sealed class DedicatedThreadTaskFlow : ThreadTaskFlow
{
private readonly Thread _thread;

/// <summary>
/// Initializes a new instance of the <see cref="DedicatedThreadTaskFlow"/> class with default options and an optional name.
/// </summary>
/// <param name="name">Optional name for the dedicated thread. If null or empty, uses the class name.</param>
/// <remarks>
/// <para>
/// This constructor uses the default options from <see cref="TaskFlowOptions.Default"/>.
/// </para>
/// <para>
/// The task flow immediately creates and starts a dedicated background thread for processing tasks.
/// </para>
/// </remarks>
public DedicatedThreadTaskFlow(string? name = default)
: this(TaskFlowOptions.Default, name)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="DedicatedThreadTaskFlow"/> class with the specified options and an optional name.
/// </summary>
/// <param name="options">The options that configure the behavior of this task flow.</param>
/// <param name="name">Optional name for the dedicated thread. If null or empty, uses the class name.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="options"/> is null.</exception>
/// <remarks>
/// <para>
/// The task flow immediately creates and starts a dedicated background thread for processing tasks.
/// </para>
/// <para>
/// The thread name is useful for debugging and thread identification in thread dumps or profiling tools.
/// </para>
/// </remarks>
public DedicatedThreadTaskFlow(TaskFlowOptions options, string? name = default)
: base(options)
{
Expand All @@ -22,6 +84,12 @@ public DedicatedThreadTaskFlow(TaskFlowOptions options, string? name = default)
_thread.Start(null);
}

/// <summary>
/// Gets the managed thread ID of the dedicated thread that is executing tasks in this task flow.
/// </summary>
/// <remarks>
/// This property returns the thread ID of the dedicated thread created for this task flow.
/// </remarks>
public override int ThreadId => _thread.ManagedThreadId;
}
}
38 changes: 38 additions & 0 deletions TaskFlow/ITaskFlow.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,45 @@
namespace System.Threading.Tasks.Flow
{
/// <summary>
/// Defines a unified interface for task flow execution that combines scheduling, information, and disposal capabilities.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="ITaskFlow"/> interface combines multiple interfaces to provide a complete API for task management:
/// </para>
/// <list type="bullet">
/// <item><see cref="ITaskScheduler"/> - For enqueueing tasks for execution</item>
/// <item><see cref="ITaskFlowInfo"/> - For accessing information about the task flow</item>
/// <item><see cref="IAsyncDisposable"/> - For asynchronous cleanup of resources</item>
/// <item><see cref="IDisposable"/> - For synchronous cleanup of resources</item>
/// </list>
/// <para>
/// Implementations of this interface are responsible for:
/// </para>
/// <list type="bullet">
/// <item>Managing the execution of tasks in a controlled manner</item>
/// <item>Ensuring proper task cancellation during disposal</item>
/// <item>Providing configuration options through the <see cref="ITaskFlowInfo.Options"/> property</item>
/// <item>Supporting both synchronous and asynchronous disposal patterns</item>
/// </list>
/// </remarks>
public interface ITaskFlow : ITaskScheduler, ITaskFlowInfo, IAsyncDisposable, IDisposable
{
/// <summary>
/// Synchronously disposes the task flow with a specified timeout.
/// </summary>
/// <param name="timeout">The maximum time to wait for task completion.</param>
/// <returns><c>true</c> if all tasks completed within the specified timeout; otherwise, <c>false</c>.</returns>
/// <remarks>
/// <para>
/// 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 <c>false</c>,
/// but resources are still properly disposed.
/// </para>
/// <para>
/// If tasks don't respond to cancellation, they may continue running after this method returns.
/// </para>
/// </remarks>
bool Dispose(TimeSpan timeout);
}
}
22 changes: 22 additions & 0 deletions TaskFlow/ITaskFlowInfo.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
namespace System.Threading.Tasks.Flow
{
/// <summary>
/// Defines a contract for accessing information about a task flow's configuration.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="ITaskFlowInfo"/> 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.
/// </para>
/// <para>
/// 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.
/// </para>
/// </remarks>
public interface ITaskFlowInfo
{
/// <summary>
/// Gets the options that configure the behavior of the task flow.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
TaskFlowOptions Options { get; }
}
}
Loading
Loading