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:
1 parent
4f54c3e07c
commit
16dfc60ad2
38 files changed
+1140
-297
No files matched your search
@@ -427,7 +427,7 @@ impl ManagedNodeRuntime {
|
||||
let node_ca_certs = env::var(NODE_CA_CERTS_ENV_VAR).unwrap_or_else(|_| String::new());
|
||||
|
||||
let valid = if fs::metadata(&node_binary).await.is_ok() {
|
||||
let result = util::command::new_smol_command(&node_binary)
|
||||
let result = util::command::new_command(&node_binary)
|
||||
.env(NODE_CA_CERTS_ENV_VAR, node_ca_certs)
|
||||
.arg(npm_file)
|
||||
.arg("--version")
|
||||
@@ -555,7 +555,7 @@ impl NodeRuntimeTrait for ManagedNodeRuntime {
|
||||
) -> Result<Output> {
|
||||
let attempt = || async {
|
||||
let npm_command = self.npm_command(proxy, subcommand, args).await?;
|
||||
let mut command = util::command::new_smol_command(npm_command.path);
|
||||
let mut command = util::command::new_command(npm_command.path);
|
||||
command.args(npm_command.args);
|
||||
command.envs(npm_command.env);
|
||||
configure_npm_command(&mut command, directory);
|
||||
@@ -640,7 +640,7 @@ pub struct SystemNodeRuntime {
|
||||
impl SystemNodeRuntime {
|
||||
const MIN_VERSION: semver::Version = Version::new(22, 0, 0);
|
||||
async fn new(node: PathBuf, npm: PathBuf) -> Result<Self> {
|
||||
let output = util::command::new_smol_command(&node)
|
||||
let output = util::command::new_command(&node)
|
||||
.arg("--version")
|
||||
.output()
|
||||
.await
|
||||
@@ -723,7 +723,7 @@ impl NodeRuntimeTrait for SystemNodeRuntime {
|
||||
args: &[&str],
|
||||
) -> anyhow::Result<Output> {
|
||||
let npm_command = self.npm_command(proxy, subcommand, args).await?;
|
||||
let mut command = util::command::new_smol_command(npm_command.path);
|
||||
let mut command = util::command::new_command(npm_command.path);
|
||||
command.args(npm_command.args);
|
||||
command.envs(npm_command.env);
|
||||
configure_npm_command(&mut command, directory);
|
||||
@@ -841,7 +841,7 @@ impl NodeRuntimeTrait for UnavailableNodeRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_npm_command(command: &mut smol::process::Command, directory: Option<&Path>) {
|
||||
fn configure_npm_command(command: &mut util::command::Command, directory: Option<&Path>) {
|
||||
if let Some(directory) = directory {
|
||||
command.current_dir(directory);
|
||||
command.args(["--prefix".into(), directory.to_path_buf()]);
|
||||
|
||||
Reference in new issue
Block a user