Stop owned processes when their coroutine exits

Merged
BI
1d ago
+474-123

Asynchronous processes left behind by their starting coroutine could retain native process resources until shutdown. Output callback failures could also interrupt cleanup in framework callers using Symfony Process directly.

Track asynchronous processes weakly in their owning coroutine and stop any remaining children when that coroutine exits. Explicitly detached processes remain detached, and processes started outside a coroutine retain their existing lifetime.

Share the output-failure cleanup guard with schema commands, queue listeners and Composer operations. Move suitable test fixtures onto the Process API, retain independent cleanup in termination regressions, and document when tests should use the framework API or Symfony Process directly.

Targeted tests cover coroutine exit, cancellation, callback failures, process pools and non-coroutine execution. Source analysis and affected tests pass; the full suite and service matrices run in CI.


Summary by cubic

Stops asynchronous processes when the coroutine that started them exits, so orphaned native process resources are released instead of lingering until shutdown.

  • Processes started outside a coroutine, or explicitly detached with create_new_console, keep their previous lifetime and require manual wait or stop.
  • Output callback failures in MySQL/Postgres schema dumps, queue listeners, and Composer operations stop the child process before rethrowing; when that emergency cleanup also cancels, the original callback failure is preserved, including when it already wraps a cancellation.
  • Test fixtures that only need coroutine-lifetime cleanup now use the Process API; termination-timing tests keep explicit cleanup.
  • support and foundation now declare a direct dependency on symfony/process; process adds a hypervel/context dependency.

[!NOTE]

Stop owned processes automatically when their starting coroutine exits

  • PendingProcess::start now registers the Symfony process in the current coroutine's ProcessState; ProcessState.php stops all tracked processes via a coroutine defer callback when the owner exits, including on cancellation. Processes started outside a coroutine or with create_new_console are not registered.
  • Moves GuardsProcessOutput from Hypervel\Process\Concerns to Hypervel\Support\Traits in GuardsProcessOutput.php. On an output-callback exception it attempts Process.stop(0) up to twice and picks between the callback exception and any cleanup cancellation when rethrowing.
  • Applies the guard to dump, queue worker, and Composer package-command callbacks in MySqlSchemaState, PostgresSchemaState, Listener, and Composer, so a throwing output callback stops the subprocess before the exception propagates.
  • Updates process, testing, and porting docs to describe coroutine ownership, pool cleanup, and explicit stop guidance; many tests add finally cleanup or switch to the Hypervel process API.
  • Behavioral Change: installing the foundation and support packages now requires symfony/process ^8.1, and the process package requires hypervel/context ^0.4 (composer.json, composer.json). Existing consumers importing GuardsProcessOutput from Hypervel\Process\Concerns must update to the new namespace.

Macroscope summarized 57e74a4.

Summary by CodeRabbit

  • New Features
    • Asynchronous processes and process pools are now stopped automatically when their owning coroutine exits, including when it is canceled. Detached processes remain independent of coroutine cleanup.
    • Output-handler failures now trigger process cleanup before the original error is surfaced.
  • Documentation
    • Clarified process ownership, cancellation, detachment, and cleanup guidance for asynchronous processes, pools, testing, and Laravel migrations.