#14306 Fix orphaned progress dialog when importing summary ensembles

Driving caf::ProgressInfo from worker threads inside the OpenMP loop in
loadFileSummaryCaseData() marshaled setProgress() to the GUI thread via a
queued connection. These updates could be delivered after the ProgressInfo
scope had ended and the progress stacks were popped, accessing an empty stack
and resurrecting the just-closed dialog stuck at 0% with no way to close it.

Only update the progress dialog from the master thread, using an atomic
counter for a monotonic progress value, and guard the ProgressInfo mutators
against stale updates delivered after the stacks are empty.
This commit is contained in:
Magne Sjaastad committed 2026-06-25 09:56:22 +02:00
1 parent 2ac1948abe
commit c0059da42c
2 files changed
+24 -3

No files matched your search

@@ -21,6 +21,7 @@
#include "RiaEclipseFileNameTools.h" #include "RiaEclipseFileNameTools.h"
#include "RiaEnsembleNameTools.h" #include "RiaEnsembleNameTools.h"
#include "RiaLogging.h" #include "RiaLogging.h"
#include "RiaOpenMPTools.h"
#include "RiaPreferencesSummary.h" #include "RiaPreferencesSummary.h"
#include "RiaPreferencesSystem.h" #include "RiaPreferencesSystem.h"
#include "RiaQStringFormatter.h" #include "RiaQStringFormatter.h"
@@ -59,6 +60,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <atomic>
#include <memory> #include <memory>
CAF_PDM_SOURCE_INIT( RimSummaryCaseMainCollection, "SummaryCaseCollection" ); CAF_PDM_SOURCE_INIT( RimSummaryCaseMainCollection, "SummaryCaseCollection" );
@@ -583,6 +585,8 @@ void RimSummaryCaseMainCollection::loadFileSummaryCaseData( const std::vector<Ri
[[maybe_unused]] bool canUseMultipleThreads = [[maybe_unused]] bool canUseMultipleThreads =
( prefs->summaryDataReader() != RiaPreferencesSummary::SummaryReaderMode::HDF5_OPM_COMMON ); ( prefs->summaryDataReader() != RiaPreferencesSummary::SummaryReaderMode::HDF5_OPM_COMMON );
std::atomic<int> completedCount = 0;
#pragma omp parallel for schedule( dynamic ) if ( canUseMultipleThreads ) #pragma omp parallel for schedule( dynamic ) if ( canUseMultipleThreads )
for ( int cIdx = 0; cIdx < static_cast<int>( fileSummaryCases.size() ); ++cIdx ) for ( int cIdx = 0; cIdx < static_cast<int>( fileSummaryCases.size() ); ++cIdx )
{ {
@@ -611,7 +615,14 @@ void RimSummaryCaseMainCollection::loadFileSummaryCaseData( const std::vector<Ri
} }
} }
progInfo.setProgress( cIdx ); int completed = ++completedCount;
// Only update progress from the GUI thread; worker-thread updates are marshaled via a
// queued connection and may be delivered after this ProgressInfo scope has ended.
if ( RiaOpenMPTools::currentThreadIndex() == 0 )
{
progInfo.setProgress( completed );
}
} }
for ( const auto& txt : threadSafeLogger.messages() ) for ( const auto& txt : threadSafeLogger.messages() )
{ {
@@ -565,6 +565,9 @@ void ProgressInfoStatic::setProgressDescription( const QString& description )
if ( !isUpdatePossible() ) return; if ( !isUpdatePossible() ) return;
// Ignore stale updates delivered after the owning ProgressInfo scope has ended.
if ( descriptionStack().empty() ) return;
descriptionStack().back() = description; descriptionStack().back() = description;
QProgressDialog* dialog = progressDialog(); QProgressDialog* dialog = progressDialog();
@@ -592,6 +595,9 @@ void ProgressInfoStatic::setProgress( size_t progressValue )
if ( !isUpdatePossible() ) return; if ( !isUpdatePossible() ) return;
// Ignore stale updates delivered after the owning ProgressInfo scope has ended.
if ( progressStack().empty() ) return;
std::vector<size_t>& progressStack_v = progressStack(); std::vector<size_t>& progressStack_v = progressStack();
std::vector<size_t>& progressSpanStack_v = progressSpanStack(); std::vector<size_t>& progressSpanStack_v = progressSpanStack();
std::vector<size_t>& maxProgressStack_v = maxProgressStack(); std::vector<size_t>& maxProgressStack_v = maxProgressStack();
@@ -644,10 +650,12 @@ void ProgressInfoStatic::incrementProgress()
if ( !isUpdatePossible() ) return; if ( !isUpdatePossible() ) return;
// Ignore stale updates delivered after the owning ProgressInfo scope has ended.
if ( progressStack().empty() ) return;
std::vector<size_t>& progressStack_v = progressStack(); std::vector<size_t>& progressStack_v = progressStack();
std::vector<size_t>& progressSpanStack_v = progressSpanStack(); std::vector<size_t>& progressSpanStack_v = progressSpanStack();
CAF_ASSERT( progressStack_v.size() );
ProgressInfoStatic::setProgress( progressStack_v.back() + progressSpanStack_v.back() ); ProgressInfoStatic::setProgress( progressStack_v.back() + progressSpanStack_v.back() );
} }
@@ -667,7 +675,9 @@ void ProgressInfoStatic::setNextProgressIncrement( size_t nextStepSize )
if ( !isUpdatePossible() ) return; if ( !isUpdatePossible() ) return;
CAF_ASSERT( progressSpanStack().size() ); // Ignore stale updates delivered after the owning ProgressInfo scope has ended.
if ( progressSpanStack().empty() ) return;
std::vector<size_t>& maxProgressStack_v = maxProgressStack(); std::vector<size_t>& maxProgressStack_v = maxProgressStack();
std::vector<size_t>& progressStack_v = progressStack(); std::vector<size_t>& progressStack_v = progressStack();