Skip to content

fix: restore runInBackground on the main thread after input simulation - #1812

Merged
hatayama merged 1 commit into
feature/windows-v3-verification-integrationfrom
fix/input-simulation-run-in-background-dispose
Jul 17, 2026
Merged

hatayama merged 1 commit into
feature/windows-v3-verification-integrationfrom
fix/input-simulation-run-in-background-dispose

Conversation

@hatayama

@hatayama hatayama commented Jul 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

uloop simulate-mouse-input and uloop simulate-keyboard failed on every invocation with UNITY_RPC_ERROR / set_runInBackground can only be called from the main thread (reproduced 5/5 on Windows during the v3 verification pass; the mechanism is platform-independent). The simulation itself succeeded (VibeLogs recorded *_complete with Success:true), but the successful response was discarded, and the escaped exception left PlayMode in Error Pause, blocking subsequent attempts.

Root cause

  • PR fix: Harden IPC contracts, empty RPC errors, and Settings async UI #1778's ConfigureAwait guard mechanically added .ConfigureAwait(false) to the executor awaits in SimulateMouseInputUseCase / SimulateKeyboardUseCase.
  • Those awaits complete through a TaskCompletionSource created with TaskCreationOptions.RunContinuationsAsynchronously (InputSystemConfiguredUpdateApplier), so with ConfigureAwait(false) the continuation always resumes on a thread-pool thread instead of Unity's main thread.
  • The using InputSimulationRunInBackgroundScope Dispose then ran on that pool thread and set Application.runInBackground — a main-thread-only API — which threw, replacing the successful response with an internal error.

Fix

Follow the existing precedent in PlayModeTestExecuter (why-not-using + finally with a main-thread switch):

  • SimulateMouseInputUseCase / SimulateKeyboardUseCase: replace using with an explicit scope + try/finally; the finally awaits InputSystemUpdateHelper.SwitchToMainThreadIfNeeded(CancellationToken.None) before Dispose(). (SwitchToMainThreadIfNeeded is a custom awaitable, so it is outside the ConfigureAwait guard's scope — no re-collision.)
  • InputSimulationRunInBackgroundScope.Dispose: added Debug.Assert(MainThreadSwitcher.IsMainThread, ...) so any future off-main-thread disposal fails fast. This required referencing the ToolContracts asmdef from the InputSimulation asmdef (same dependency the sibling Common/InputSystem asmdef already has).

Verification (Windows, Unity 2022.3.62f3, native uloop 3.1.0-beta.13)

  • Red: existing PlayMode tests SimulateMouseInputTests.LongPress_Should_RestoreRunInBackground_WhenOriginallyDisabled and SimulateKeyboardTests.Press_Should_RestoreRunInBackground_WhenOriginallyDisabled both failed before the fix with exactly set_runInBackground can only be called from the main thread.
  • Green: both tests pass after the fix.
  • uloop compile: 0 errors, 0 warnings.
  • E2E: PlayMode start → uloop simulate-keyboard --action Press --key Space x3 and uloop simulate-mouse-input --action Click x3 → all Success:true / exit 0 (previously 0/5); Unity console shows no errors or warnings afterwards; PlayMode stopped cleanly (no Error Pause).
  • Regression: full PlayMode SimulateMouseInputTests + SimulateKeyboardTests suites → 49/49 passed.

Review in cubic

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8074a023-713e-4df1-b71f-f108e833fa11

📥 Commits

Reviewing files that changed from the base of the PR and between d578c38 and 47c3c9d.

⛔ Files ignored due to path filters (1)
  • Packages/src/Editor/FirstPartyTools/Common/InputSimulation/UnityCLILoop.FirstPartyTools.Common.InputSimulation.Editor.asmdef is excluded by none and included by none
📒 Files selected for processing (3)
  • Packages/src/Editor/FirstPartyTools/Common/InputSimulation/InputSimulationRunInBackgroundScope.cs
  • Packages/src/Editor/FirstPartyTools/SimulateKeyboard/SimulateKeyboardUseCase.cs
  • Packages/src/Editor/FirstPartyTools/SimulateMouseInput/SimulateMouseInputUseCase.cs
 _______________________________________________________________________________________________________________________________
< Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live. - John Woods >
 -------------------------------------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/input-simulation-run-in-background-dispose

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.

simulate-mouse-input and simulate-keyboard always failed with
"set_runInBackground can only be called from the main thread"
(UNITY_RPC_ERROR) even though the simulation itself succeeded, and the
thrown exception additionally left PlayMode in Error Pause.

The executor awaits in both use cases were given .ConfigureAwait(false)
by the ConfigureAwait guard (#1778). Their completions come from a TCS
created with RunContinuationsAsynchronously, so the method always
resumed on a thread-pool thread, and the `using` scope's Dispose then
set Application.runInBackground (a main-thread-only API) off the main
thread, discarding the successful response.

Follow the existing PlayModeTestExecuter pattern: replace `using` with
an explicit scope + try-finally that switches back to the main thread
via InputSystemUpdateHelper.SwitchToMainThreadIfNeeded before Dispose.
Also assert the main-thread precondition inside
InputSimulationRunInBackgroundScope.Dispose so future regressions fail
fast (requires the ToolContracts asmdef reference).
@hatayama
hatayama force-pushed the fix/input-simulation-run-in-background-dispose branch from f64e531 to 47c3c9d Compare July 17, 2026 11:50
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
putComment timed out

@hatayama
hatayama merged commit 200677c into feature/windows-v3-verification-integration Jul 17, 2026
8 checks passed
@hatayama
hatayama deleted the fix/input-simulation-run-in-background-dispose branch July 17, 2026 11:59
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