glfwCreateWindow crashes in separate thread which also called glfwInit


#1

Following the advice of this thread, I am trying to initialize GLFW and handle all rendering in a separate thread while computation is performed on the main thread. Several threads I’ve found say that functions like glfwCreateWindow can be called from any thread as long as that thread also called glfwInit (source). Yet when I call glfwCreateWindow() on this second thread, I get a variety of colorful error messages including “[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread”. A minimal example looks like this:

void myfunc() {
    if( !glfwInit() )
    {
        fprintf( stderr, "Failed to initialize GLFW\n" );
        getchar();
    }

    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //We don't want the old OpenGL

    GLFWwindow * window = glfwCreateWindow(1024, 768, "My Window", NULL, NULL);

    return;
}

int main() {
    std::thread foo(myfunc);
    foo.join();
    return 0;
}

Do you know why this is? Is there any way I can do all OpenGL/GLFW handling in this foo thread instead of the main one?

For reference, I am running Mac OS Sierra 10.12.6 with GLFW 3.2.1 on a 2015 Macbook Pro.


#2

The full traceback for the sample code is

> 2018-06-08 19:16:30.594 graphics[25934:1666687] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1349.91/Misc.subproj/NSUndoManager.m:363
> 2018-06-08 19:16:30.595 graphics[25934:1666687] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.'
> *** First throw call stack:
> (
> 	0   CoreFoundation                      0x00007fffbd10e2cb __exceptionPreprocess + 171
> 	1   libobjc.A.dylib                     0x00007fffd1f2848d objc_exception_throw + 48
> 	2   CoreFoundation                      0x00007fffbd113042 +[NSException raise:format:arguments:] + 98
> 	3   Foundation                          0x00007fffbeb5bbe0 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
> 	4   Foundation                          0x00007fffbeae6093 +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 170
> 	5   AppKit                              0x00007fffbab724ed -[NSApplication run] + 1200
> 	6   libglfw.3.dylib                     0x0000000106890f7c initializeAppKit + 1420
> 	7   libglfw.3.dylib                     0x0000000106890641 _glfwPlatformCreateWindow + 35
> 	8   libglfw.3.dylib                     0x000000010688c430 glfwCreateWindow + 487
> 	9   graphics                            0x00000001067cb85b _Z6myfuncv + 91
> 	10  graphics                            0x00000001067ccef5 _ZNSt3__114__thread_proxyINS_5tupleIJNS_10unique_ptrINS_15__thread_structENS_14default_deleteIS3_EEEEPFvvEEEEEEPvSA_ + 517
> 	11  libsystem_pthread.dylib             0x00007fffd2a2793b _pthread_body + 180
> 	12  libsystem_pthread.dylib             0x00007fffd2a27887 _pthread_body + 0
> 	13  libsystem_pthread.dylib             0x00007fffd2a2708d thread_start + 13
> )
> libc++abi.dylib: terminating with uncaught exception of type NSException

#3

On macOS you cannot do this from thread. Read this comment: Multithreading GLFW?

So you need to do window creation and event processing on main thread.


#4

As per @mmozeiko’s answer the stackoverflow answer about calling init and create from any thread so long as it was the same was incorrect, indeed the question refers to an older API and FAQ. I’ve posted a reply to help clarify (I hope it does, let me know if it’s not sufficiently comprehensible).

In case further clarification is needed, create your window on the main thread and do event processing there. Then on your other thread make the OpenGL context current with glfwMakeContextCurrent and call OpenGL calls from there.