glslang/shim: popAll the calling thread's TPoolAllocator at exit
Yesterday's atexit hook (5e21396f2) called glslang::FinalizeProcess
but heaptrack on the post-fix binary showed identical 24.44 MB
leaked — the ~12 MB rooted in glslang::TPoolAllocator::allocate
was unchanged. Tracing the heaptrack stack revealed the leak's
TLS owner is the GUI thread (not a renderer thread):
TPoolAllocator::allocate
... glslang internals ...
shadertoy.spirvFromGlsl
shadertoy.loadFromFile (per-surface init)
generic.Renderer.initShaders
Surface.init
ghostty_surface_new
GhosttySurface::GhosttySurface
MainWindow::newTab ← GUI thread
ghostty_surface_new runs glslang synchronously from
MainWindow::newTab, so the pool pages accumulate on the GUI
thread's TLS. The GUI thread doesn't exit until process exit,
and FinalizeProcess only frees the SharedSymbolTables — NOT the
calling thread's pool. Pages persist to process termination.
Fix: call glslang::GetThreadPoolAllocator().popAll() inside
ghastty_glslang_finalize_process, before FinalizeProcess. popAll
is the documented release-all method on TPoolAllocator and frees
the pages back to the system allocator. Safe at atexit because
every renderer thread has joined (ThreadState.cleanup ran via
Vulkan.threadExit on each), the SPV cache was just cleared, and
FinalizeProcess doesn't reach into per-thread pool state.
Co-Authored-By: claude-flow <ruv@ruv.net>
pull/12846/head
parent
5e21396f27
commit
1c2c5760b7
|
|
@ -9,6 +9,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <glslang/Include/PoolAlloc.h>
|
||||
#include <glslang/Public/ShaderLang.h>
|
||||
#include <glslang/Public/ResourceLimits.h>
|
||||
#include <SPIRV/GlslangToSpv.h>
|
||||
|
|
@ -248,10 +249,25 @@ extern "C" void ghastty_glslang_finalize_process(void) {
|
|||
std::lock_guard<std::mutex> lg(spv_cache_mutex());
|
||||
spv_cache().clear();
|
||||
}
|
||||
// Release glslang's process-wide state: the thread-local
|
||||
// TPoolAllocator pages that accumulated to their high-water mark
|
||||
// on the first surface's compiles + any per-thread bookkeeping.
|
||||
// Matches the implicit InitializeProcess on first use (or the
|
||||
// explicit C-API glslang_initialize_process in pkg/glslang/init.zig).
|
||||
// Free this thread's TPoolAllocator pages. heaptrack pointed
|
||||
// the ~12 MB glslang leak at TPoolAllocator::allocate calls
|
||||
// rooted in shadertoy.spirvFromGlsl on the GUI thread (since
|
||||
// ghostty_surface_new runs glslang synchronously from
|
||||
// MainWindow::newTab) — that pool's pages persist until thread
|
||||
// exit, but the GUI thread doesn't exit until process
|
||||
// termination. glslang::FinalizeProcess only frees the
|
||||
// process-wide SharedSymbolTables, NOT this pool. Call popAll()
|
||||
// explicitly to release the pages back to the system allocator.
|
||||
//
|
||||
// Safe here because (a) we're called from atexit, every render
|
||||
// thread has joined via Vulkan.threadExit (which also runs its
|
||||
// own popAll-equivalent via ThreadState.cleanup); (b) the SPV
|
||||
// cache was cleared above, so no compiled blob references the
|
||||
// pool; (c) FinalizeProcess below won't reach into this pool
|
||||
// either.
|
||||
glslang::GetThreadPoolAllocator().popAll();
|
||||
|
||||
// Release glslang's process-wide shared state (the version-
|
||||
// indexed SharedSymbolTables built at first compile).
|
||||
glslang::FinalizeProcess();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue