Hello,
I have a Cocoa application from which I fork a new process (helper sort of) and it crashes on fork due to some cleanup code probably registered with pthreads_atfork()
in Network framework.
This is crash from the child process:
Application Specific Information:
*** multi-threaded process forked ***
BUG IN CLIENT OF LIBPLATFORM: os_unfair_lock is corrupt
Abort Cause 258
crashed on child side of fork pre-exec
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libsystem_platform.dylib 0x194551238 _os_unfair_lock_corruption_abort + 88
1 libsystem_platform.dylib 0x19454c788 _os_unfair_lock_lock_slow + 332
2 Network 0x19b1b4af0 nw_path_shared_necp_fd + 124
3 Network 0x19b1b4698 -[NWConcrete_nw_path_evaluator dealloc] + 72
4 Network 0x19af9d970 __nw_dictionary_dispose_block_invoke + 32
5 libxpc.dylib 0x194260210 _xpc_dictionary_apply_apply + 68
6 libxpc.dylib 0x19425c9a0 _xpc_dictionary_apply_node_f + 156
7 libxpc.dylib 0x1942600e8 xpc_dictionary_apply + 136
8 Network 0x19acd5210 -[OS_nw_dictionary dealloc] + 112
9 Network 0x19b1beb08 nw_path_release_globals + 120
10 Network 0x19b3d4fa0 nw_settings_child_has_forked() + 312
11 libsystem_pthread.dylib 0x100c8f7c8 _pthread_atfork_child_handlers + 76
12 libsystem_c.dylib 0x1943d9944 fork + 112
(...)
I'm trying to create a child process with boost::process::child
which does basically just a fork()
followed by execv()
and I do it before the - [NSApplication run]
is called.
Is it know bug or behavior which I've run into? Also what is a correct way to spawn child processes in Cocoa applications? As far as my understanding goes the basically all the available APIs (e.g. posix, NSTask) should be more or less the same thing calling the same syscalls. So forking the process early before main run loop starts and not starting another NSApplication in forked child should be ok ...or not?
Combining fork
with Apple’s frameworks is a tricky business [1]. If you only use Posix APIs, fork
should behave reliably. Once you start using our higher-level frameworks, well… we try to keep things working but we can’t make any guarantees.
There are two cases here:
-
Calling
fork
withoutexec*
to create a long-running ‘clone’ of the current process -
Combining
fork
andexec*
to run a new program in a child process
The first case will (usually :-) work if you limit yourself to Posix APIs. It’s fundamentally incompatible with our higher-level frameworks.
For the second case, the best way to avoid problems is to use an API that combines fork
and exec*
. At the BSD level, that’s posix_spawn
. Higher up, you have NSTask
(aka Process
in Swift).
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] There’s a fundamental disconnect between BSD and Mach on this topic, and Apple’s frameworks rely on Mach a lot.