Search code examples
multithreadingmacosopenglglutmacos-sierra

Using GLUT after fork on OSX Sierra


Before Sierra, I used to be able to initialize GLUT on the child process after forking the original process. With the latest version of Sierra, this seems to have changed. The following program crashes with a segmentation fault. If I instead move all the glut functions to the parent process, everything works. Why is there a difference between using the parent/child process?

#include <stdlib.h>
#include <GLUT/glut.h>

void pass(void){
}

int main(int argc, char* argv[]) {
    pid_t childpid;
    childpid = fork();
    if (childpid == 0){
        glutInit(&argc,argv);
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
        glutInitWindowSize(100,100);
        glutCreateWindow("test");
        glutDisplayFunc(pass);
        glGetError();
        glutMainLoop();
    }else{
        sleep(5);
    }
    exit(1);
}

The segmentation fault I get:

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes:       0x0000000000000001, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Illegal instruction: 4
Termination Reason:    Namespace SIGNAL, Code 0x4
Terminating Process:   exc handler [0]

Application Specific Information:
BUG IN CLIENT OF LIBDISPATCH: _dispatch_main_queue_callback_4CF called from the wrong thread
crashed on child side of fork pre-exec

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libdispatch.dylib               0x00007fffe8e7bd21 _dispatch_main_queue_callback_4CF + 1291
1   com.apple.CoreFoundation        0x00007fffd3c7bbe9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
2   com.apple.CoreFoundation        0x00007fffd3c3d00d __CFRunLoopRun + 2205
3   com.apple.CoreFoundation        0x00007fffd3c3c514 CFRunLoopRunSpecific + 420
4   com.apple.Foundation            0x00007fffd57e1c9b -[NSRunLoop(NSRunLoop) limitDateForMode:] + 196
5   com.apple.glut                  0x0000000104f39e93 -[GLUTApplication run] + 321
6   com.apple.glut                  0x0000000104f46b0e glutMainLoop + 279
7   a.out                           0x0000000104f24ed9 main + 121 (main.c:18)
8   libdyld.dylib                   0x00007fffe8ea4255 start + 1

Solution

  • fork() is not creating a thread, it forks the process. After calling fork you have two processes with nearly identical address space contents, but their address spaces are protected from each other. The general rule regarding fork() is, that the only sensisble thing to do after a fork in the child process, is to replace the process image with execve(); doing anything else requires a lot of foresight in the program's design.

    Thread's are created in a different way. I suggest you use the actual threading primitives offered by your programming language of choice.

    That being said, many OSs and GUI libraries want a process' GUI parts to run in the main thread, so that could be part of the reason as well. Also be aware that OpenGL and multithreading is a little bit finicky.