// Compile with -std=c++11 -lGL -lX11. // Add -DTEST2 to reproduce the crash after letting a thread exit with a // current context. #define GL_GLEXT_PROTOTYPES 1 #include #include #include #include #include #include #include namespace { void f(Display * const display, Window window, GLXContext context) { if (glXMakeCurrent(display, window, context) != True) { printf("Failed to make context current on second thread.\n"); exit(1); } PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddress( reinterpret_cast("glXSwapIntervalMESA")); if (!glXSwapIntervalMESA) { printf("Failed to get address of glXSwapIntervalMESA.\n"); exit(1); } if (glXSwapIntervalMESA(0) != 0) { printf("Failed to set swap interval.\n"); exit(1); } } // Check if a GL or GLX style extension list contains an extension. // The extension must begin at the start of the list, or follow a space. // The extension must end at the end of the list, or preceed a space. bool has_extension(const char * const extensions, const char * const extension) { const std::size_t extensionLength = std::strlen(extension); const char * pos = extensions; while (true) { pos = std::strstr(pos, extension); if (pos == nullptr) return false; if ((pos == extensions || *(pos - 1) == ' ') && (*(pos + extensionLength) == ' ' || *(pos + extensionLength) == '\0')) return true; pos = std::strchr(pos, ' '); } } // unnamed namespace } int main(int argc, char* argv[]) { if (XInitThreads() == 0) { printf("XInitThreads() failed.\n"); return 1; } Display *display = XOpenDisplay(NULL); if (!display) { printf("Failed to open X display\n"); return 1; } const char * const glx_extensions = glXQueryExtensionsString(display, 0); if (!has_extension(glx_extensions, "GLX_MESA_multithread_makecurrent")) { printf("GLX_MESA_multithread_makecurrent not present.\n"); return 1; } if (!has_extension(glx_extensions, "GLX_MESA_swap_control")) { printf("GLX_MESA_swap_control not present.\n"); return 1; } // Get a matching FB config static int visual_attribs[] = { GLX_X_RENDERABLE , True, GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, GLX_RENDER_TYPE , GLX_RGBA_BIT, GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, GLX_RED_SIZE , 8, GLX_GREEN_SIZE , 8, GLX_BLUE_SIZE , 8, GLX_ALPHA_SIZE , 8, GLX_DEPTH_SIZE , 24, GLX_STENCIL_SIZE , 8, GLX_DOUBLEBUFFER , True, None }; int glx_major, glx_minor; // FBConfigs were added in GLX version 1.3. if (!glXQueryVersion(display, &glx_major, &glx_minor) || ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) { printf("Invalid GLX version"); return 1; } int fbcount; GLXFBConfig* fbc = glXChooseFBConfig(display, DefaultScreen(display), visual_attribs, &fbcount); if (!fbc) { printf("Failed to retrieve a framebuffer config\n"); return 1; } GLXFBConfig bestFbc = fbc[0]; XFree(fbc); // Get a visual XVisualInfo *vi = glXGetVisualFromFBConfig(display, bestFbc); XSetWindowAttributes swa; Colormap colourmap; swa.colormap = colourmap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone); swa.background_pixmap = None ; swa.border_pixel = 0; swa.event_mask = StructureNotifyMask; Window window = XCreateWindow( display, RootWindow(display, vi->screen), 0, 0, 100, 100, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); if (!window) { printf("Failed to create window\n"); return 1; } XFree(vi); XMapWindow(display, window); GLXContext context = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True); if (!context) { printf("Failed to create an OpenGL context\n"); return 1; } XSync(display, False); #if !defined(TEST2) if (glXMakeCurrent(display, window, context) != True) { printf("Failed to make context current on main thread.\n"); exit(1); } #endif // crashes in f on the new thread if TEST2 is undefined std::thread t(f, display, window, context); t.join(); if (glXMakeCurrent(display, window, context) != True) { printf("Failed to make context current on main thread.\n"); exit(1); } // Crashes in here if TEST2 is defined. f(display, window, context); glXMakeCurrent(display, 0, 0); glXDestroyContext(display, context); XDestroyWindow(display, window); XFreeColormap(display, colourmap); XCloseDisplay(display); return 0; }