Repository navigation
Open
Labels
concurrencyParallel / batch / async executionParallel / batch / async executionenhancementNew feature or requestNew feature or requestextensionExtension behaviors / helpersExtension behaviors / helpers
Description
Summary
Add ExecuteAllInContext — an IContextManager extension that runs a list of async delegates inside one logical context scope, with per-task fork-join isolation and correct ExecutionContext/AsyncLocal flow.
Goal
Provide a safe, first-party replacement for a hand-rolled array of tasks + Task.WhenAll, so context metadata (trace/tenant/correlation) never leaks or is lost across parallel branches.
Problem
Bare Task.WhenAll does not guarantee that tasks launched in a loop keep the library's ambient context, especially under SynchronizationContext configurations. The current ContextManager only executes single actions, so there is no library-level fork-join primitive.
Scope
-
IContextManager<C>.ExecuteAllInContext(C ctx, params Func<Task>[] work)extension. - Per-delegate snapshotting: each task starts from its own clone of the entering context (Isolation by default, No Ambient Write).
- Launches each delegate through
manager.ExecuteInContext(snapshot, work)with a correctExecutionContext. - Waits via
Task.WhenAll; task scopes disposed deterministically when the batch completes. - Emit
ContextLifecycleEventKind.Started/Completedaround each branch (per [Context]: add ActivityObserver for tracing context execution #11 event contract).
Internal Loop Reference
var tasks = delegates.Select(work => Task.Run(async () =>
{
using var scope = manager.BeginContext(snapshot);
try { await work(); }
catch (Exception ex) { Log(ex, snapshot.CorrelationId); }
}));
await Task.WhenAll(tasks);Design Expectations
- Isolation by default: child mutations never leak to siblings or the parent thread.
- Deterministic lifetime tied to
Task.WhenAllduration. - No global lock: the method owns no shared/global semaphore (resource control lives in the concurrency issue).
- Resource ownership stays with the caller.
- Child writes never propagate back unless an explicit aggregating adapter is used (Join discards).
Acceptance Criteria
- Context flows to every branch; each branch has an isolated snapshot.
- Child context changes do not affect siblings or the caller afterward.
- Scopes are disposed once the batch completes.
- Events emitted per task and testable via the lifecycle observer.
- No behavior change for a single delegate.
Non-Goals
- No
MaxConcurrencyin this issue — that is the separateExecutionOptionsconcurrency issue. - No ordered-result preservation, no partial-success (
TryEach), no graph orchestration here — separate issues. - No blocking/
Parallel.ForEach— async-only.
Metadata
Metadata
Assignees
Labels
concurrencyParallel / batch / async executionParallel / batch / async executionenhancementNew feature or requestNew feature or requestextensionExtension behaviors / helpersExtension behaviors / helpers
Activity