Skip to content

fix: Dynamic code cancellation no longer leaves Unity busy - #1369

Merged
hatayama merged 2 commits into
v3-betafrom
feature/hatayama/fix-frame-debugger-hang
Jun 18, 2026
Merged

fix: Dynamic code cancellation no longer leaves Unity busy#1369
hatayama merged 2 commits into
v3-betafrom
feature/hatayama/fix-frame-debugger-hang

Conversation

@hatayama

@hatayama hatayama commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Dynamic code execution now releases cancellation promptly even when the executed code returns an async task that ignores cancellation.
  • Late task failures remain observed, and completed task results or failures are preserved when cancellation arrives at the same time.

User Impact

  • Before this change, canceling a dynamic-code operation could leave Unity or the CLI communication path appearing stuck until the editor was restarted.
  • After this change, cancellation returns a clean canceled result and the next dynamic-code request can run normally.

Changes

  • Make dynamic-code awaiting cancellation-aware while keeping await continuations context-free.
  • Observe late faults from abandoned user tasks so they do not surface as unrelated unobserved exceptions.
  • Add EditMode coverage for cancellation release, late-fault observation, completed result preservation, and completed fault preservation.

Verification

  • cli/dist/darwin-arm64/uloop compile --project-path "$(git rev-parse --show-toplevel)"
  • cli/dist/darwin-arm64/uloop run-tests --project-path "$(git rev-parse --show-toplevel)" --test-mode EditMode --filter-type regex --filter-value "io\\.github\\.hatayama\\.UnityCliLoop\\.Tests\\.Editor\\.DynamicCodeToolTests\\.CommandRunnerCancellationTests\\..*"
  • cli/dist/darwin-arm64/uloop run-tests --project-path "$(git rev-parse --show-toplevel)" --test-mode EditMode --filter-type regex --filter-value "io\\.github\\.hatayama\\.UnityCliLoop\\.Tests\\.Editor\\.DynamicCodeToolTests\\..*"
  • ~/.codex/skills/codex-review/scripts/codex-review --parallel-tests ... v3-beta returned clean with no accepted/actionable findings.

Review in cubic

hatayama added 2 commits June 18, 2026 21:50
Dynamic snippets can return awaitables that never complete when user code ignores cancellation. Bind Task, ValueTask, and custom awaitable waits to the request token so CommandRunner releases its execution slot after client disconnects, and cover the blocked async-result path with an EditMode regression test.
Ensure canceled dynamic-code awaits observe late task faults, keep helper awaits context-free, and let already-completed returned tasks surface their result or exception instead of being overwritten by late cancellation.
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 623a28b6-e23f-4f9c-8b7a-12e0241f4427

📥 Commits

Reviewing files that changed from the base of the PR and between ada8255 and d78132d.

⛔ Files ignored due to path filters (1)
  • Assets/Tests/Editor/DynamicCodeToolTests/CommandRunnerCancellationTests.cs.meta is excluded by none and included by none
📒 Files selected for processing (3)
  • Assets/Tests/Editor/DynamicCodeToolTests/CommandRunnerCancellationTests.cs
  • Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/AwaitableHelper.cs
  • Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/CommandRunner.cs

📝 Walkthrough

Walkthrough

AwaitableHelper.AwaitIfNeeded gains a CancellationToken parameter and three new private helpers (AwaitTaskWithCancellationAsync, ObserveAbandonedTaskFaultAsync, AwaitObjectTaskWithCancellationAsync) that implement Task.WhenAny-based cancellation with fault observation for abandoned tasks. CommandRunner.ExecuteInternalAsync now forwards its token to AwaitIfNeeded. A new test fixture validates cancellation scenarios.

Changes

Cancellation-aware async execution

Layer / File(s) Summary
AwaitIfNeeded signature, cancellation helpers, and CommandRunner wiring
Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/AwaitableHelper.cs, Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/CommandRunner.cs
AwaitIfNeeded adds a CancellationToken parameter and routes Task/ValueTask awaiting through new AwaitTaskWithCancellationAsync helper (uses Task.WhenAny + a cancellation TaskCompletionSource). ObserveAbandonedTaskFaultAsync attaches a fault-observing continuation to tasks abandoned by cancellation. The awaitable-pattern fallback path is updated to use AwaitObjectTaskWithCancellationAsync. CommandRunner.ExecuteInternalAsync passes cancellationToken to AwaitIfNeeded.
Cancellation test fixture, state coordinator, and DynamicCommand wrapper
Assets/Tests/Editor/DynamicCodeToolTests/CommandRunnerCancellationTests.cs
CommandRunnerCancellationTests adds four tests covering: runner recovery after async code ignores cancellation, fault observation on an abandoned task, and AwaitIfNeeded behavior when the token is already canceled at task completion. WrappedDynamicCommandState uses TaskCompletionSource signals to script started/blocking/returning behaviors. UnityCliLoop.Dynamic.DynamicCommand wraps WrappedDynamicCommandState.ExecuteAsync.

Sequence Diagram(s)

sequenceDiagram
  participant CommandRunner
  participant AwaitIfNeeded
  participant AwaitTaskWithCancellationAsync
  participant ObserveAbandonedTaskFaultAsync

  CommandRunner->>AwaitIfNeeded: AwaitIfNeeded(invoked, cancellationToken)
  AwaitIfNeeded->>AwaitTaskWithCancellationAsync: forward task + cancellationToken

  rect rgba(255, 100, 100, 0.5)
    Note over AwaitTaskWithCancellationAsync: cancellation fires first
    AwaitTaskWithCancellationAsync->>ObserveAbandonedTaskFaultAsync: attach fault-observer continuation
    AwaitTaskWithCancellationAsync-->>AwaitIfNeeded: throw OperationCanceledException
    AwaitIfNeeded-->>CommandRunner: propagate cancellation
  end

  rect rgba(100, 200, 100, 0.5)
    Note over AwaitTaskWithCancellationAsync: task completes first
    AwaitTaskWithCancellationAsync-->>AwaitIfNeeded: return Task<object> result
    AwaitIfNeeded-->>CommandRunner: return result
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: Dynamic code cancellation no longer leaves Unity busy' directly addresses the main issue fixed in this PR—cancellation now releases resources promptly instead of leaving the system stuck.
Description check ✅ Passed The description comprehensively explains the fix, user impact, implementation approach, and verification steps, all directly related to the changeset's goal of fixing cancellation behavior in dynamic code execution.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/hatayama/fix-frame-debugger-hang

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

Re-trigger cubic

@hatayama
hatayama merged commit e7e58e9 into v3-beta Jun 18, 2026
9 of 10 checks passed
@hatayama
hatayama deleted the feature/hatayama/fix-frame-debugger-hang branch June 18, 2026 14:27
@github-actions github-actions Bot mentioned this pull request Jun 18, 2026
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.

1 participant