From f25d2a585ab68809bc7a2a9494dd7c04b64716b0 Mon Sep 17 00:00:00 2001 From: Alessandro Genova Date: Mon, 29 Jun 2026 11:37:58 -0400 Subject: [PATCH 1/2] fix crash from VolumeData unregistering its color-map proxy on the worker thread --- tomviz/pipeline/data/VolumeData.cxx | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/tomviz/pipeline/data/VolumeData.cxx b/tomviz/pipeline/data/VolumeData.cxx index 714faafa7..a3ad0c9e1 100644 --- a/tomviz/pipeline/data/VolumeData.cxx +++ b/tomviz/pipeline/data/VolumeData.cxx @@ -24,7 +24,10 @@ #include #include +#include #include +#include +#include #include @@ -70,9 +73,31 @@ VolumeData::~VolumeData() // unique, counter-based name. The proxy manager holds its own reference, // so dropping our vtkSmartPointer isn't enough — without this unregister // the proxy lives until app exit and every load/reset cycle adds another. - if (m_colorMap) { + if (!m_colorMap) { + return; + } + + // UnRegisterProxy mutates the ParaView session proxy manager, which is + // not thread-safe and must run on the GUI thread. A VolumeData is a port + // payload whose lifetime is governed by shared_ptr refcounting, and the + // pipeline executor can drop the last ref on the worker thread (inflight + // eviction, clearHandle, the OnDisk deleter) — destroying us there. + // Touching the proxy manager off the GUI thread races + // pqServerManagerObserver / pqServerManagerModel and crashes in + // onProxyRegistered. Marshal the unregister to the GUI thread; the proxy + // manager's own reference keeps the captured proxy alive until the queued + // call runs. + vtkSmartPointer proxy = m_colorMap; + auto unregister = [proxy]() { vtkNew controller; - controller->UnRegisterProxy(m_colorMap); + controller->UnRegisterProxy(proxy); + }; + + auto* app = QCoreApplication::instance(); + if (!app || QThread::currentThread() == app->thread()) { + unregister(); + } else { + QMetaObject::invokeMethod(app, unregister, Qt::QueuedConnection); } } From 1ee518a0f545841b0aa51fdd8dbf2c73635eed43 Mon Sep 17 00:00:00 2001 From: Alessandro Genova Date: Mon, 29 Jun 2026 12:18:18 -0400 Subject: [PATCH 2/2] keep downstream visualization on screen while inserting an operator --- tomviz/TransformUtils.cxx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tomviz/TransformUtils.cxx b/tomviz/TransformUtils.cxx index 0e3359daa..60f205dd7 100644 --- a/tomviz/TransformUtils.cxx +++ b/tomviz/TransformUtils.cxx @@ -168,6 +168,15 @@ static pipeline::DeferredLinkInfo appendTransformAtPortDeferred( deferred.linksToRestore.append({ targetPort, sinkInput }); pip->removeLink(m.link); pip->createLink(m.newOut, sinkInput); + // removeLink() reset this terminal sink/group's visualization. Re-show it + // (the VTK objects still hold the last data) so the downstream module + // stays on screen while the dialog is open. Nothing clears it again until + // the not-yet-run transform produces output and the sink swaps it in + // directly, so it also stays visible all through execution. Mirrors the + // restorePresentation() the dialog's cancel path performs. + if (auto* node = sinkInput->node()) { + node->restorePresentation(); + } } return deferred; }