util: Always use posix_spawn on macOS even with pre_exec hooks (#49090)

Here's some backstory:
* on macOS, @cole-miller and I noticed that since roughly Oct 2025, due
to some changes to latest macOS Tahoe, for any spawned child process we
needed to reset Mach exception ports
(https://github.com/zed-industries/zed/issues/36754 +
https://github.com/RemiKalbe/zed/commit/6e8f2d2ebe5a93753625a3026aeb996de8cb436b)
* the changes in that PR achieve that via `pre_exec` hook on
`std::process::Command` which then abandons `posix_spawn` syscall for
`fork` + `execve` dance on macOS (we tracked it down in Rust's std
implementation)
* as it turns out, `fork` + `execve` is pretty expensive on macOS
(apparently way more so than on other OSes like Linux) and `fork` takes
a process-wide lock on the allocator which is bad
* however, since we wanna reset exception ports on the child, the only
official way supported by Rust's std is to use `pre_exec` hook
* posix_spawn on macOS exposes this tho via a macOS specific extension
to that syscall `posix_spawnattr_setexceptionports_np` but there is no
way to use that via any standard interfaces in `std::process::Command`
* thus, it seemed like a good idea to instead create our own custom
Command wrapper that on non-macOS hosts is a zero-cost wrapper of
`smol::process::Command`, while on macOS we reimplement the minimum to
achieve `smol::process::Command`  with `posix_spawn` under-the-hood

Notably, this changeset improves git-blame in very large repos
significantly.

Release Notes:

- Fixed performance spawning child processes on macOS by always forcing
`posix_spawn` no matter what.

---------

Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
Jakub KonkaandCole Miller authored and GitHub committed 2026-02-13 20:16:11 +01:00
1 parent 4f54c3e07c
commit 16dfc60ad2
38 files changed
+1140 -297

No files matched your search

+7 -7
View File
@@ -30,9 +30,9 @@ use terminal::terminal_settings::TerminalSettings;
use smol::lock::OnceCell;
use std::cmp::{Ordering, Reverse};
use std::env::consts;
use std::process::Stdio;
use util::command::Stdio;
use util::command::new_smol_command;
use util::command::new_command;
use util::fs::{make_file_executable, remove_matching};
use util::paths::PathStyle;
use util::rel_path::RelPath;
@@ -1620,7 +1620,7 @@ impl PyLspAdapter {
let mut path = PathBuf::from(work_dir.as_ref());
path.push("pylsp-venv");
if !path.exists() {
util::command::new_smol_command(python_path)
util::command::new_command(python_path)
.arg("-m")
.arg("venv")
.arg("pylsp-venv")
@@ -1641,7 +1641,7 @@ impl PyLspAdapter {
// Try to detect situations where `python3` exists but is not a real Python interpreter.
// Notably, on fresh Windows installs, `python3` is a shim that opens the Microsoft Store app
// when run with no arguments, and just fails otherwise.
let Some(output) = new_smol_command(&path)
let Some(output) = new_command(&path)
.args(["-c", "print(1 + 2)"])
.output()
.await
@@ -1853,7 +1853,7 @@ impl LspInstaller for PyLspAdapter {
let venv = self.base_venv(delegate).await.map_err(|e| anyhow!(e))?;
let pip_path = venv.join(BINARY_DIR).join("pip3");
ensure!(
util::command::new_smol_command(pip_path.as_path())
util::command::new_command(pip_path.as_path())
.arg("install")
.arg("python-lsp-server[all]")
.arg("--upgrade")
@@ -1864,7 +1864,7 @@ impl LspInstaller for PyLspAdapter {
"python-lsp-server[all] installation failed"
);
ensure!(
util::command::new_smol_command(pip_path)
util::command::new_command(pip_path)
.arg("install")
.arg("pylsp-mypy")
.arg("--upgrade")
@@ -2421,7 +2421,7 @@ impl LspAdapter for RuffLspAdapter {
.0
.ok()?;
let mut command = util::command::new_smol_command(&binary.path);
let mut command = util::command::new_command(&binary.path);
command
.args(&["config", "--output-format", "json"])
.stdout(Stdio::piped())