Skip to content

Fix disposal completion and preserve transparent enqueue forwarding - #40

Merged
dombrovsky merged 10 commits into
mainfrom
issue-39-thread-flow-disposal
Oct 24, 2026
Merged

dombrovsky merged 10 commits into
mainfrom
issue-39-thread-flow-disposal

Conversation

@dombrovsky

@dombrovsky dombrovsky commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Thread-affine flows could leave caller-facing Enqueue tasks incomplete after disposal because typed and convenience async wrappers outlived queue shutdown. Return the typed execution item's completion task directly and forward it through convenience overloads. Retain ExecutionItem.Cancel.

Pipeline forwarding does not convert an exception thrown before an operation task is returned into a completion outcome. Returned task failures retain completion middleware behavior. Pipelines enforce their own middleware-lifetime and admission invariants without probing terminal disposal. Accepted child execution and independent completion continuations stay tracked until quiescence, including when outer middleware throws or times out. Explicit Submit retains its admission-decision/completion-task contract.

TaskFlowSchedulerAdapter, cancellation scope, cancel-previous, timeout, and WhenAnyCancelRest retain their base-branch implementations. The adapter leaves Task.Factory.StartNew exceptions alone, including its TaskSchedulerException wrapping. Existing async wrappers retain task-based rejection and their original resource ownership. Tests distinguish these behaviors from immediate pipeline forwarding rejection.

The pipeline invokes enqueue before handing the returned task to observation. The observation helper retains the continuation's activity until its task settles; releasing that activity when invocation returns could let accepted work outlive its middleware lifetime. Observation now sits outside the invocation try/catch, making the handoff explicit.

Remaining extension changes are direct task forwarding/validation in convenience and extended-state overloads, plus the one-line throttle fix that keeps its own admission rejection task-based. Rollback experiments with unchanged assertions found:

  • Reverting convenience overload forwarding failed ten original task-identity regressions, independently of the newer synchronous-exception tests.
  • Reverting the throttle fix while retaining transparent pipeline forwarding failed twelve pre-existing throttle tests on .NET 8.
  • Reverting cancellation scope and cancel-previous changed only two new immediate-rejection timing expectations; resource cleanup remained correct. Their tests now explicitly verify the restored task-based contract.

No public API signatures or analyzer suppressions were added. The universal synchronous-exception wording on ITaskScheduler was removed; raw adapter behavior has focused regression coverage instead.

Validation on 0632faa2e8ab011bd9b10ef70e77f878b37db4d6:

  • Restore, whitespace, and file-scoped namespace checks passed.
  • Release build passed with zero warnings and zero errors.
  • All 4,850 solution tests passed locally across .NET 8 and .NET 10.
  • The single automatic GitHub Actions Build run passed all 4,850 tests across both runtimes. No reruns were requested.
  • CodeQL, documentation, benchmark smoke, and benchmark comparison workflows passed.

Fixes #39

@dombrovsky dombrovsky changed the title Fix thread-flow disposal task settlement race Fix disposal completion and synchronous enqueue rejection Oct 18, 2026
@dombrovsky
dombrovsky force-pushed the issue-39-thread-flow-disposal branch from 5bb679c to 7deb974 Compare October 20, 2026 03:07
@dombrovsky
dombrovsky marked this pull request as ready for review October 20, 2026 03:54
@dombrovsky dombrovsky changed the title Fix disposal completion and synchronous enqueue rejection Fix disposal completion and preserve synchronous enqueue rejection Oct 20, 2026
@dombrovsky dombrovsky changed the title Fix disposal completion and preserve synchronous enqueue rejection Fix disposal completion and preserve transparent enqueue forwarding Oct 20, 2026
@dombrovsky
dombrovsky merged commit 3952bd7 into main Oct 24, 2026
10 checks passed
@dombrovsky
dombrovsky deleted the issue-39-thread-flow-disposal branch October 24, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix thread-flow disposal race: queued operation tasks can remain incomplete after DisposeAsync

1 participant