From 67ad0ef4bb4605004cc016760940681145aaf86d Mon Sep 17 00:00:00 2001 From: Alessandro Genova Date: Thu, 25 Jun 2026 00:45:39 -0400 Subject: [PATCH] fix reentrant histogram LUT rebuild crashing color-map rescale --- tomviz/HistogramWidget.cxx | 32 +++++++++++++++++++++++++------- tomviz/HistogramWidget.h | 4 ++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/tomviz/HistogramWidget.cxx b/tomviz/HistogramWidget.cxx index 7b141549e..4b1ec5d6c 100644 --- a/tomviz/HistogramWidget.cxx +++ b/tomviz/HistogramWidget.cxx @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -315,16 +316,33 @@ void HistogramWidget::onColorFunctionChanged() // Avoid infinite recursion return; } - m_updatingColorFunction = true; - updateLUTProxy(); - if (m_LUT) { - m_LUT->Build(); - renderViews(); - emit colorMapUpdated(); + // This slot is wired to the color transfer function's ModifiedEvent, which + // can fire reentrantly from deep inside another mutation of the *same* + // function -- e.g. VolumeData::rescaleColorMap() -> + // vtkSMProxy::UpdateVTKObjects() -> vtkColorTransferFunction::UpdateRange(). + // Rebuilding the LUT here (Build() -> SetAnnotations()) while that outer + // mutation is still on the stack corrupts the function's internal arrays and + // crashes in ~vtkVariant. Defer the rebuild to the next event-loop turn so + // it runs after the mutation unwinds; the pending flag coalesces the + // multi-Hz churn from live operator updates. + if (m_colorFunctionUpdatePending) { + return; } + m_colorFunctionUpdatePending = true; + QTimer::singleShot(0, this, [this]() { + m_colorFunctionUpdatePending = false; + m_updatingColorFunction = true; + + updateLUTProxy(); + if (m_LUT) { + m_LUT->Build(); + renderViews(); + emit colorMapUpdated(); + } - m_updatingColorFunction = false; + m_updatingColorFunction = false; + }); } void HistogramWidget::onScalarOpacityFunctionChanged() diff --git a/tomviz/HistogramWidget.h b/tomviz/HistogramWidget.h index 518968523..40e6c09d1 100644 --- a/tomviz/HistogramWidget.h +++ b/tomviz/HistogramWidget.h @@ -133,6 +133,10 @@ public slots: // To prevent infinite recursion... bool m_updatingColorFunction = false; + // Coalesces the deferred color-function rebuild (see + // onColorFunctionChanged) so reentrant ModifiedEvents don't pile up. + bool m_colorFunctionUpdatePending = false; + static const int m_defaultAutoContrastThreshold = 5000; int m_currentAutoContrastThreshold = 5000; };