Skip to content

feat(process): add revocable process quiescence - #218

Closed
4o3F wants to merge 1 commit into
mainfrom
feat/process-quiescence
Closed

4o3F wants to merge 1 commit into
mainfrom
feat/process-quiescence

Conversation

@4o3F

@4o3F 4o3F commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a revocable process "activity" input. A host can use it to pause automatic failure recovery during shutdown and to stop processes without starting any new ones. Part of libnyanpasu/clash-nyanpasu#6054.

Changes

  • Add ProcessActivitySource, ProcessActivity and ProcessActivityState::{Active, Quiescing}, plus SupervisorBuilder::activity. Behavior is unchanged when no activity is supplied.
  • While Quiescing, the supervisor does not respawn after a failure. It keeps the remaining backoff, spends no extra restart budget, and performs one deferred retry after the state is revoked. Processes that exited cleanly are not resurrected.
  • The activity is re-checked before respawning, at the final spawn boundary, and when the engine handles a queued stop.
  • On Windows, a stop while Quiescing kills the existing process tree directly, without launching the Ctrl+C helper. Active stops keep the graceful helper path.

Verification

  • Passing in WSL: process library tests (41), the targeted integration tests (24), the full suite (107 plus 1 doctest), clippy -D warnings, rustdoc and crate formatting. The new tests failed before the fix.
  • cargo +nightly check --target x86_64-pc-windows-gnu --all-targets --no-default-features --features process passes, so the Windows code paths type-check.
  • cargo fmt --all --check fails only on the existing log-preview/src/lib.rs.

Needs Windows CI

The process tests are not in the Windows matrix yet; the current CI only runs the pid-file tests explicitly. Please run process_supervisor, process_kill and process_helper_smoke on Windows to confirm that a Quiescing stop kills descendants with zero helper calls and that Active stops still use the helper.

Pause automatic failure recovery while the host is quiescing, preserving
the remaining backoff and one deferred retry without spending extra budget.

Read the same activity in supervisor stops and queued engine controls so
Windows shutdown kills the existing tree without launching a Ctrl+C helper.
Keep graceful stops for Active processes and preserve default behavior.

Refs libnyanpasu/clash-nyanpasu#6054
@semanticdiff-com

semanticdiff-com Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  src/process/supervisor.rs  19% smaller
  tests/process_kill.rs  1% smaller
  src/process/activity.rs  0% smaller
  src/process/command.rs  0% smaller
  src/process/engine.rs Unsupported file format
  src/process/mod.rs  0% smaller
  tests/helpers/test_child.rs  0% smaller
  tests/process_helper_smoke.rs  0% smaller
  tests/process_supervisor.rs  0% smaller

@greenhat616

Copy link
Copy Markdown
Contributor

Thanks for digging into libnyanpasu/clash-nyanpasu#6054. I'd like to narrow the scope of this PR.

Shutdown should be irreversible at this layer. Once the host starts shutting down, the supervisor should finish stopping. If the session turns out not to end (WM_ENDSESSION(FALSE)), the host can re-run startup after cleanup completes. That decision belongs to the application, not to the process supervisor. So a revocable Quiescing state isn't needed here, and neither is the watch channel.

Restart suppression already exists: once the supervisor's token is cancelled it never respawns (both the backoff wait and the pre-spawn check observe it). That makes the following unnecessary:

  • ProcessActivity / ProcessActivitySource / ProcessActivityState and SupervisorBuilder::activity
  • wait_to_restart and the remaining-backoff bookkeeping
  • Command::activity / Command::restart_cancel, the extra spawn-boundary check in engine::spawn (which also makes the engine depend on the supervisor), and select_stop

What we do need is a host-chosen stop strategy, so that a session-end stop on Windows kills the tree instead of launching the Ctrl+C helper. Because a stop can come from cascading cancellation (the token_.cancelled() branch), not just from Supervisor::stop(), the strategy has to be injected at build time and read at the moment of stopping. It's a one-shot read, so an atomic is enough:

  • an #[atomic_enum] StopStrategy { Graceful, Kill }
  • SupervisorBuilder::stop_strategy(Arc<AtomicStopStrategy>)
  • both Supervisor::stop and the cancellation branch load(Acquire) it and pass it to stop_process
  • the host does store(Kill, Release) before cancelling the root token
  • default (no strategy supplied) keeps today's behavior

Windows process tests still aren't in CI, so please run process_supervisor / process_kill / process_helper_smoke on Windows for the Kill path (no helper launched, descendants killed).

@4o3F

4o3F commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Closing: per maintainer decision, the session-end handling for libnyanpasu/clash-nyanpasu#6054 stays in the app's Tauri layer, so no process-supervisor change is needed here. Thanks for the review.

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.

2 participants