Skip to content

refactor: introduce managed process lifecycle#2216

Merged
yohamta0 merged 2 commits into
mainfrom
refactor/process-lifecycle-semantics
May 27, 2026
Merged

refactor: introduce managed process lifecycle#2216
yohamta0 merged 2 commits into
mainfrom
refactor/process-lifecycle-semantics

Conversation

@yohamta0
Copy link
Copy Markdown
Collaborator

@yohamta0 yohamta0 commented May 27, 2026

Summary

  • add a managed process lifecycle module in cmdutil with explicit stop requests and outcomes
  • use Windows Job Objects for contained cleanup with process-tree fallback
  • migrate command, harness, and local DAG executor process stops through the lifecycle module

Testing

  • go test ./internal/cmn/cmdutil ./internal/runtime/... -count=1
  • go test ./internal/intg -run 'TestProcHeartbeat' -count=1

Summary by cubic

Introduced cmdutil.ManagedProcess to manage process lifecycles with clear stop requests and outcomes, and migrated command, harness, and local DAG execution to use it. Cleanup is hardened on containment or parent-watcher failures; Windows uses Job Objects with a process-tree fallback, Unix uses process groups.

  • Refactors

    • Added cmdutil.ManagedProcess with StopRequest/StopOutcome, parent-exit watching, and idempotent Release.
    • Windows: contain processes in Job Objects; fallback to process-tree kill; degrade gracefully if assignment/creation fails.
    • Unix: stop via process group; SetupCommand preserves existing SysProcAttr and ensures PGID is set.
    • Updated command executor, harness, and DAG runner to use managed processes; resource limits attach via PID(); release on exit.
    • Hardened failure paths: if containment or watcher setup fails, force-stop, wait, and release; outcomes report mechanism/containment.
    • Kept TerminateProcessGroup helpers, now backed by the lifecycle layer.
  • Migration

    • Use cmdutil.StartManagedProcess(cmd), then proc.Stop(...), proc.Wait(), and proc.Release().
    • Prefer intent-based stops (GracefulTermination/ForceTermination) via StopRequest (optionally include a reason) over raw signals.

Written for commit 0f7979d. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Introduced managed process lifecycle API for improved command execution control and process cleanup.
    • Added new termination models for standardized process stop requests and outcomes.
  • Refactor

    • Reorganized process termination logic into platform-specific lifecycle implementations.
    • Updated command and harness executors to use managed process API for better resource management and shutdown behavior.
  • Tests

    • Added test coverage for managed process lifecycle operations.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

This pull request refactors process lifecycle management by introducing ManagedProcess, a wrapper that abstracts OS-specific termination behavior. Process tracking shifts from raw exec.Cmd to managed-process instances across command executor, harness executor, and sub-DAG executor; Unix and Windows implementations are split into dedicated adapter files with platform-specific termination strategies.

Changes

Process Lifecycle Management Refactoring

