From 626d68ec590d88a07e94ddb652f39482cddf812d Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Tue, 14 Oct 2025 11:14:10 +0200 Subject: [PATCH] Avoids iterator invalidation in QwtPlot item lists When working with a summary ensemble in a Debug build on Windows, a crash happened in RiuQwtPlotWidget::resetPlotCurveHighlighting(). When iterating over list of plot items, one pointer to item pointed to 0xddddddd, a clear indication that the underlying list had been updated during the loop and the iterator invalidated. The change ensures that modifications to the item list during iteration do not cause unexpected behavior, particularly in scenarios involving grid lines and curve highlighting. This is achieved by making a copy of the item list before iteration when modifications may occur. For cases where no add/remove will be performed during the iteration, a comment is added to clarify that it is safe to access the reference to the list. --- .../CorrelationPlots/RimParameterResultCrossPlot.cpp | 1 + .../AnalysisPlots/RiuGroupedBarChartBuilder.cpp | 1 + ApplicationLibCode/UserInterface/RiuPvtPlotWidget.cpp | 1 + .../UserInterface/RiuQwtCurvePointTracker.cpp | 1 + ApplicationLibCode/UserInterface/RiuQwtPlotTools.cpp | 1 + ApplicationLibCode/UserInterface/RiuQwtPlotWidget.cpp | 11 +++++++++-- 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimParameterResultCrossPlot.cpp b/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimParameterResultCrossPlot.cpp index 087f3e0c6e..4e2715896f 100644 --- a/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimParameterResultCrossPlot.cpp +++ b/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimParameterResultCrossPlot.cpp @@ -569,6 +569,7 @@ protected: double minDistance = std::numeric_limits::max(); QString closestCurveLabel; + // Ok to access reference to list, no add/remove of item during iteration for ( QwtPlotItem* item : plot()->itemList() ) { if ( item->rtti() == QwtPlotItem::Rtti_PlotCurve ) diff --git a/ApplicationLibCode/UserInterface/AnalysisPlots/RiuGroupedBarChartBuilder.cpp b/ApplicationLibCode/UserInterface/AnalysisPlots/RiuGroupedBarChartBuilder.cpp index 8778225ca8..88b33b2a15 100644 --- a/ApplicationLibCode/UserInterface/AnalysisPlots/RiuGroupedBarChartBuilder.cpp +++ b/ApplicationLibCode/UserInterface/AnalysisPlots/RiuGroupedBarChartBuilder.cpp @@ -710,6 +710,7 @@ void RiuGroupedBarChartBuilder::addBarChartToPlot( QwtPlot* plot, Qt::Orientatio // Get or create the two grids used in the plot { + // Ok to access reference to list, no add/remove of item during iteration QwtPlotItemList gridList = plot->itemList( QwtPlotItem::Rtti_PlotGrid ); for ( QwtPlotItem* plItem : gridList ) { diff --git a/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.cpp b/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.cpp index 5a6a6e3bf2..c62c1df296 100644 --- a/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.cpp +++ b/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.cpp @@ -424,6 +424,7 @@ const QwtPlotCurve* RiuPvtPlotWidget::closestCurveSample( const QPoint& cursorPo double distMin = HUGE_VAL; int closestPointSampleIndex = -1; + // Ok to access reference to list, no add/remove of item during iteration const QwtPlotItemList& itemList = m_qwtPlot->itemList(); for ( QwtPlotItemIterator it = itemList.begin(); it != itemList.end(); it++ ) { diff --git a/ApplicationLibCode/UserInterface/RiuQwtCurvePointTracker.cpp b/ApplicationLibCode/UserInterface/RiuQwtCurvePointTracker.cpp index ceed8c7331..0da1501466 100644 --- a/ApplicationLibCode/UserInterface/RiuQwtCurvePointTracker.cpp +++ b/ApplicationLibCode/UserInterface/RiuQwtCurvePointTracker.cpp @@ -146,6 +146,7 @@ QPointF RiuQwtCurvePointTracker::closestCurvePoint( const QPoint& cursorPosition double distMin = DBL_MAX; int closestPointSampleIndex = -1; + // Ok to access reference to list, no add/remove of item during iteration const QwtPlotItemList& itmList = m_plot->itemList(); for ( QwtPlotItemIterator it = itmList.begin(); it != itmList.end(); it++ ) { diff --git a/ApplicationLibCode/UserInterface/RiuQwtPlotTools.cpp b/ApplicationLibCode/UserInterface/RiuQwtPlotTools.cpp index 546d3a32c2..929403268b 100644 --- a/ApplicationLibCode/UserInterface/RiuQwtPlotTools.cpp +++ b/ApplicationLibCode/UserInterface/RiuQwtPlotTools.cpp @@ -325,6 +325,7 @@ QList RiuQwtPlotTools::createLegendData( const std::vectoritemList( QwtPlotItem::Rtti_PlotGrid ); for ( QwtPlotItem* plotItem : plotItems ) { diff --git a/ApplicationLibCode/UserInterface/RiuQwtPlotWidget.cpp b/ApplicationLibCode/UserInterface/RiuQwtPlotWidget.cpp index 6121238ecd..cf84cf5634 100644 --- a/ApplicationLibCode/UserInterface/RiuQwtPlotWidget.cpp +++ b/ApplicationLibCode/UserInterface/RiuQwtPlotWidget.cpp @@ -349,6 +349,7 @@ void RiuQwtPlotWidget::setAxisLabelsAndTicksEnabled( RiuPlotAxis axis, bool enab //-------------------------------------------------------------------------------------------------- void RiuQwtPlotWidget::enableGridLines( RiuPlotAxis axis, bool majorGridLines, bool minorGridLines ) { + // NB: Make a copy, not reference to avoid iterator invalidation QwtPlotItemList plotItems = m_plot->itemList( QwtPlotItem::Rtti_PlotGrid ); auto qwtAxis = toQwtPlotAxis( axis ); for ( QwtPlotItem* plotItem : plotItems ) @@ -893,6 +894,7 @@ void RiuQwtPlotWidget::findClosestPlotItem( const QPoint& pos, QwtPlotItem** clo *closestCurveSampleIndex = -1; *distanceFromClick = std::numeric_limits::infinity(); + // Ok to access reference to list, no add/remove of item during iteration const QwtPlotItemList& itmList = m_plot->itemList(); for ( auto it : itmList ) { @@ -1109,6 +1111,7 @@ void RiuQwtPlotWidget::highlightPlotCurves( const std::vector& cu return; } + // NB: Make a copy, not reference to avoid iterator invalidation auto plotItemList = m_plot->itemList(); for ( QwtPlotItem* plotItem : plotItemList ) { @@ -1190,6 +1193,7 @@ void RiuQwtPlotWidget::highlightPlotCurves( const std::vector& cu //-------------------------------------------------------------------------------------------------- void RiuQwtPlotWidget::highlightPlotShapeItems( const std::set& closestItems ) { + // NB: Make a copy, not reference to avoid iterator invalidation auto plotItemList = m_plot->itemList(); for ( QwtPlotItem* plotItem : plotItemList ) { @@ -1238,7 +1242,8 @@ void RiuQwtPlotWidget::resetPlotCurveHighlighting() return; } - const auto& plotItemList = m_plot->itemList(); + // NB: Make a copy, not reference to avoid iterator invalidation + const QwtPlotItemList plotItemList = m_plot->itemList(); for ( QwtPlotItem* plotItem : plotItemList ) { if ( auto* riuPlotCurve = dynamic_cast( plotItem ) ) @@ -1260,7 +1265,8 @@ void RiuQwtPlotWidget::resetPlotCurveHighlighting() //-------------------------------------------------------------------------------------------------- void RiuQwtPlotWidget::resetPlotShapeItemHighlighting() { - const auto& plotItemList = m_plot->itemList(); + // NB: Make a copy, not reference to avoid iterator invalidation + const QwtPlotItemList plotItemList = m_plot->itemList(); for ( QwtPlotItem* plotItem : plotItemList ) { auto* plotShapeItem = dynamic_cast( plotItem ); @@ -1312,6 +1318,7 @@ void RiuQwtPlotWidget::highlightPlotItemsForQwtAxis( QwtAxisId axisId ) { std::vector curves; + // NB: Make a copy, not reference to avoid iterator invalidation auto plotItemList = m_plot->itemList(); for ( QwtPlotItem* plotItem : plotItemList ) {