Skip to content

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

Open
HugoFara wants to merge 2 commits into
devfrom
fix/quit-does-not-exit
Open

HugoFara wants to merge 2 commits into
devfrom
fix/quit-does-not-exit

Conversation

@HugoFara

@HugoFara HugoFara commented Aug 12, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #160.

@filippi, one decision for you: Command.cpp:1125 (safe-topology catch, // TODO supersafe mode ?) used to call quit() and end the host with status 0. It now reports and returns an error, so coupled runs keep going after a safe-topology failure. If you need a hard stop there, I'll add a separate command that the Python binding does not expose.

  • quit no longer calls exit(0). It releases the session and sets a flag (quitRequested() / clearQuitRequest()). The app/forefire shell reads the flag and exits, so quit[] over HTTP still stops the server.
  • quit no longer deletes currentSession.params. That is the shared SimulationParameters singleton, and without the exit() deleting it would become a use-after-free.
  • CTest now requires doctest's Status: SUCCESS! line, so a test that exits early fails instead of passing.

Tested: the #160 reproduction now reaches finally and atexit. Unit tests 5/5 (3 new). runff KML and NetCDF match.


Code and description generated with Claude Opus 5, reviewed before submitting.

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

This branch has not been deployed

No deployments
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.

Command::quit calls exit(0) from inside the library, silently killing the host process

2 participants