Libuv: uv_spawn is slow when parent process allocates mmap with MAP_JIT flag

Created on 21 Nov 2020  路  19Comments  路  Source: libuv/libuv

  • Version: tip-of-tree (1.x)
  • Platform: macOS Big Sur (11.0.1)

Repro Steps : https://github.com/deepak1556/libuv-spawn

The issue is derived from https://github.com/electron/electron/issues/26143

Applications running on hardened runtime based on chromium/electron create mmap regions using MAP_JIT flag. With Big Sur the fork() calls done by uv_spawn have become slow. Theory is that before big sur fork() didn't have access to the JIT memory regions but they do now.

// Using the repro linked above

$ sudo dtruss -e ./out/Default/uv_spawn --use-jit

45600 fork()         = 7815 0
42260 fork()         = 7816 0

$ sudo dtruss -e ./out/Default/uv_spawn

558 fork()       = 7801 0
391 fork()       = 7800 0

Chromium's own child process spawning code doesn't suffer from this new behaviour because it uses posix_spawn.

Is it in the interest of libuv project to make the change to use posix_spawn atleast for macOS >= 11.0 ?

macos not-stale

Most helpful comment

Thanks for the added context! I will work on the fix this week.

All 19 comments

Thanks for the repro. I don't run Big Sur so I have no way to test myself but your analysis sounds right.

posix_spawn() on macOS is an actual system call (on other Unices libc often emulates it through fork() + execve()) with a shortcut to spawn the new process directly. No memory mappings are copied, ergo, it's very fast.

Switching to posix_spawn() isn't an option in general - it's too limited for libuv's use cases - but it could be an option for macOS because you can use platform-specific extensions.

At the very least you'll need:

  • posix_spawn_file_actions_addchdir_np() to implement the cwd option to uv_spawn().
  • posix_spawnattr_set_uid_np() for uid
  • posix_spawnattr_set_gid_np() for gid
  • probably posix_spawnattr_set_groups_np() to emulate the setgroups() logic in src/unix/spawn.c

I won't be working on that myself but you or anyone else is welcome to send a pull request.

The slow speed is unfortunate, but what's worse is that it locks the JavaScript thread (in Electron, freezing the app UI), before nodejs' spawn() call even returns.

I'm not familiar with what part of that is node vs libuv, but is it possible for this slow part to happen asynchronously (since the rest of the spawn api is async)?

Thanks for the added context! I will work on the fix this week.

@deepak1556 Do you think this will be fixed this year? Is there any way we can help? Our Electron app is unusable on Big Sur because it relies heavily on child processes, so we鈥檙e trying to determine if we should build workarounds or contribute to the fix. Thanks again for the help!

@pdesantis the issue is on our projects' December iteration task but can't promise a date and I haven't started on it. I also was not aware if anyone else wanted to work on it. So, feel free to contribute!

All the relevant api swaps that @bnoordhuis pointed are to be made in src/unix/process.c, specifically under fn uv__process_child_init and uv_spawn

@deepak1556 thanks! Taking a crack at it today, although don't get your hopes up too high since we're new to this codebase 馃槄. Will report back by the end of the day.

@deepak1556 @bnoordhuis we've made some progress here: https://github.com/descriptinc/libuv/commit/58cf78e29ff19edc15321655a807d3a54632b688 (shoutout to @jpcanepa!)

It's incomplete but seems promising so far. Using the repro project https://github.com/deepak1556/libuv-spawn we're seeing ls launch & exit times of 0.5ms & 3ms, which is inline with the times from the unsigned executable using fork (actually, they're slightly faster!)

Any tips on porting the file descriptor (options->stdio) logic?

Disregard my previous question, @jpcanepa posted a PR here https://github.com/libuv/libuv/pull/3064

I want to make sure that eveyrone involved here is aware that that PR (#3064) is in _draft_ because I need _help_. I can continue trying to bang things into compiling correctly, but I kind of hope that someone (@deepak1556?, @bnoordhuis?) can chip in as well.

As a bit of expectation management: I have very little time at the moment.

I've updated the PR so that now it builds correctly with CMake and Autotools, and there are only two open questions: a failing unit test case due to a behavior difference from the fork/execvp flow and a question about one of the posix_spawn_*_np function calls.

FWIW, the PR #3064 seems to be done, just needs some attention for a final review, to get it merged.

FWIW, the PR #3064 seems to be done, just needs some attention for a final review, to get it merged.

Looking forward to seeing this fixed so GitKraken will run again without it being slowed down (issue)!

Reopening because https://github.com/libuv/libuv/pull/3064 was reverted.

Any progress here?

Still happens

any progress here ?

@cjihrig it seems the patch is merged as of today, so is there any progress on this?

@darkguy2008 it was merged, but then reverted in https://github.com/libuv/libuv/pull/3107.

Was this page helpful?
0 / 5 - 0 ratings