Layer / File(s) Summary
ManagedProcess core abstraction and lifecycle
internal/cmn/cmdutil/lifecycle.go
Introduces ManagedProcess type wrapping *exec.Cmd with mutex-protected state, platform adapter interface managedPlatformProcess, and lifecycle methods: NewManagedProcess to wrap commands, StartManagedProcess to start with setup/preparation/containment coordination and optional parent-exit watcher, PID/Command/Wait accessors, Stop to normalize requests and delegate to platform adapter, and Release for idempotent cleanup.
Stop request and outcome modeling
internal/cmn/cmdutil/termination.go
Adds StopReason and StopMechanism string types with named constants for process stop reasons (cancel, timeout, shutdown, parent-exit) and mechanisms (none, process-group, process-tree, job-object); introduces StopRequest (intent + reason) and StopOutcome (requested/applied modes, mechanism, containment/partial flags) structs; implements request normalization and noop outcome builders.
Unix platform adapter and termination
internal/cmn/cmdutil/lifecycle_unix.go, internal/cmn/cmdutil/cmd_unix.go
Implements unixManagedProcess adapter with no-op lifecycle hooks and stop issuing syscall.Kill to process groups (SIGKILL for force intents, derived/default signals for graceful); re-exports TerminateProcessGroup and deprecated KillProcessGroup wrappers plus multi-process variants delegating to managed-process API; refactors cmd_unix.go to remove old termination helpers and safely initialize SysProcAttr fields only when nil.
Windows platform adapter with Job Object support
internal/cmn/cmdutil/lifecycle_windows.go, internal/cmn/cmdutil/cmd_windows.go
Implements windowsManagedProcess adapter creating a Job Object configured to terminate child processes on close, assigning processes after start, and stopping by terminating the Job Object with fallback to process-tree killing; re-exports termination wrappers with signal-to-intent mapping; removes old termination APIs from cmd_windows.go.
ManagedProcess lifecycle test suite
internal/cmn/cmdutil/lifecycle_test.go
Tests force termination (verifying requested/applied force modes and non-empty mechanism with 3-second timeout), nil-command no-op handling (graceful modes with StopMechanismNone), and idempotent Release() calls; includes OS-specific helpers for long-running and quick-exit command builders.
Command executor integration
internal/runtime/builtin/command/command.go, internal/runtime/builtin/command/command_test.go
Updates commandExecutor to track *cmdutil.ManagedProcess instead of *exec.Cmd; refactors Run to start via StartManagedProcess, assign resource limits using managed process PID, defer Release(), and wait asynchronously; implements context cancellation via Stop() with force-termination request; updates test fixture accordingly.
Harness executor integration
internal/runtime/builtin/harness/harness.go
Updates harnessExecutor to track process via *cmdutil.ManagedProcess; refactors Stop to delegate to internal helper and runOnce to start via StartManagedProcess, assign resource limits, defer release, and handle context cancellation by requesting force termination through managed-process stop API with timeout reason before setting exit code 124.
Sub-DAG executor integration
internal/runtime/executor/dag_runner.go, internal/runtime/executor/dag_runner_test.go
Updates SubDAGExecutor to track local executions using processes map of *cmdutil.ManagedProcess instances (replacing cmds map of *exec.Cmd); refactors runLocalCommand to start via StartManagedProcess, store in processes, defer release, and clean up on completion; updates Stop to call process.Stop() on managed instances; adjusts all test fixtures to initialize and populate managed-process map.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • dagucloud/dagu#2125: Implements parent-exit watcher (StartParentExitWatcher) for Unix child cleanup; this PR integrates that watcher into ManagedProcess.StartManagedProcess for automatic force termination on parent death.
  • dagucloud/dagu#2210: Refactors cmdutil termination wrappers to use explicit TerminationIntent; this PR builds on that foundation by wrapping managed processes and routing all termination through the intent-based API.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'refactor: introduce managed process lifecycle' accurately captures the main change: introducing a new managed process lifecycle module. However, it is quite broad and doesn't mention key aspects like Windows Job Object integration or the migration across multiple components.
Description check ✅ Passed The PR description includes a clear summary, detailed changes/refactors with technical rationale, testing instructions, and migration guidance matching the template's key sections.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/process-lifecycle-semantics

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 12 files

Re-trigger cubic

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/cmn/cmdutil/lifecycle.go`:
- Around line 94-118: Release() currently calls p.platform.release() without
synchronization while Stop() holds p.mu, risking concurrent access; change
Release() to acquire p.mu before invoking p.stopWatch() and p.platform.release()
(while still using p.releaseOnce.Do to ensure one-time semantics) and store the
result into p.releaseErr under the same lock to avoid races with Stop(); keep
the early nil check for p and ensure the lock is released before returning.
- Around line 54-57: When proc.platform.afterStart(cmd) fails, the started
process isn't reaped; call cmd.Wait() (on the same *exec.Cmd referenced as cmd)
before/while releasing platform resources to avoid leaving a zombie. Update the
failure branch that currently calls proc.platform.release() and returns
fmt.Errorf("failed to contain process: %w", err) to first invoke cmd.Wait(),
handle/collect any Wait error (append or wrap it with the original afterStart
error) and then call proc.platform.release(); ensure the final returned error
includes both the afterStart error and any cmd.Wait() error for visibility.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 852223b0-3b26-4d04-9094-c3d2a42daa0b

📥 Commits

Reviewing files that changed from the base of the PR and between 536a3b1 and 2e4ba4c.

📒 Files selected for processing (12)
  • internal/cmn/cmdutil/cmd_unix.go
  • internal/cmn/cmdutil/cmd_windows.go
  • internal/cmn/cmdutil/lifecycle.go
  • internal/cmn/cmdutil/lifecycle_test.go
  • internal/cmn/cmdutil/lifecycle_unix.go
  • internal/cmn/cmdutil/lifecycle_windows.go
  • internal/cmn/cmdutil/termination.go
  • internal/runtime/builtin/command/command.go
  • internal/runtime/builtin/command/command_test.go
  • internal/runtime/builtin/harness/harness.go
  • internal/runtime/executor/dag_runner.go
  • internal/runtime/executor/dag_runner_test.go
💤 Files with no reviewable changes (1)
  • internal/cmn/cmdutil/cmd_windows.go

Comment thread internal/cmn/cmdutil/lifecycle.go
Comment thread internal/cmn/cmdutil/lifecycle.go
@yohamta0 yohamta0 changed the title refactor: deepen process lifecycle semantics refactor: introduce managed process lifecycle May 27, 2026
@yohamta0 yohamta0 merged commit 552ef02 into main May 27, 2026
11 checks passed
@yohamta0 yohamta0 deleted the refactor/process-lifecycle-semantics branch May 27, 2026 10:52
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