Skip to content

Stop quit[] from ending the host process - #181

Open
HugoFara wants to merge 1 commit into
devfrom
fix/quit-does-not-exit
Open

Stop quit[] from ending the host process#181
HugoFara wants to merge 1 commit into
devfrom
fix/quit-does-not-exit

Conversation

@HugoFara

@HugoFara HugoFara commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Closes #160. quit is in the script command table, so it is reachable from the Python binding and from the HTTP server, and it called exit(0). The reproduction from the issue, before and after:

before quit[]                          before quit[]
<process gone>                         AFTER quit[]
                                       FINALLY ran
                                       still alive, exiting normally
                                       ATEXIT ran

That one line gave no exception to catch, skipped finally and atexit, lost buffered output, and returned status 0 — so a batch job records success having stopped early.

What changed

quit now releases the session and records the request; Command::quitRequested() exposes it and clearQuitRequest() forgets it. The shell in app/forefire is what leaves. Both the interactive loop and the HTTP listen loop check, so a quit[] served over HTTP still stops the server. An embedder is free to ignore the request.

quit was also deleting currentSession.params, which is the SimulationParameters singleton, shared with every other holder. Only the exit() on the next line hid it; removing the exit alone would have turned this into a use-after-free on the next parameter read. It is no longer deleted.

@filippi — the safe topology path is your call

Command.cpp:1125 called quit() from a catch (...) in the atmospheric step loop, under // TODO supersafe mode ?, so an internal failure ended the host process with status 0. It now reports and returns error. This changes behaviour in coupled runs: a safe-topology failure no longer terminates, and the caller continues. If a hard stop needs to stay reachable there, say so and I will make it a distinct command the binding does not expose, as #160 suggests.

Verification

Reproduction from #160 passes, output above. Unit suite 5/5 with three new cases. All five CTest entries now require doctest's closing Status: SUCCESS! line, so an early exit(0) fails the suite instead of passing it — without that, my first test passed with the bug still in place. runff KML and NetCDF both match within tolerance.


This pull request, including its code changes and this description, was generated by Claude Opus 5, and reviewed manually before submitting.

EDIT: rewrote for human readability.

quit is in the script command table, so it is reachable from the Python
binding and from the HTTP server, and it called exit(0). A host got no
exception and no traceback, its finally blocks and atexit handlers never
ran, buffered output was lost, and the status was 0 — so a batch job
recorded success having stopped early.

quit now releases the session and records the request; quitRequested()
exposes it and clearQuitRequest() forgets it. The shell in app/forefire
is what leaves: both the interactive loop and the HTTP listen loop check
it, so a served quit[] still stops the server. executeLoop stops reading
a script after one, rather than the process disappearing mid-file.

currentSession.params is no longer deleted. It is the SimulationParameters
singleton, owned by GetInstance() and shared with every other holder, so
deleting it left GetInstance() handing out a dangling pointer. That went
unnoticed only because exit() followed on the next line; without the
exit it would be a use-after-free on the next parameter read.

The safe topology error path called quit() too, so an internal failure
terminated the host on its own, with status 0. It reports and returns
error instead. The message moves to stderr and is no longer gated on
commandOutputs: a returned error nobody is obliged to check should not
also be silent.

tests/unit/test_quit_command.cpp covers it, and the CTest entries now
require doctest's closing 'Status: SUCCESS!' line. Without that a suite
whose process exits mid-run reports as a pass, because ctest sees 0 and
does not look for the summary that never printed — the first version of
this test passed happily with exit(0) still in place.

Verified with the reproduction from #160:

  before quit[] / AFTER quit[] / FINALLY ran / ATEXIT ran, exit 0

against the previous output, which was 'before quit[]' and a dead
interpreter. Unit suite 5/5; runff KML and NetCDF match.

Closes #160
@HugoFara
HugoFara requested a review from filippi August 12, 2026 20:13
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