Skip to content

Remove async code from the Drop handler and make cancellation more gentle for uv#90

Merged
bradhe merged 6 commits intodevelopfrom
fixes/cancel-on-drop
Sep 8, 2025
Merged

Remove async code from the Drop handler and make cancellation more gentle for uv#90
bradhe merged 6 commits intodevelopfrom
fixes/cancel-on-drop

Conversation

@bradhe
Copy link
Contributor

@bradhe bradhe commented Sep 7, 2025

Since the code was async, terminate was never actually called. In order to avoid all this crazy, we just push all the async code out (as much as possible) to make things simpler. We don't explicitly need to join the execution handler after the child is terminated...it just seems like the right thing to do to ensure the Drop implementation waits for the whole thing to be dead.

On top of that, I figured out that uv will leave child processes floating about if you use SIGKILL -- so we need to work around it a bit with SIGTERM being sent first.

This comment was marked as outdated.

@bradhe bradhe changed the title Remove async code from the Drop handler Remove async code from the Drop handler and make cancellation more gentle for uv Sep 7, 2025
@bradhe bradhe requested a review from Copilot September 7, 2025 17:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR removes async code from the Drop handler for LocalApp and implements more graceful process termination for uv child processes. The changes address issues where terminate was never called due to async constraints and improves child process cleanup by using SIGTERM before SIGKILL.

  • Remove async code from Drop implementation and simplify termination logic
  • Add process group configuration to uv command spawning for better process management
  • Implement graceful child process termination with SIGTERM followed by SIGKILL timeout

Reviewed Changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.

File Description
crates/tower-runtime/src/local.rs Refactored Drop handler to be non-async, simplified terminator field, and added graceful process killing
crates/tower-uv/src/lib.rs Added process_group(0) to all uv command spawns for better process group management
crates/tower-runtime/Cargo.toml Added nix dependency for Unix signal handling
Cargo.toml Added nix workspace dependency with signal features

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

nix::sys::signal::Signal::SIGTERM
).ok();

// If it doesn't die after 5 seconds then we'll forcefullt kill it.
Copy link

Copilot AI Sep 7, 2025

Choose a reason for hiding this comment

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

There's a typo in the comment: 'forcefullt' should be 'forcefully'.

Suggested change
// If it doesn't die after 5 seconds then we'll forcefullt kill it.
// If it doesn't die after 5 seconds then we'll forcefully kill it.

Copilot uses AI. Check for mistakes.

#[cfg(unix)]
async fn kill_child_process(ctx: &tower_telemetry::Context, mut child: Child) {
let pid = child.id().unwrap();
Copy link

Copilot AI Sep 7, 2025

Choose a reason for hiding this comment

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

Using unwrap() on child.id() could panic if the process ID is unavailable. Consider handling this error case gracefully or adding a comment explaining why panic is acceptable here.

Suggested change
let pid = child.id().unwrap();
let pid = match child.id() {
Some(pid) => pid,
None => {
debug!(ctx: &ctx, "Failed to get child process ID; process may have already exited.");
return;
}
};

Copilot uses AI. Check for mistakes.
Comment on lines +438 to +441
nix::sys::signal::killpg(
nix::unistd::Pid::from_raw(pid as i32),
nix::sys::signal::Signal::SIGTERM
).ok();
Copy link

Copilot AI Sep 7, 2025

Choose a reason for hiding this comment

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

[nitpick] The PID conversion nix::unistd::Pid::from_raw(pid as i32) is duplicated. Consider extracting it to a variable to improve readability and reduce duplication.

Copilot uses AI. Check for mistakes.
Comment on lines +452 to +455
nix::sys::signal::killpg(
nix::unistd::Pid::from_raw(pid as i32),
nix::sys::signal::Signal::SIGKILL
).ok();
Copy link

Copilot AI Sep 7, 2025

Choose a reason for hiding this comment

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

[nitpick] The PID conversion nix::unistd::Pid::from_raw(pid as i32) is duplicated. Consider extracting it to a variable to improve readability and reduce duplication.

Copilot uses AI. Check for mistakes.
if timeout.is_err() {
killpg(
pid,
Signal::SIGKILL
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe a bit old school but i reckon a SIGHUP before SIGKILL might be an idea

Copy link
Contributor

@konstantinoscs konstantinoscs left a comment

Choose a reason for hiding this comment

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

In general LGTM

}

#[cfg(not(unix))]
async fn kill_child_process(ctx: &tower_telemetry::Context, mut child: Child) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok so in windows will default back to whatever child.kill() tries to do

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, apparently (untested by me) it's not an issue on Windows.

Co-authored-by: Konstantinos St <kons.ste@gmail.com>
@bradhe bradhe merged commit e38f31a into develop Sep 8, 2025
4 checks passed
@bradhe bradhe deleted the fixes/cancel-on-drop branch September 8, 2025 13:44
bradhe added a commit that referenced this pull request Sep 9, 2025
* Remove async code from the Drop handler and make cancellation more gentle for `uv` (#90)

* Remove async code from the Drop handler

* feat: More graceful process shutdown to be gentle to uv

* chore: Refactor kill_child_process based on @copilot feedback

* chore: Only use process_group on unix systems

* chore: Only import the nix::* content in Unix land

* Update crates/tower-runtime/src/local.rs

Co-authored-by: Konstantinos St <kons.ste@gmail.com>

---------

Co-authored-by: Konstantinos St <kons.ste@gmail.com>

* Bump version to v0.3.28

* chore: Avoid unneeded import on Windows

---------

Co-authored-by: Konstantinos St <kons.ste@gmail.com>
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.

4 participants