# vtkXOpenGLRenderWindow::DestroyWindow() can deadlock when Finalize() runs from QVTKRenderWindowInteractor.closeEvent()

**URL:** https://discourse.vtk.org/t/vtkxopenglrenderwindow-destroywindow-can-deadlock-when-finalize-runs-from-qvtkrenderwindowinteractor-closeevent/16515
**Category:** Development
**Tags:** python, code
**Created:** [August 19, 2026, 8:42am UTC](https://discourse.vtk.org/t/vtkxopenglrenderwindow-destroywindow-can-deadlock-when-finalize-runs-from-qvtkrenderwindowinteractor-closeevent/16515 "2026-08-19T08:42:56Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![DungTruog](https://discourse.vtk.org/user_avatar/discourse.vtk.org/dungtruog/32/10725_2.png) [@DungTruog](https://discourse.vtk.org/u/DungTruog)
#### Post date: [August 19, 2026, 8:42am UTC](https://discourse.vtk.org/t/vtkxopenglrenderwindow-destroywindow-can-deadlock-when-finalize-runs-from-qvtkrenderwindowinteractor-closeevent/16515/1 "2026-08-19T08:42:57Z")

</div>

We meet an intermittent SIGSEGV on closing a Qt window that embeds `QVTKRenderWindowInteractor`. Confirmed via a core-dump backtrace and by reading `vtkXOpenGLRenderWindow`: the vendored interactor only finalizes its render window through a fallback wired to the parent’s `destroyed` signal, which fires from `~QObject()` – i.e. _\*after\*_ `~QWidget()` has already torn down the native X11 window. By the time that fallback runs, `MakeCurrent()` calls `glXMakeCurrent(DisplayId, WindowId, ContextId)` against an already-destroyed drawable,

As a workaround, we tried calling `render_window.Finalize()` ourselves, explicitly, early in `closeEvent()`, before Qt’s native teardown runs. That stopped the crash — but it surfaced a new **\*\*hang instead\*\***. Live debugging gdb shows this is a genuine deadlock between our GUI thread inside `vtkXOpenGLRenderWindow::DestroyWindow()` (called from our early `Finalize()`) and a driver-internal thread NVIDIA’s GLX implementation spawns for Present/vsync bookkeeping.

**## Environment**

- VTK 9.6.2 (Python wheel, `vtkmodules`), locally repackaged but as far as we can tell unmodified in `Rendering/OpenGL2` and the vendored Qt interactor — we could read the installed `QVTKRenderWindowInteractor.py` directly and it matches stock upstream in the relevant closeEvent/Finalize path.

- Python 3.8, PySide2 (Qt 5)

- Ubuntu 22.04, X11 (not Wayland), kernel 6.8 (real-time variant)

- NVIDIA RTX 2000 Ada Generation, proprietary driver **\*\*595.71.05\*\*** , CUDA 13.2

- Rendering is on-screen, single `vtkRenderWindow` with two layered `vtkRenderer`s, HDRI/PBR lighting + a tone-mapping pass, embedded via `QVTKRenderWindowInteractor` in a normal (non-Xvfb) desktop session.

**## Symptom 1 (worked around): intermittent SIGSEGV on close**

Root cause, confirmed via a core-dump backtrace and by reading `vtkXOpenGLRenderWindow`: the vendored `QVTKRenderWindowInteractor` only finalizes its render window via the fallback wired to its **\*\*parent’s\*\*** `destroyed` signal, which fires from `~QObject()` — i.e. _\*after\*_ `~QWidget()` has already torn down the native X11 window. `MakeCurrent()` then calls `glXMakeCurrent(DisplayId, WindowId, ContextId)` against an already-destroyed drawable with no validity check on `WindowId` — a use-after-free that segfaults under the NVIDIA driver.

**\*\*Workaround we tried:\*\*** call `render_window.Finalize()` ourselves, explicitly, early in our widget’s `closeEvent()` — before `super().closeEvent()` runs and before Qt schedules `deleteLater()`/`deleteChildren()`. This reliably stopped the crash.

**## Symptom 2 (new, unresolved): intermittent hang on close, after that workaround**

Since applying that workaround, closing the same window sometimes hangs instead of crashing — completely unresponsive, no crash, requiring the desktop’s “application not responding — Force Quit” path (i.e. `SIGKILL`) to recover.

**## Some Questions**

1. Has anyone seen `DestroyWindow()`/`Finalize()` hang (not crash) specifically inside `libnvidia-glcore.so` / `libGLX_nvidia.so`, with a driver-spawned thread stuck in `xcb_wait_for_special_event()`?

2. Is this the same underlying issue as #18637, just manifesting as a hang instead of a block-forever-but-still-“running” state, or a different bug in the same neighborhood?

3. Is there a documented-safe way to ensure no GPU work/Present is in flight before calling `Finalize()`/`DestroyWindow()` (an explicit “wait for idle” that’s safer than `glFinish()`'s own internal behavior seems to be here), so downstream users don’t have to manually track and stop every possible renderer themselves?

4. Should `vtkXOpenGLRenderWindow`'s Qt-embedding docs call out a stronger thread-safety/timing contract for `Finalize()` given it can apparently deadlock against the vendor driver’s own threads?

---

<div class="post-metadata">

### Author: ![sankhesh](https://discourse.vtk.org/user_avatar/discourse.vtk.org/sankhesh/32/73_2.png) [@sankhesh](https://discourse.vtk.org/u/sankhesh)
#### Post date: [September 17, 2026, 6:52pm UTC](https://discourse.vtk.org/t/vtkxopenglrenderwindow-destroywindow-can-deadlock-when-finalize-runs-from-qvtkrenderwindowinteractor-closeevent/16515/2 "2026-09-17T18:52:37Z")

</div>

Hi @DungTruog,

Thank you for the detailed report. Based on the finalize ordering you describe, I would separate the original crash from the later hang: they may share the same teardown boundary, but they are not necessarily the same defect.

> 1. Has anyone seen `DestroyWindow()`/`Finalize()` hang (not crash) specifically inside `libnvidia-glcore.so` / `libGLX_nvidia.so`, with a driver-spawned thread stuck in `xcb_wait_for_special_event()`?

I think that is possible but I’m not sure yet if the deadlock is entirely inside NVIDIA’s driver or whether VTK’s call sequence is the creating a lock cycle with it. It would be useful to see a complete all-thread backtrace from the hung process or at a minimum the nvidia thread backtrace.

> 2. Is this the same underlying issue as #18637, just manifesting as a hang instead of a block-forever-but-still-“running” state, or a different bug in the same neighborhood?

Could be or at the least in the same issue family :).

> 3. Is there a documented-safe way to ensure no GPU work/Present is in flight before calling `Finalize()`/`DestroyWindow()` (an explicit “wait for idle” that’s safer than `glFinish()`'s own internal behavior seems to be here), so downstream users don’t have to manually track and stop every possible renderer themselves?

It is difficult to guarantee that all driver-internal activity has completed before `DestroyWindow`.  
Several distinctions matter:

- `glFinish()` waits for completion of GL commands issued to the current context, but it does not necessarily provide a universal guarantee about every window-system Present operation or every driver-managed auxiliary thread.
- `vtkRenderWindow::WaitForCompletion()` is documented as a rendering-completion wait primarily useful for measurement; it should not be assumed to be a complete X11/GLX teardown barrier.
- Waiting for VTK pipeline execution is not the same as waiting for the window-system compositor or NVIDIA’s Present bookkeeping.
- Calling `Finalize()` from another thread would be unsafe: the OpenGL context, Qt widget, X11 display connection, and VTK render window should remain owned and destroyed on the main thread.

> 4. Should `vtkXOpenGLRenderWindow`'s Qt-embedding docs call out a stronger thread-safety/timing contract for `Finalize()` given it can apparently deadlock against the vendor driver’s own threads?

Yes, the documentation needs to be improved, in general.
