Repository navigation
Stop owned processes when their coroutine exits - #99
Conversation
Track native process handles weakly in coroutine-local state and reuse pool cleanup at owner exit. Preserve detached, fake and non-coroutine starts. Share the output guard through Support while retaining interrupted-stop behavior. Verified ProcessTest, ProcessNonCoroutineTest, real concurrency consumers, source/type analysis and facade consistency.
Guard require and remove output callbacks so their exceptions cannot leave Composer changing the project in the background. Both real-child regressions fail before correction; SupportComposerTest passes.
Apply the shared output guard to MySQL, MariaDB and PostgreSQL dump callbacks without changing schema extension points. Real-child regressions fail before correction; MySQL and PostgreSQL schema-state test files pass.
Prevent a caller-supplied output handler from abandoning the current worker process after an exception. The regression fails before correction; QueueListenerTest passes.
Declare the Process component used directly by Foundation console commands and Composer integration, using the framework's existing version constraint. Package metadata parses successfully; no root dependency versions change.
Start the fixture through the Process API while retaining graceful shutdown before log inspection. SwooleMaintenanceTimerWorkerRecycleTest passes.
Use coroutine-owned subprocesses and return ProcessResult from the profiler helper, preserving output, exit-code and report-cleanup assertions. ProfileScriptTest passes.
Retain each child before starting it and stop all started readers if startup, waiting or an assertion fails. FilesystemNonCoroutineTest passes.
Avoid signaling the test runner's process group when the fixture has no process ID. HttpClientStreamingTest passes with its established platform skip.
Keep real workers and supervisor processes inside exception-safe cleanup while preserving mocked behavior and assertions. Both changed Horizon files pass against an isolated temporary Redis instance.
Keep process-group cleanup limited to a fixture with a known process ID. ShutdownOnInterruptListenerTest passes with its established Swoole skips.
Guard nullable process IDs and remove the startup sleep because Symfony obtains the PID during start. CommanderServeTest passes directly and through package:test with one worker.
Explain coroutine-exit cleanup, explicit detachment and raw Symfony ownership. Recommend the framework Process API for fixtures while retaining explicit shutdown where timing or process trees require it. Documentation matches the reviewed source and behavior tests.
Integrate the merged framework and integration cancellation fixes before publishing process ownership and callback cleanup. Preserve cancellation-aware stop, pool cleanup and regression coverage alongside the previously reviewed weak process tracking and shared output guard. The merge needs no conflict resolution. The process change remains 31 files; differences from its reviewed patch only reflect already-landed dependencies, imports and adjacent cancellation changes. Complete Process and non-coroutine tests, targeted source analysis and formatting pass.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
| Layer / File(s) | Summary |
|---|---|
Coroutine-owned process lifecycle src/process/src/ProcessState.php, src/process/src/PendingProcess.php, src/process/src/Pool.php, src/process/composer.json, tests/Process/*, src/docs/processes.md, src/docs/porting-from-laravel.md, src/docs/testing.md, src/process/README.md |
PendingProcess::start() registers attached processes with coroutine state. ProcessState stops tracked processes on coroutine exit. Documentation describes ownership, detachment, and cleanup. Tests cover cancellation, completed processes, forked coroutines, and non-coroutine use. |
Output callback failure cleanup src/support/src/Traits/GuardsProcessOutput.php, src/support/src/Composer.php, src/database/src/Schema/*, src/queue/src/Listener.php, src/support/composer.json, src/foundation/composer.json, tests/Database/*, tests/Queue/QueueListenerTest.php, tests/Support/SupportComposerTest.php, tests/Process/ProcessTest.php |
The shared guard stops processes after output callback failures and preserves exception handling. Database dump, queue worker, and Composer process paths use the guard. Tests verify that callback exceptions propagate and the processes stop. |
Hypervel process APIs in tests tests/Cache/SwooleMaintenanceTimerWorkerRecycleTest.php, tests/Testing/Profile/ProfileScriptTest.php |
The tests use Hypervel process APIs instead of Symfony Process directly. Profiler tests read output and exit status from ProcessResult. |
Subprocess test cleanup tests/Filesystem/FilesystemNonCoroutineTest.php, tests/Integration/Horizon/Feature/*, tests/Http/HttpClientStreamingTest.php, tests/Server/Listeners/ShutdownOnInterruptListenerTest.php, tests/Testbench/CommanderServeTest.php |
Tests stop subprocesses in cleanup blocks and skip process-group signals when the PID is null. The serve test also removes a delay before recording the process PID. |
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Bug fix
Sequence Diagram(s)
sequenceDiagram
participant PendingProcess
participant SymfonyProcess
participant ProcessState
participant InvokedProcessPool
PendingProcess->>SymfonyProcess: Start process
PendingProcess->>ProcessState: Register attached process
ProcessState->>InvokedProcessPool: Stop tracked processes on coroutine exit
Merge Risk: 
Low · previous review d4084
-
A process may remain running after an output callback fails if cleanup also fails. This narrow failure path warrants a fix or explicit acceptance before merging.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed-
Full details: Title check
-
Explanation
The title "Stop owned processes when their coroutine exits" matches the main change. The PR tracks asynchronous processes per coroutine in ProcessState and stops them when the owning coroutine exits, as the raw summary and PR objectives describe. The title omits the output-failure guard move and the docs/test changes, which is acceptable because the title need not cover every detail.
Full details: Description check
-
Explanation
The description follows the template's main sections: Problem and change, Supporting evidence, Verification, and the checklist. It explains the problem (asynchronous processes left running after their coroutine exits, and output callback failures interrupting cleanup), the change, and the detachment and non-coroutine exceptions. Gaps: the contribution type checkboxes are not selected, the Supporting evidence section does not give the regression test with failing and passing results for the bug fix, and the Verification section does not list the composer fix run from the repository root. It also states that the full suite runs in CI, which does not satisfy the required pre-PR composer fix run. The description is mostly complete, so the check passes, but the author should fill these gaps.
-
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 38.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 31 functions across 24 files. (7 skipped: 7 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches
-
Generate docstrings
-
- Commit to this branch
- Create a new PR
Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
|
@coderabbitai review |
Action not completed
|
PR Summary by QodoStop owned processes when their coroutine exits
AI Description
Diagram
High-Level Assessment
Files changed (31)
|
Code Review by Qodo
1. Discarded handles leave children running
|
|
@coderabbitai review |
Action performed
|
A callback failure can already carry cancellation in its exception chain. If emergency stop also throws cancellation, checking only the callback exception class replaces that original failure and loses its cleanup context. Use the shared cancellation-precedence helper when selecting the callback or stop failure. Keep the native-only stop catch, bounded retry and callback re-entry guard unchanged. Extend the existing emergency-stop regression with a wrapped callback failure and assert the retained exception identity. The added row fails before the correction; the complete Process test file, targeted analysis of the trait and its consumers, and formatting pass.
|
The weak-ownership finding is declined. An attached Symfony Process stops its child in __destruct(), so leaving the WeakMap through destruction does not abandon the child. Quiet processes without a retaining callback can be destroyed immediately; output-enabled processes can remain reachable through their callback until owner cleanup. A real probe discarding the facade handle while retaining only its PID confirms that the child is gone after owner exit. Strong references and explicit deregistration would add retention and bookkeeping without fixing a reproduced failure. |
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.
create_new_console, keep their previous lifetime and require manualwaitorstop.supportandfoundationnow declare a direct dependency onsymfony/process;processadds ahypervel/contextdependency.Written for commit 57e74a4. Summary will update on new commits.
Note
Stop owned processes automatically when their starting coroutine exits
PendingProcess::startnow registers the Symfony process in the current coroutine'sProcessState; ProcessState.php stops all tracked processes via a coroutine defer callback when the owner exits, including on cancellation. Processes started outside a coroutine or withcreate_new_consoleare not registered.GuardsProcessOutputfromHypervel\Process\ConcernstoHypervel\Support\Traitsin GuardsProcessOutput.php. On an output-callback exception it attemptsProcess.stop(0)up to twice and picks between the callback exception and any cleanup cancellation when rethrowing.MySqlSchemaState,PostgresSchemaState,Listener, andComposer, so a throwing output callback stops the subprocess before the exception propagates.finallycleanup or switch to the Hypervel process API.symfony/process ^8.1, and the process package requireshypervel/context ^0.4(composer.json, composer.json). Existing consumers importingGuardsProcessOutputfromHypervel\Process\Concernsmust update to the new namespace.Macroscope summarized 57e74a4.
Summary by CodeRabbit