FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Ensure `uv.exe` exits when `uvw.exe` or `uvx.exe` is killed by zanieb · Pull Request #17500 · astral-sh/uv · GitHub

/ uv Public

Ensure uv.exe exits when uvw.exe or uvx.exe is killed - #17500

Merged
zanieb merged 5 commits into
astral-sh:mainfrom
zaniebot:claude/investigate-issue-17492-znH4F
Jan 29, 2026
Merged

zanieb merged 5 commits into
astral-sh:mainfrom
zaniebot:claude/investigate-issue-17492-znH4F

Conversation

zanieb commented Jan 15, 2026
edited
Loading

Copy link
Copy Markdown
Member

This matches the approach used by the trampoline in bounce.rs so we combine the implementations in a new uv-windows crate.

Closes #17492
Closes #11817

zanieb added the bug Something isn't working label Jan 15, 2026
zaniebot force-pushed the claude/investigate-issue-17492-znH4F branch 7 times, most recently from c89872b to f1ebfb9 Compare January 15, 2026 23:50
zanieb changed the title Ensure uv.exe exits when uvw.exe is killed Ensure uv.exe exits when uvw.exe or uvx.exe is killed Jan 15, 2026

zanieb commented Jan 15, 2026

Copy link
Copy Markdown
Member Author

Terrifying code. Will need to review this closely.

zaniebot force-pushed the claude/investigate-issue-17492-znH4F branch 3 times, most recently from 934469f to e37fe6d Compare January 16, 2026 13:27
zanieb force-pushed the claude/investigate-issue-17492-znH4F branch from aa418af to c29d707 Compare January 16, 2026 22:40

samypr100 commented Jan 17, 2026
edited
Loading

Copy link
Copy Markdown
Collaborator

This relates to #11817 (can also close)

zanieb commented Jan 17, 2026

Copy link
Copy Markdown
Member Author

Ah thanks I had forgotten about that discussion.

zanieb force-pushed the claude/investigate-issue-17492-znH4F branch 4 times, most recently from 08c1d52 to 5d4c5f6 Compare January 18, 2026 21:32
On Windows, use Job Objects to ensure the child `uv.exe` process is
terminated when wrapper processes (`uvw.exe`, `uvx.exe`) are killed.
This is important for tools like Task Scheduler that terminate the
wrapper process.

The fix uses the `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` flag which
instructs Windows to terminate all processes associated with the job
when the job object handle is closed (which happens when the wrapper
is terminated).

This change:
- Creates a shared `windows_spawn` module with the Job Object logic
- Updates both `uvw.exe` and `uvx.exe` to use this shared module
- Matches the approach used by the trampoline in `bounce.rs`

Closes astral-sh#17492
zanieb force-pushed the claude/investigate-issue-17492-znH4F branch 2 times, most recently from 683933f to f920c85 Compare January 28, 2026 04:01
zanieb added the test:extended Enable extended tests for a pull request label Jan 28, 2026
zanieb force-pushed the claude/investigate-issue-17492-znH4F branch from f920c85 to 073f7b0 Compare January 28, 2026 04:21
zanieb marked this pull request as ready for review January 28, 2026 14:08
konstin requested a review from EliteTK January 28, 2026 14:33

EliteTK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Looks good, but note the handle leak in spawn_child. That's the only real issue, the rest can wait.

Comment thread crates/uv-windows/src/spawn.rs Outdated
Comment thread crates/uv-windows/src/spawn.rs Outdated
Comment thread crates/uv-windows/src/spawn.rs Outdated
Comment thread crates/uv-windows/src/spawn.rs Outdated
Comment thread crates/uv-windows/src/job.rs Outdated
Comment on lines +140 to +141
info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

If we're only writing to the basic information field why are we querying the extended information?

Looks like this is a carry-over from the previous code... Might be a problem for another PR.

/// to receive and handle the control signal, not the wrapper. Returning `TRUE`
/// from the handler tells Windows we've handled the signal (by ignoring it).
///
/// See: `distlib/PC/launcher.c::control_key_handler`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

The actual logic in the referenced code is somewhat more sophisticated.

Specifically it handles CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT specially by disabling JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. The reason here is, as I understand it, ignoring these events doesn't stop windows from killing your process, and these events are also going to go to the child anyway. Disabling KILL_ON_JOB_CLOSE gives the child a chance to more gracefully handle these.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Although, again, this looks like it was taken from the previous code, so maybe not relevant here.

