Repository navigation
fix(csharp): a sliced task wait still runs a queued task inline where its scheduler allows - #11
Merged
Merged
Conversation
… its scheduler allows Regression in affe6ef, measured live in Godot (main-thread GodotSynchronizationContext, 5 s budget): Task.Factory.StartNew(() => 13, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).Result returned 13 on 2556a2f and Timeout after the budget on affe6ef: under a budget, a wait without bound for a still-queued task no longer ran it inline on the waiting thread, so a task queued to the blocked thread's own context could never run. The guard's task waits (Wait / WaitAll / Result / GetAwaiter().GetResult()) without bound and without a cancelable token now first offer a still-queued task to its scheduler to run inline (Task.WrappedTryRunInline, the framework's own path, found by reflection; absent -> every wait is sliced). The scheduler decides whether this thread may run it; a declined task or one with no body (an async method's promise) is waited for in slices as before, so `async F().Result` on a main-thread context still stops at the budget. Tests on a single-threaded test SynchronizationContext: the regression case (Result, GetResult, Wait, WaitAll, Task.Start) returns 13 (fails without the fix: Timeout after 5 s); async-method waits, the context's task waited on from the pool, and an inlined endless body still stop at the budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…not fail, spec forms From a report-only reviewer agent (high): - No inline attempt on a thread-pool thread: the pool's scheduler could hand the snippet's thread any task from its local queue (not necessarily the snippet's); other pool threads run those anyway. Godot/Unity main threads are unaffected. - The WrappedTryRunInline lookup cannot throw in the guard's type initializer (that would break every guarded snippet). - WaitAll offers its tasks in the framework's order (last to first). - Spec: names the forms that inline (not WaitAny), the pool-thread and missing-method cases, and that a non-snippet body run inline is stopped only when it returns. - Tests: a cold-compile-safe time bound, an inlined body that throws keeps its exception (not Timeout), WaitAll of a pool task plus a context task. Not taken: retrying the inline attempt between slices (the framework does not either). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
IvanMurzak
added a commit
that referenced
this pull request
Oct 30, 2026
* chore: release 0.4.0 (execution guards, sliced waits) Version 0.3.1 -> 0.4.0 in dotnet/Directory.Build.props, the README snippet and the Python module; the contract's Unreleased changelog entry becomes v0.4.0 (title v0.4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * simplify: no changes Report-only review: the bump covers every hard-coded version; the blank line before v0.3.1 is pre-existing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * code review: no findings Report-only reviewer (high): every version reference is 0.4.0, the v0.4.0 changelog matches #7-#11. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Regression (affe6ef)
This was measured live in Godot, with the main-thread
GodotSynchronizationContextand a 5 s budget:Task.Factory.StartNew(() => 13, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).Resultreturned
13on 2556a2f (the task ran inline) and returnsTimeoutafter the budget on affe6ef.Cause: under a budget, a wait with no timeout on a task that is still queued became 50 ms slices of
Wait(ms). A wait with a timeout never runs the task inline, so a task queued to the blocked thread's own context could never run.Fix
Wait,WaitAll,.Result,GetAwaiter().GetResult(), all in the guard stand-ins), it first offers a still-queued task to its scheduler to run inline. It uses the framework's own path,Task.WrappedTryRunInline, found by reflection.async F().Resulton a main-thread context still returnsTimeoutpromptly;Timeout.Tests
These run on a single-threaded test
SynchronizationContext(posts are queued until the thread is free):Result,GetResult,Wait,WaitAll,Task.Start, andWaitAllof a pool task plus a context task) returns 13. Without the fix it fails:Timeoutafter 5 s, verified locally.Result,GetResult,WaitandWaitAll;Timeout.Stages:
simplify:andcode review:commits, the review done by a report-only reviewer agent. No version change, no release, no workflow changes. Unity-MCP re-copiesdotnet/CodeExec/ExecutionGuard.cs.🤖 Generated with Claude Code