Skip to content

[codex] add exit-code lifecycle runner - #9

Merged
LimiNode merged 6 commits into
mainfrom
codex/exit-code-runner-forced-shutdown
Jun 8, 2026
Merged

[codex] add exit-code lifecycle runner#9
LimiNode merged 6 commits into
mainfrom
codex/exit-code-runner-forced-shutdown

Conversation

@LimiNode

@LimiNode LimiNode commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a reusable ConsoleApplicationRunner that drives the Consolix component lifecycle and returns an exit code without calling std::exit, plus an optional wakeable throttle component for polling loops.

What Changed

  • Added ConsoleApplicationRunner with run_for_exit_code(), request_stop(), and forced-stop waiting for runner-thread cleanup.
  • Added consolix::run_for_exit_code(), consolix::request_stop(code), and consolix::stop(code) convenience APIs.
  • Refactored ConsoleApplication into a compatibility facade over the runner while preserving process-owning run() behavior via std::exit(returned_code).
  • Updated Windows console-control handling so Ctrl+C/Ctrl+Break request cooperative shutdown, while close/logoff/shutdown events request runner-thread cleanup and wait for a bounded timeout.
  • Made runner instances explicitly single-use, documented active-runner routing for global stop requests, and covered repeat-run rejection with a test.
  • Added LoopThrottleComponent, a wakeable optional wait step for polling loops, with wake() and configurable delay.
  • Added LoopWakeService as a shared wake channel so ordinary runner stop requests wake throttled loop waits without hard-coding the runner to a specific component.
  • Ordered throttle-local wake state before shared wake broadcasts to avoid a stale next-pass wake.
  • Added runnable examples for the exit-code runner and wakeable loop throttling.
  • Documented polling-loop CPU behavior, stop-aware throttle wake behavior, POSIX signal wake limitations, and the component-level final shutdown hook pattern based on LIFO shutdown order.
  • Removed parent-directory include paths from Consolix headers and documented the include-policy rule for agents.
  • Added runner/throttle tests and advanced external/log-it-cpp to the latest main pointer.

Why

Backends and embedded runtimes need Consolix lifecycle and signal handling without losing control through std::exit. Windows control handlers also should not perform full component shutdown directly from the handler thread because that can race or deadlock with normal component processing, logging, or module work.

Consolix is also a polling-loop runtime. Keeping sleep out of the runner preserves low-latency behavior by default, while LoopThrottleComponent gives regular services and tools an explicit way to avoid busy-loop CPU usage and still wake early when new work arrives. LoopWakeService lets ordinary consolix::stop() requests wake long throttle waits through the existing resource model, so shutdown does not wait for the configured throttle delay.

Runner reuse is intentionally rejected instead of reset in place because shutdown may already have destroyed components, services, and logging resources. A fresh AppComponentManager plus a fresh runner is the clearer lifecycle boundary.

The include cleanup keeps Consolix aligned with the project's umbrella-header model: aggregate-owned internal headers receive shared dependencies from their entry point, and standalone public leaf headers use forward project include paths instead of ../.

Validation

  • cmake --build tmp/agent-work/build-runner-mingw --target test_console_application_runner test_loop_throttle_component example_loop_throttle_component --parallel 1
  • ctest --test-dir tmp/agent-work/build-runner-mingw -R "test_console_application_runner|test_loop_throttle_component" --output-on-failure -> 2/2 passed
  • cmake --build tmp/agent-work/build-runner-cxx11-mingw --target test_console_application_runner test_loop_throttle_component example_loop_throttle_component --parallel 1
  • ctest --test-dir tmp/agent-work/build-runner-cxx11-mingw -R "test_console_application_runner|test_loop_throttle_component" --output-on-failure -> 2/2 passed
  • cmake --build tmp/agent-work/build-runner-mingw --parallel 1
  • ctest --test-dir tmp/agent-work/build-runner-mingw --output-on-failure -> 7/7 passed
  • cmake --build tmp/agent-work/build-runner-cxx11-mingw --parallel 1
  • ctest --test-dir tmp/agent-work/build-runner-cxx11-mingw --output-on-failure -> 5/5 passed
  • Ran examples/example_loop_throttle_component.exe -> exit code 0
  • Direct include compile check for <consolix/utils/enums.hpp> and <consolix/utils/path_utils.hpp>
  • Direct compile check for LoopWakeService through <consolix/core.hpp>
  • rg -n '^\\s*#include .*\\.\\./' include -> no matches
  • git diff --check

Note: an earlier full MinGW build with high parallelism hit cc1plus.exe: out of memory; the same builds passed when rerun sequentially.

@LimiNode
LimiNode marked this pull request as ready for review June 8, 2026 10:13
@LimiNode
LimiNode marked this pull request as draft June 8, 2026 10:58
@LimiNode
LimiNode marked this pull request as ready for review June 8, 2026 20:33
@LimiNode
LimiNode merged commit e2def4e into main Jun 8, 2026
11 checks passed
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