Reputation: 2981
I am on OS X 10.10.5, and Xcode 7.2. I have a program that spawns a child process to run a visualization using OpenGL/GLUT, i.e.
pid_t childpid;
childpid = fork();
if(childpid == 0) { // this is the child process
glutInit(&argc, argv);
...
}
else{ // this is the parent process that does something else
...
}
When I run this in Xcode, the program never returns from glutInit. If instead I use the parent process to do the visualization, and the child to run everything else, i.e.
if(childpid != 0) { // this is the parent process
glutInit(&argc, argv);
...
}
...
then the OpenGL window pops up, and the whole program runs fine. Additionally, if I run the original version (with the child doing the visualization) when building with Makefiles in the terminal outside of Xcode, everything runs fine.
Any ideas on what could cause this?
I haven't been able to debug things very far within Xcode, because the debugger always follows the parent process. If I try to attach to the child process through Debug->Attach to process, I get an error: "Xcode couldn't attach to “J2”. “J2” does not support a debuggable architecture." I found some questions on this last issue of attaching to other processes in xcode, but the solutions didn't work for me.
Upvotes: 1
Views: 282
Reputation: 90531
You can't really do anything on the child side of a fork()
other than some variety of exec()
or _exit()
.
I'll quote the Leopard CoreFoundation Framework Release Notes. I don't know if they're still online, since Apple tends to replace rather than extend the Core Foundation release notes.
CoreFoundation and fork()
Due to the behavior of fork(), CoreFoundation cannot be used on the child-side of fork(). If you fork(), you must follow that with an exec*() call of some sort, and you should not use CoreFoundation APIs within the child, before the exec*(). The applies to all higher-level APIs which use CoreFoundation, and since you cannot know what those higher-level APIs are doing, and whether they are using CoreFoundation APIs, you should not use any higher-level APIs either. This includes use of the daemon() function.
Additionally, per POSIX, only async-cancel-safe functions are safe to use on the child side of fork(), so even use of lower-level libSystem/BSD/UNIX APIs should be kept to a minimum, and ideally to only async-cancel-safe functions.
This has always been true, and there have been notes made of this on various Cocoa developer mailling lists in the past. But CoreFoundation is taking some stronger measures now to "enforce" this limitation, so we thought it would be worthwhile to add a release note to call this out as well. A message is written to stderr when something uses API which is definitely known not to be safe in CoreFoundation after fork(). If file descriptor 2 has been closed, however, you will get no message or notice, which is too bad. We tried to make processes terminate in a very recognizable way, and did for a while and that was very handy, but backwards binary compatibility prevented us from doing so.
In other words, it has never been safe to do much of anything on the child side of a fork()
other than exec a new program.
Besides the general POSIX prohibition, an oft-mentioned explanation is: a) pretty much all Cocoa programs are multithreaded these days, what with GCD and the like. B) when you fork()
, only the calling thread survives into the child process; the other threads just vanish. Since those threads could have been manipulating shared resources, the child process can't rely on having sane state. For example, the malloc()
implementation may have a lock to protect shared structures and that lock could have been held by one of the now-gone threads at the time of the fork()
. So, if the remaining thread tries to allocate memory, it may hang indefinitely. Other shared data structures may simply be in a corrupted state. Etc.
Upvotes: 2