mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
The GUI thread deadlocked on a non-recursive QMutex during OSDU well import. The download-completion slots and DataLoadController::onTaskFinished held a mutex while triggering progress updates, which call QApplication::processEvents(). That re-enters the event loop and dispatches another queued parquet-download-complete event for a different well, whose handler then blocks trying to lock the same mutex. Release the mutex before emitting taskDone in the OSDU well path and well log data loaders, and before calling incrementProgress() in onTaskFinished. The map access that the mutex protects stays inside the locked scope; the captured ProgressInfo outlives the call (kept alive by blockUntilDone) and incrementProgress() is thread-safe via self-marshaling to the GUI thread. Fixes #14230.
157 lines
6.0 KiB
C++
157 lines
6.0 KiB
C++
//##################################################################################################
|
|
//
|
|
// Custom Visualization Core library
|
|
// Copyright (C) 2011-2013 Ceetron AS
|
|
//
|
|
// This library may be used under the terms of either the GNU General Public License or
|
|
// the GNU Lesser General Public License as follows:
|
|
//
|
|
// GNU General Public License Usage
|
|
// This library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// See the GNU General Public License at <<http://www.gnu.org/licenses/gpl.html>>
|
|
// for more details.
|
|
//
|
|
// GNU Lesser General Public License Usage
|
|
// This library is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation; either version 2.1 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// See the GNU Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
|
|
// for more details.
|
|
//
|
|
//##################################################################################################
|
|
|
|
#include "cafDataLoadController.h"
|
|
|
|
#include "cafDataLoadTask.h"
|
|
#include "cafDataLoader.h"
|
|
#include "cafPdmObject.h"
|
|
#include "cafProgressInfo.h"
|
|
|
|
#include <QApplication>
|
|
#include <QRunnable>
|
|
#include <QThreadPool>
|
|
|
|
using namespace caf;
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
DataLoadController::DataLoadController()
|
|
: m_taskId( 0 )
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
DataLoadController* DataLoadController::instance()
|
|
{
|
|
static DataLoadController* singleton = new DataLoadController;
|
|
return singleton;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void DataLoadController::registerDataLoader( const QString& objectType,
|
|
const QString& dataType,
|
|
std::unique_ptr<DataLoader> dataLoader )
|
|
{
|
|
dataLoader->taskDone.connect( this, &DataLoadController::onTaskFinished );
|
|
|
|
std::pair<QString, QString> key = { objectType, dataType };
|
|
m_dataLoaders[key] = std::move( dataLoader );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void DataLoadController::loadData( caf::PdmObject& object, const QString& dataType, ProgressInfo& progressInfo )
|
|
{
|
|
std::pair<QString, QString> key = { object.classKeyword(), dataType };
|
|
auto it = m_dataLoaders.find( key );
|
|
if ( it != m_dataLoaders.end() )
|
|
{
|
|
QMutexLocker locker( &m_mutex );
|
|
m_pendingTasksByType[dataType]++;
|
|
m_progressInfos[dataType] = &progressInfo;
|
|
locker.unlock();
|
|
|
|
if ( it->second->isRunnable() )
|
|
{
|
|
DataLoadTask* task = new DataLoadTask( *this, *it->second.get(), object, dataType, m_taskId++, progressInfo );
|
|
task->setAutoDelete( true );
|
|
QThreadPool::globalInstance()->start( task );
|
|
}
|
|
else
|
|
{
|
|
it->second->loadData( object, dataType, m_taskId++, progressInfo );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void DataLoadController::blockUntilDone( const QString& dataType )
|
|
{
|
|
int numPending = 0;
|
|
{
|
|
QMutexLocker locker( &m_mutex );
|
|
numPending = m_pendingTasksByType[dataType];
|
|
}
|
|
while ( numPending > 0 )
|
|
{
|
|
{
|
|
QMutexLocker locker( &m_mutex );
|
|
numPending = m_pendingTasksByType[dataType];
|
|
if ( m_progressInfos[dataType]->isCancelled() )
|
|
{
|
|
for ( auto& [key, loader] : m_dataLoaders )
|
|
{
|
|
if ( key.second == dataType )
|
|
{
|
|
loader->cancel();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QApplication::processEvents();
|
|
QThread::msleep( 100 );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void DataLoadController::onTaskFinished( const caf::SignalEmitter* emitter, QString dataType, int taskId )
|
|
{
|
|
ProgressInfo* progressInfo = nullptr;
|
|
{
|
|
QMutexLocker locker( &m_mutex );
|
|
m_pendingTasksByType[dataType]--;
|
|
auto it = m_progressInfos.find( dataType );
|
|
if ( it != m_progressInfos.end() ) progressInfo = it->second;
|
|
}
|
|
|
|
// Update progress after releasing the lock. incrementProgress() calls QApplication::processEvents(),
|
|
// which can re-enter this slot for another finished task. Holding the non-recursive m_mutex across that
|
|
// re-entrant call would deadlock the GUI thread (#14230).
|
|
if ( progressInfo ) progressInfo->incrementProgress();
|
|
}
|