Skip to content

[patch] Run queued Invoke/InvokeAsync work on the default scheduler, so it stays on the owner thread - #74

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/65-run-on-default-scheduler
Oct 6, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/65-run-on-default-scheduler

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #65

Problem

DoInvokes drained TaskQueue with task.RunSynchronously(), which targets TaskScheduler.Current. Suppose DoInvokes is itself called from a task on a scheduler whose TryExecuteTaskInline returns false. The queued task then went back to that scheduler, with two possible outcomes:

  • Thread-pool scheduler: the Invoked delegate ran on another thread, which breaks the library's core guarantee.
  • Single dedicated-thread scheduler: the owner deadlocked.

Change

  • task.RunSynchronously(TaskScheduler.Default). The default scheduler always inlines here, so the delegate runs on the owner thread. A comment explains why the argument matters.

Tests

New DoInvokesFromATaskOnANonInliningSchedulerRunsInvokeOnTheOwnerThread in InvokerTests:

  • A private NonInliningTaskScheduler queues to the thread pool and refuses to inline. The owner loop runs as a task on it.
  • Another thread calls Invoke, and the owner calls DoInvokes.
  • The test asserts that DoInvokes returns within a 10 s bound, and that the delegate ran on the owner's thread.

Checked locally (Linux, net10.0):

  • Without the fix, the test fails: the delegate ran on a different thread.
  • With the fix, the full suite passes (27/27) across 3 repeated runs.

#71 also edits the TaskQueue drain loop in DoInvokes, so whichever lands second will need a one-line merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bm33TiYrofqKs4oGc3UGUU


Generated by Claude Code

…so it stays on the owner thread

DoInvokes called task.RunSynchronously() with no scheduler, which targets
TaskScheduler.Current. When DoInvokes is itself called from a task on a
scheduler that refuses to inline, the queued task went back to that scheduler,
so the delegate ran off the owner thread or, on a single-thread scheduler,
deadlocked. RunSynchronously(TaskScheduler.Default) always inlines here.

Fixes #65

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bm33TiYrofqKs4oGc3UGUU
@sonarqubecloud

sonarqubecloud Bot commented Oct 6, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 6b2ccd3 into main Oct 6, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the fix/65-run-on-default-scheduler branch October 6, 2026 06:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant