Use posix_spawn on modern glibc to avoid NSTask fork deadlocks#572
Conversation
I will be adding tests just give me a few hours sorry didn't realize how tests worked in this repo. I have actually found a small issue with detachments so we should talk about it when I get the tests up. |
a0c72b1 to
6612b02
Compare
|
Ok I have added tests now. I would also like to mention an issue that I ran across writing tests and I have had copilot summarize the issue: After switching NSTask from fork() to posix_spawn(), the existing test in launch.m:98 started failing. The test expects launched tasks to detach from the controlling terminal, which used to happen automatically with the old fork() + setsid() path. The failure wasn’t due to NSTask itself, but due to environment restrictions. The flag is valid (glibc ≥ 2.26, kernel ≥ 3.4), but So the code was correct, but the environment blocked the operation. Two coordinated changes fix the issue: NSTask fallback logic The test no longer assumes that glibc ≥ 2.35 guarantees session creation. So in summary: |
|
It used to be best practice to detach long-lived background tasks (daemons) from the controlling terminal, presumably because it's really bad user interface to have odd messages written to a terminal. Loss of that feature doesn't seem hugely harmful but I suppose we should really try to find a way to restore it. |
Yes I agree we should bring this functionality back. The thing is most newer versions of Glibc support POSIX_SPAWN_SETSID so this is really only a backstop for older versions of Glibc that are more than 6 years old. I will figure out the test failures thank you for your response. |
6612b02 to
1ed5140
Compare
|
Ok @rfm I believe this was actually a problem with the tests. I think I have fixed this I tried several different linux platforms. Can you please re-run the CI? |
|
Running |
58b621a to
abf2342
Compare
|
I’ve made a set of changes that should address the issue. I ran into significant difficulty getting detachment to work reliably across certain platforms, and it appears this behavior is dependent on the glibc version in use. After reviewing the changes more critically, I wasn’t comfortable with the earlier modification to the existing processgroup.m helper, so I’ve reverted that adjustment. |
|
I believe we can rerun the pipelines now and they all pass for me locally. |
This change updates NSTask on Linux to use posix_spawn() instead of fork() when running on glibc 2.29 or newer. The old fork() path was unsafe in multi‑threaded applications and could deadlock before calling exec(), especially on newer distributions like Ubuntu 22.04. The new implementation uses posix_spawn_file_actions to set up stdin/stdout/stderr, PTY handling, signal defaults, file‑descriptor cleanup, and working‑directory changes through the glibc addchdir_np() extension. This keeps all setup work in the parent and ensures the child immediately executes the target program without touching unsafe APIs. Systems with glibc older than 2.29 continue using the existing fork() implementation. This improves reliability, avoids multi‑threaded deadlocks, and matches modern best practices without changing NSTask’s external behavior.