Repository navigation
Fix host hang when Electron exits before the socket bridge connects - #1124
FlorianRappl merged 3 commits into
Conversation
When Electron exits before the socket bridge connects (for example because another instance holds the single-instance lock), RuntimeControllerDotNetFirst stops the bridge, but SocketBridgeService only reaches Stopped through BridgeDisconnected, which a never-connected socket does not raise. The controller then never reaches Stopped and the .NET host hangs forever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Create the tests for the lifecycle. Rest looks fine to me. |
…ServiceBase - SocketBridgeServiceLifecycleTests: a bridge that never connects (closed loopback port) reaches Stopped after Stop(), raises Stopping then Stopped exactly once, and is not moved out of Stopped by the pending connect attempt. These fail without the previous commit (the bridge stays in Stopping and Stopped is never raised). - LifetimeServiceBaseTests: Start/Stop transitions, Stopped before Ready completing every earlier wait task (what lets hosts awaiting WaitReadyTask observe a stopped runtime), same-state transitions being ignored, and backward transitions / Stop after Stopped throwing. No Electron runtime is required for either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks! Added lifecycle tests for |
|
Right now almost all tests are failing. Not sure if this is related but beforehand tests worked and this is not just one faulty machine but across all runners. |
|
Thanks for flagging this. I looked into it, and the failures come from Electron crashes at startup with The cause is the Host lockfile. It's copied into I've opened #1125 to repair the lockfile. Locally on Linux (Electron 38.2.2), that fix with this PR merged on top gives 112 passed, 18 skipped, 0 failed. Once it's merged, I'll update this branch from |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pr-comment: Run #161
🎉 All tests passed!Github Test Reporter by CTRF 💚 |
Problem
With the DotNet-first startup and
SingleInstanceenabled, launching the app a second time leaves the second .NET process hanging forever.The second Electron process loses the single-instance lock and quits right after printing its socket port, before the socket bridge has connected. What happens next:
ElectronProcessActivetransitions toStopped→RuntimeControllerDotNetFirst.HandleStopped()sees the socket bridge is not stopped and callssocketBridge.Stop().SocketBridgeService.StopCore()disposes the socket, andLifetimeServiceBase.Stop()moves the bridge toStopping.StoppedthroughBridgeDisconnected, but a socket that never connected never raises it.Stopped.WaitReadyTask/WaitStoppedTasknever complete, so the host'sMainwaits forever.The same applies whenever Electron exits before the bridge connects (e.g. a crash during startup).
Fix
SocketBridgeServiceoverridesStop(): after the base implementation (dispose +Stopping), it transitions toStoppedif the disconnect event has not already done so. A connected socket that raisesBridgeDisconnectedbehaves as before. The state is only checked afterbase.Stop()returns, so theStoppingtransition insidebase.Stop()can't run afterStoppedand throw for an invalid backward transition.The controller then reaches
Stopped. BecauseStopped>Ready,WaitReadyTaskcompletes too, so the host can checkStateand exit.Testing
Added lifecycle tests to
ElectronNET.IntegrationTests. Neither class needs an Electron runtime:SocketBridgeServiceLifecycleTestsstarts a bridge against a closed loopback port, so it can never connect, and checks that afterStop()it:Stoppedand completesWaitStoppedTask;StoppingthenStopped, exactly once;Stoppedafter the pending connect attempt gives up.Without the fix, the first two fail with the bridge stuck in
StoppingandStoppednever raised. The third one also fails, because the bridge never got toStoppedin the first place.LifetimeServiceBaseTestscovers the state-machine rules the fix and the controller rely on:Start/Stoptransitions;StoppedbeforeReadycompleting every earlier wait task, includingWaitReadyTask, so a host awaiting readiness can observe the stopped runtime and exit;Stop()afterStopped, throwing.Results on Linux:
GlobalShortcutTests.Can_register_and_unregister, which also fails on its own on this machine: Wayland/Hyprland doesn't allow Electron global shortcuts.🤖 Generated with Claude Code