fix: exit lifecycle probe from verified idle

This commit is contained in:
codex
2026-08-03 17:02:18 +03:00
parent 79067b72de
commit d8e61b7a5e
@@ -180,7 +180,10 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
{ {
transitions.Add("LifecycleProbeCompleted"); transitions.Add("LifecycleProbeCompleted");
WriteEvidence(true, null); WriteEvidence(true, null);
RequestControlledExit(); // LifecycleProbe is already running from the first verified Idle callback.
// Do not wait for a second Idle: a startup-script host is permitted not to
// raise one, which otherwise leaves acad.exe responsive but stranded.
RequestControlledExit(idleSafe: true);
} }
private void OnIdle(object? sender, EventArgs e) => RunCallback("Idle", BeginContextVerification); private void OnIdle(object? sender, EventArgs e) => RunCallback("Idle", BeginContextVerification);
@@ -285,18 +288,28 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
// AutoCAD's direct Application.Quit can leave a startup-script host alive. // AutoCAD's direct Application.Quit can leave a startup-script host alive.
// Dispatching the native QUIT command from the next quiescent Idle cycle // Dispatching the native QUIT command from the next quiescent Idle cycle
// preserves normal command shutdown semantics. // preserves normal command shutdown semantics.
private void RequestControlledExit() private void RequestControlledExit(bool idleSafe = false)
{ {
if (exitQueued) return; if (exitQueued) return;
exitQueued = true; exitQueued = true;
Transition(CoordinatorState.ExitQueued, "ExitQueued"); Transition(CoordinatorState.ExitQueued, "ExitQueued");
if (idleSafe)
{
DispatchNativeQuit();
return;
}
AcadApplication.Idle += OnExitIdle; AcadApplication.Idle += OnExitIdle;
exitHandlerSubscribed = true; exitHandlerSubscribed = true;
} }
private void OnExitIdle(object? sender, EventArgs args) private void OnExitIdle(object? sender, EventArgs args)
{ {
RunCallback("ControlledExitIdle", () => RunCallback("ControlledExitIdle", DispatchNativeQuit);
}
private void DispatchNativeQuit()
{
try
{ {
if (exitHandlerSubscribed) AcadApplication.Idle -= OnExitIdle; if (exitHandlerSubscribed) AcadApplication.Idle -= OnExitIdle;
exitHandlerSubscribed = false; exitHandlerSubscribed = false;
@@ -306,7 +319,13 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
if (active is null) { AcadApplication.Quit(); return; } if (active is null) { AcadApplication.Quit(); return; }
transitions.Add("NativeQuitDispatched"); transitions.Add("NativeQuitDispatched");
active.SendStringToExecute("_.QUIT _N ", true, false, false); active.SendStringToExecute("_.QUIT _N ", true, false, false);
}); }
catch (System.Exception exception)
{
// This is terminal cleanup: contain the exception and leave process
// cleanup to Launcher rather than re-entering document APIs.
FailFromCallback("ControlledExitIdle", exception, "AUTOMATION_CONTROLLED_EXIT_FAILED");
}
} }
private bool IsTerminal => state is CoordinatorState.Completed or CoordinatorState.Failed or CoordinatorState.ExitQueued; private bool IsTerminal => state is CoordinatorState.Completed or CoordinatorState.Failed or CoordinatorState.ExitQueued;