Comment thread crates/uv-trampoline/src/bounce.rs Outdated
Comment thread crates/uv-windows/src/spawn.rs Outdated
Comment on lines +63 to +64
// Explicitly drop child to close the process handle before exiting.
drop(child);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

If we do this we might want to drop the job first too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

It's unclear to me if we need to do either drops manually? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I don't believe we need to drop child manually at all.

But I did think about this a bit further and I think there's another subtle problem. Job might get dropped immediately after we assign the process as we never use it again.

I think dropping job here explicitly might guarantee it lives long enough... There are also some other options.

But, I think really what we should do here is something like:

struct SupervisedChild<'a> {
    job: HANDLE,
    child: HANDLE,
    _child_lifetime: PhantomData<&'a ()>,
}

impl<'a> SupervisedChild<'a> {
    fn new<T>(child: HANDLE, _child_reference: &'a T) -> Self {
        let job = unsafe { CreateJobObjectW(None, None) }.map_err(|e| JobError::Create(e.code().0))?;
        // Configure limits
        // Assign the child
        // Maybe even set up the control handler given both code paths do it?
        Ok(Self { job, child, PhantomData::default() })
    }

    fn wait(self) -> Result<whatever> {
        WaitForSingleObject ...
    }
}

The PhantomData would ensure that the child outlives the JobSupervisedChild. And moving the wait here would ensure that we don't drop the SupervisedChild too early.

For the trampoline case we might just want to have a tiny wrapper around HANDLE that drops it at the right time and lets you access the HANDLE, then we can use a reference to the wrapper for the lifetime handling.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

We could even go a step further and have the ChildHandle wrapper be in the same place as SupervisedChild, give it a From implementation for the std case, and then you can just give SupervisedChild the ChildHandle reference directly, let it keep it, and avoid the PhantomData.

One last alternative (probably too much boilerplate for two code paths): we could make SupervisedChild generic over the type, have our ChildHandle wrapper for the trampoline case, have it implement our own AsRawHandle trait, and then implement it for Child in std contexts...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

spawn.rs is std only right now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

We may want to fix this in the trampolines too but maybe I'll just do that in a separate change? It seems like it'd be a pre-existing problem?

I guess in the worst-case we just vendor the std impl?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

The existing code (in the trampolines) didn't have this problem because it didn't have a Drop impl on the Job handle so it couldn't automatically get cleaned up too early.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Ah, I see. Will look at that briefly then.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Comment thread crates/uv-windows/src/spawn.rs Outdated
zanieb force-pushed the claude/investigate-issue-17492-znH4F branch from 628b493 to 15be283 Compare January 28, 2026 17:49
zanieb merged commit 1b4407f into astral-sh:main Jan 29, 2026
167 checks passed
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Feb 2, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [astral-sh/uv](https://github.com/astral-sh/uv) | patch | `0.9.27` → `0.9.28` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>astral-sh/uv (astral-sh/uv)</summary>

### [`v0.9.28`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0928)

[Compare Source](astral-sh/uv@0.9.27...0.9.28)

Released on 2026-01-29.

##### Python

- Update CPython to use [OpenSSL 3.5.5](https://github.com/openssl/openssl/releases/tag/openssl-3.5.5) which includes fixes for high severity CVEs ([python-build-standalone#960](astral-sh/python-build-standalone#960))

##### Enhancements

- Add support for Pyodide interpreter on Windows ([#&#8203;17658](astral-sh/uv#17658))
- Warn if multiple indexes include `default = true` ([#&#8203;17713](astral-sh/uv#17713))
- Skip uploads when validation reports 'Already uploaded' ([#&#8203;17412](astral-sh/uv#17412))

##### Configuration

- Add a reflink alias for the "clone" link mode ([#&#8203;17724](astral-sh/uv#17724))

##### Bug fixes

- Ensure `uv.exe` exits when `uvw.exe` or `uvx.exe` is killed ([#&#8203;17500](astral-sh/uv#17500))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45NC42IiwidXBkYXRlZEluVmVyIjoiNDIuOTQuNiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6OnBhdGNoIl19-->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working test:extended Enable extended tests for a pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Terminating uvw does not terminate uv [Windows] Processes spawned by uv are not killed when uv is killed

3 participants


Back | FazBrowse Home | New Git URL