http://bugs.winehq.org/show_bug.cgi?id=60113 Bug ID: 60113 Summary: Deadlock between winex11.drv/Xlib and the OpenGL driver lock hangs multi-threaded GL application Product: Wine Version: 11.13 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: winex11.drv Assignee: wine-bugs@list.winehq.org Reporter: Seth2313@gmail.com Target Milestone: --- Distribution: --- IN PLAIN WORDS Two threads of the same application block each other forever. One of them is inside Wine's X11 code and needs the graphics driver; the other is inside the graphics driver and is waiting for an answer from the X server - an answer that has already arrived, but that only the first thread could pick up. Neither can move, the application hangs for good. The X server itself is perfectly healthy the whole time. SUMMARY Deadlock between winex11.drv/Xlib and the OpenGL driver's internal lock: a multi-threaded application using OpenGL hangs permanently, with the main thread holding an X11-related lock while blocked on the GL driver lock, and a second thread holding the GL driver lock while waiting for an X reply that can no longer be delivered. ENVIRONMENT Wine: wine-staging 11.13 (also seen on 11.9-11.12) Application: FL Studio 2026 (64-bit), Image-Line. OpenGL is loaded dynamically via LoadLibrary("opengl32.dll") GPU driver: NVIDIA 610.43.03 (open modules) Display: XWayland 24.1.13 under KWin 6.7.3 (KDE Plasma Wayland session) Kernel: 7.1.3 (CachyOS), ntsync in use Sync: WINEESYNC=1 WINEFSYNC=1, __GL_THREADED_OPTIMIZATIONS=0 Note that __GL_THREADED_OPTIMIZATIONS=0 was already set, so the NVIDIA threaded-optimization helper thread is NOT involved. SYMPTOM The application freezes completely and can normally only be terminated with "wineserver -k". The X server itself stays healthy the entire time and answers other clients immediately. This has occurred repeatedly over months; the analysis below is from one instance that was caught while still running. MEASURED STATE DURING THE HANG All 137 threads were parked; no thread in state R; zero CPU progress over several seconds. --- Thread A - main thread (TID == PID) --- /proc/PID/task/TID/syscall: 202 (futex) uaddr=0x7f0313fe58a0 op=0x80 (FUTEX_WAIT|PRIVATE) val=2 timeout=NULL pthread_mutex_t at 0x7f0313fe58a0, read from /proc/PID/mem: __lock=2 __count=1 __owner=19561 __nusers=1 __kind=1 (PTHREAD_MUTEX_RECURSIVE) Stack, obtained by scanning $rsp against the process mappings - gdb produces only "?? ()" for Wine processes. Innermost first: libc.so.6 <- libnvidia-glcore <- libGLX_nvidia <- libnvidia-glcore <- libGLX_nvidia <- libGL.so.1 <- libGLX_nvidia <- libGLX.so.0 <- libX11.so.6 <- libGLX.so.0 <- libX11.so.6 <- libGL.so.1 <- winex11.so <- win32u.so <- opengl32.so <- ntdll.so <- opengl32.dll Thread A additionally HELD a second, uncontended lock: 0x7f03e04d6240 __lock=1 __owner=<TID of thread A> __kind=0 (PTHREAD_MUTEX_NORMAL) in an anonymous mapping adjacent to winex11.so. I could not resolve this to a symbol - addresses are process-local and the process has since been restarted. --- Thread B - TID 19561, thread name "SmoothAnimation" --- /proc/PID/task/19561/syscall: 202 (futex) uaddr=0x5555946a1ce4 op=0x189 (FUTEX_WAIT_BITSET|PRIVATE|CLOCK_REALTIME) val=254 timeout=NULL val3=0xffffffff op=0x189 with FUTEX_BITSET_MATCH_ANY is glibc's pthread_cond_wait; the address is in [heap]. Stack: libc.so.6 <- libX11.so.6 <- libc.so.6 <- libGLX_nvidia <- libnvidia-glcore <- libGLX_nvidia <- libGLX.so.0 <- win32u.so <- winex11.so <- win32u.so <- opengl32.so <- ntdll.so <- opengl32.dll Thread B HELD the recursive mutex thread A was waiting for, plus a second recursive mutex at 0x7f023424f2c0 (__lock=1, __count=1) located in the mapping range of libGL.so.1 / libGLX.so.0, i.e. the GLX dispatch lock. --- X connection --- "ss -x -p" on the application's X11 socket showed 200 bytes sitting UNREAD in the receive queue while the process was hung. The X server was fully responsive to other clients (xdpyinfo from another process returned immediately). THE CYCLE Thread A (main): holds X11/winex11-related lock -> blocks acquiring GL driver lock Thread B: holds GL driver lock + GLX lock -> waits inside libX11 for an X reply The reply bytes had already arrived from the X server but nobody consumed them: the thread that would have dispatched them is thread A, which is blocked inside the GL driver. VERIFICATION OF THE DIAGNOSIS Releasing the GL driver mutex on thread B's behalf - writing __lock=__count=__owner=__nusers=0 and issuing FUTEX_WAKE - immediately resolved the hang: thread A acquired the mutex through the normal glibc path, the X socket receive queue drained to 0, thread B woke up, and the application continued running normally for over an hour afterwards. This confirms the cycle above rather than an external X stall. This is of course not a fix, just a demonstration. Thread B then continues without the lock it believes it holds; its later pthread_mutex_unlock fails cleanly with EPERM because glibc checks __owner. ANALYSIS / SUGGESTED DIRECTION winex11.drv appears to call into the GL/GLX stack while holding an internal lock that is also required to dispatch X replies. The GL driver in turn performs an X round trip while holding ITS lock. That is a lock-order inversion between two components that know nothing about each other, so it can only be resolved on the Wine side - e.g. by not holding the display/driver lock across calls into libGL, or by giving GL round trips a separate X connection. NOT AVAILABLE I do not have a minimal reproducer. The hang occurs sporadically in a large commercial application. The diagnostic path above is fully scriptable, though, so anyone hitting a similar hang can confirm the pattern in a few seconds: 1. cat /proc/PID/task/TID/syscall for the main thread -> futex address 2. read 20 bytes at that address from /proc/PID/mem -> __owner gives the holding TID. Works for PTHREAD_MUTEX_RECURSIVE and ERRORCHECK; NORMAL mutexes store no owner. 3. repeat for the holder to follow the chain Requires kernel.yama.ptrace_scope=0. I am happy to test any candidate patch against the workload that triggers this. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.