Job improvements (#13895)

* Add job wait/queued state
* Add support for limiting number of jobs running
* Add job queue for jobs waiting to run
This commit is contained in:
JJ
2026-04-17 19:55:50 +02:00
committed by GitHub
parent 7d2d1b831a
commit f1923333af
17 changed files with 493 additions and 67 deletions
@@ -38,6 +38,10 @@ RiaPreferencesOpm::RiaPreferencesOpm()
CAF_PDM_InitFieldNoDefault( &m_opmFlowCommand, "opmFlowCommand", "Path to OPM Flow executable" );
m_opmFlowCommand.uiCapability()->setUiEditorTypeName( caf::PdmUiFilePathEditor::uiEditorTypeName() );
CAF_PDM_InitFieldNoDefault( &m_maxParallelJobs, "maxParallelJobs", "Maximum number of jobs to run in parallel" );
m_maxParallelJobs = 1;
m_maxParallelJobs.setRange( 1, 100 );
CAF_PDM_InitField( &m_useWsl, "useWsl", false, "Use WSL to run OPM Flow" );
CAF_PDM_InitField( &m_useMpi, "useMpi", false, "Enable MPI" );
@@ -89,6 +93,8 @@ void RiaPreferencesOpm::appendItems( caf::PdmUiOrdering& uiOrdering )
opmGrp->add( &m_mpirunCommand );
}
opmGrp->add( &m_maxParallelJobs );
auto cmdGrp = uiOrdering.addNewGroup( "Default Command Line Settings" );
m_jobSettings->uiOrdering( cmdGrp );
}
@@ -173,3 +179,11 @@ RimOpmFlowJobSettings* RiaPreferencesOpm::createDefaultJobSettings() const
{
return m_jobSettings->clone();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RiaPreferencesOpm::maxParallelJobs() const
{
return m_maxParallelJobs();
}
@@ -44,6 +44,7 @@ public:
bool useWsl() const;
bool useMpi() const;
QString mpirunCommand() const;
size_t maxParallelJobs() const;
RimOpmFlowJobSettings* createDefaultJobSettings() const;
@@ -57,6 +58,7 @@ private:
caf::PdmField<bool> m_useMpi;
caf::PdmField<QString> m_mpirunCommand;
caf::PdmChildField<RimOpmFlowJobSettings*> m_jobSettings;
caf::PdmField<size_t> m_maxParallelJobs;
QStringList m_availableWslDists;
};
@@ -4,6 +4,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RicDuplicateJobFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicViewJobLogFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicStopJobFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicStopAllJobsFeature.h
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -12,6 +13,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicDuplicateJobFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicViewJobLogFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicStopJobFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicStopAllJobsFeature.cpp
)
list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
@@ -0,0 +1,71 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RicStopAllJobsFeature.h"
#include "RiaGuiApplication.h"
#include "Jobs/RimGenericJob.h"
#include "Jobs/RimJobCollection.h"
#include "cafSelectionManager.h"
#include <QAction>
#include <QMessageBox>
CAF_CMD_SOURCE_INIT( RicStopAllJobsFeature, "RicStopAllJobsFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicStopAllJobsFeature::onActionTriggered( bool isChecked )
{
if ( auto coll = dynamic_cast<RimJobCollection*>( caf::SelectionManager::instance()->selectedItem() ) )
{
stopAllJobs( coll );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicStopAllJobsFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setIcon( QIcon( ":/stop.svg" ) );
actionToSetup->setText( "Stop..." );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicStopAllJobsFeature::stopAllJobs( RimJobCollection* coll )
{
if ( coll != nullptr )
{
if ( QMessageBox::question( RiaGuiApplication::widgetToUseAsParent(),
"Stop All Jobs",
"Do you want to stop all running and queued jobs?",
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes )
{
for ( auto job : coll->jobs() )
{
if ( job->isRunning() ) job->stop();
}
}
}
}
@@ -0,0 +1,38 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafCmdFeature.h"
class RimJobCollection;
//==================================================================================================
///
//==================================================================================================
class RicStopAllJobsFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
public:
static void stopAllJobs( RimJobCollection* jobCollection );
protected:
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};
@@ -24,6 +24,7 @@
#include "RimJobMonitor.h"
#include "RimProcess.h"
#include "RimProcessQueue.h"
#include "RiuGuiTheme.h"
@@ -40,11 +41,10 @@ CAF_PDM_XML_ABSTRACT_SOURCE_INIT( RimGenericJob, "GenericJob" ); // Do not use.
//--------------------------------------------------------------------------------------------------
RimGenericJob::RimGenericJob()
: m_percentageDone( 0.0 )
, m_lastRunFailed( false )
, m_isRunning( false )
, m_process( nullptr )
, m_jobState( JobState::Idle )
, m_errorsDetected( 0 )
, m_warningsDetected( 0 )
, m_process( nullptr )
{
CAF_PDM_InitObject( "Generic Job" );
}
@@ -94,7 +94,15 @@ QString RimGenericJob::workingDirectory() const
//--------------------------------------------------------------------------------------------------
bool RimGenericJob::isRunning() const
{
return m_isRunning;
return ( m_jobState == JobState::Queued ) || ( m_jobState == JobState::Running );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimGenericJob::JobState RimGenericJob::state() const
{
return m_jobState;
}
//--------------------------------------------------------------------------------------------------
@@ -104,7 +112,7 @@ bool RimGenericJob::stop()
{
if ( !m_process.isNull() )
{
m_process->terminate();
RimProcessQueue::stopProcess( m_process->ID() );
RiaLogging::info( "Job \"" + name() + "\" stopped by user." );
return true;
}
@@ -120,8 +128,11 @@ bool RimGenericJob::execute()
m_errorsDetected = 0;
m_warningsDetected = 0;
m_process = nullptr;
m_percentageDone = 0.0;
m_process = nullptr;
m_jobState = JobState::Idle;
onProgress( m_percentageDone );
// job preparations
{
@@ -131,7 +142,7 @@ bool RimGenericJob::execute()
if ( !onPrepare() )
{
m_lastRunFailed = true;
m_jobState = JobState::Failed;
onProgress( m_percentageDone );
return false;
}
@@ -141,18 +152,19 @@ bool RimGenericJob::execute()
if ( !onRun() ) return false;
QStringList cmdLine = command();
if ( cmdLine.isEmpty() ) return false;
if ( cmdLine.isEmpty() )
{
m_jobState = JobState::Failed;
onProgress( m_percentageDone );
return false;
}
// cannot delete job while running
setDeletable( false );
m_jobState = JobState::Queued;
m_process = new RimProcess( true, new RimJobMonitor( this ) );
m_isRunning = true;
m_lastRunFailed = false;
onProgress( m_percentageDone );
// build process to run
QString cmd = cmdLine.takeFirst();
m_process->setCommand( cmd );
@@ -163,20 +175,10 @@ bool RimGenericJob::execute()
m_process->addEnvironmentVariable( name, value );
}
// run process
bool startOk = m_process->start();
if ( !startOk )
{
onCompleted( false );
m_lastRunFailed = true;
m_isRunning = false;
setDeletable( true );
QMessageBox::critical( RiaGuiApplication::widgetToUseAsParent(),
name(),
"Failed to start job. Check log window for additional information." );
}
RimProcessQueue::queueProcess( m_process );
onProgress( m_percentageDone );
return startOk;
return true;
}
//--------------------------------------------------------------------------------------------------
@@ -184,9 +186,7 @@ bool RimGenericJob::execute()
//--------------------------------------------------------------------------------------------------
bool RimGenericJob::setFinished( bool runOk )
{
m_isRunning = false;
m_lastRunFailed = !runOk;
m_jobState = runOk ? JobState::Completed : JobState::Failed;
m_percentageDone = 100.0;
onProgress( m_percentageDone );
@@ -197,6 +197,15 @@ bool RimGenericJob::setFinished( bool runOk )
return runOk;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimGenericJob::setStarted()
{
m_jobState = RimGenericJob::JobState::Running;
onProgress( m_percentageDone );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -206,22 +215,32 @@ void RimGenericJob::defineObjectEditorAttribute( QString uiConfigName, caf::PdmU
static auto contrastWarnColor =
QColor( RiaColorTools::toQColor( RiaColorTools::contrastColor( cvf::Color3f( cvf::Color3f::DARK_YELLOW ) ) ) );
static auto waitColor = QColor( RiaColorTools::toQColor( cvf::Color3f( cvf::Color3f::LIGHT_GRAY ) ) );
static auto contrastWaitColor =
QColor( RiaColorTools::toQColor( RiaColorTools::contrastColor( cvf::Color3f( cvf::Color3f::LIGHT_GRAY ) ) ) );
if ( auto* treeItemAttribute = dynamic_cast<caf::PdmUiTreeViewItemAttribute*>( attribute ) )
{
if ( m_lastRunFailed )
if ( m_jobState == JobState::Failed )
{
auto txt = m_errorsDetected > 0 ? QString( "[%1]" ).arg( m_errorsDetected ) : "!!!";
auto tag =
caf::PdmUiTreeViewItemAttribute::createTag( QColor( Qt::red ), RiuGuiTheme::getColorByVariableName( "backgroundColor1" ), txt );
treeItemAttribute->tags.push_back( std::move( tag ) );
}
else
else if ( m_jobState == JobState::Queued )
{
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
tag->text = "Waiting...";
tag->bgColor = waitColor;
tag->fgColor = contrastWaitColor;
treeItemAttribute->tags.push_back( std::move( tag ) );
}
else if ( ( m_jobState == JobState::Running ) || ( m_jobState == JobState::Completed ) )
{
if ( ( m_percentageDone == 0.0 ) && ( !m_isRunning ) ) return;
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
if ( m_isRunning )
if ( m_jobState == JobState::Running )
{
tag->text = QString( "%1 %" ).arg( m_percentageDone, 0, 'f', 1 );
}
@@ -258,7 +277,6 @@ void RimGenericJob::defineObjectEditorAttribute( QString uiConfigName, caf::PdmU
const QStringList RimGenericJob::jobLog() const
{
if ( m_process.isNull() ) return QStringList();
return m_process->stdOut();
}
@@ -38,15 +38,27 @@ class RimGenericJob : public RimNamedObject
CAF_PDM_HEADER_INIT;
public:
enum JobState
{
Idle, // default, nothing is happening
Queued, // waiting for available resources to run
Running, // job is running
Completed, // job completed without errors
Failed // job completed with errors
};
RimGenericJob();
~RimGenericJob() override;
bool execute();
bool setFinished( bool runOk );
void setStarted();
bool isRunning() const;
bool stop();
JobState state() const;
double percentageDone() const;
const QStringList jobLog() const;
@@ -72,7 +84,6 @@ protected:
int m_errorsDetected;
private:
bool m_lastRunFailed;
bool m_isRunning;
JobState m_jobState;
caf::PdmPointer<RimProcess> m_process;
};
@@ -128,4 +128,10 @@ void RimJobCollection::deleteAllJobs()
void RimJobCollection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicNewOpmFlowJobFeature";
if ( numberOfRunningJobs() > 0 )
{
menuBuilder.addSeparator();
menuBuilder << "RicStopAllJobsFeature";
}
}
@@ -68,3 +68,16 @@ void RimJobMonitor::finished( int exitCode, QProcess::ExitStatus exitStatus )
RimProcessMonitor::finished( exitCode, exitStatus );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimJobMonitor::started()
{
if ( m_job.notNull() )
{
m_job->setStarted();
}
RimProcessMonitor::started();
}
@@ -35,6 +35,7 @@ public:
protected:
void readyReadStandardOutput() override;
void finished( int exitCode, QProcess::ExitStatus exitStatus ) override;
void started() override;
private:
caf::PdmPointer<RimGenericJob> m_job;
@@ -1,9 +1,13 @@
set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RimProcess.h
${CMAKE_CURRENT_LIST_DIR}/RimProcessMonitor.h
set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimProcess.h
${CMAKE_CURRENT_LIST_DIR}/RimProcessMonitor.h
${CMAKE_CURRENT_LIST_DIR}/RimProcessQueue.h
)
set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimProcess.cpp
${CMAKE_CURRENT_LIST_DIR}/RimProcessMonitor.cpp
set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimProcess.cpp
${CMAKE_CURRENT_LIST_DIR}/RimProcessMonitor.cpp
${CMAKE_CURRENT_LIST_DIR}/RimProcessQueue.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
@@ -29,7 +29,7 @@
CAF_PDM_SOURCE_INIT( RimProcess, "RimProcess" );
int RimProcess::m_nextProcessId = 1;
size_t RimProcess::m_nextProcessId = 1;
//--------------------------------------------------------------------------------------------------
///
@@ -38,7 +38,7 @@ RimProcess::RimProcess( bool logStdOutErr /*true*/, RimProcessMonitor* monitor )
: m_enableLogging( logStdOutErr )
, m_qProcess( nullptr )
{
int defId = m_nextProcessId++;
size_t defId = m_nextProcessId++;
if ( monitor == nullptr )
m_monitor = new RimProcessMonitor( defId, logStdOutErr );
else
@@ -160,9 +160,9 @@ QStringList RimProcess::stdErr() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimProcess::ID() const
size_t RimProcess::ID() const
{
return m_id;
return m_id();
}
//--------------------------------------------------------------------------------------------------
@@ -197,9 +197,9 @@ bool RimProcess::start( bool enableStdOut, bool enableStdErr )
}
m_qProcess->start( m_command, m_arguments );
auto error = m_qProcess->errorString();
if ( !m_qProcess->waitForStarted( -1 ) )
{
auto error = m_qProcess->errorString();
RiaLogging::error( QString( "Failed to start process %1. %2." ).arg( m_id() ).arg( error ) );
return false;
}
@@ -233,6 +233,14 @@ void RimProcess::terminate()
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimProcess::notifyErrorFinish()
{
if ( m_monitor ) m_monitor->finished( -1, QProcess::CrashExit );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -21,6 +21,7 @@
#include "cafPdmField.h"
#include "cafPdmObject.h"
#include <QPointer>
#include <QString>
#include <QStringList>
@@ -51,7 +52,7 @@ public:
QString command() const;
QStringList parameters() const;
int ID() const;
size_t ID() const;
// blocking run
bool execute( bool enableStdOut = true, bool enableStdErr = true );
@@ -60,6 +61,7 @@ public:
bool start( bool enableStdOut = true, bool enableStdErr = true );
void cleanUpAfterRun();
void terminate();
void notifyErrorFinish();
QStringList stdErr() const;
QStringList stdOut() const;
@@ -77,13 +79,13 @@ private:
caf::PdmField<QString> m_command;
QStringList m_arguments;
caf::PdmField<QString> m_description;
caf::PdmField<int> m_id;
caf::PdmField<size_t> m_id;
caf::PdmField<caf::FilePath> m_workDir;
std::vector<std::pair<QString, QString>> m_environmentVariables;
static int m_nextProcessId;
static size_t m_nextProcessId;
RimProcessMonitor* m_monitor;
bool m_enableLogging;
QProcess* m_qProcess;
QPointer<QProcess> m_qProcess;
};
@@ -20,13 +20,15 @@
#include "RiaLogging.h"
#include "RimProcessQueue.h"
#include <QProcess>
#include <QtCore/QtCore>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimProcessMonitor::RimProcessMonitor( int processId, bool logStdOutErr /*true*/ )
RimProcessMonitor::RimProcessMonitor( size_t processId, bool logStdOutErr /*true*/ )
: QObject( nullptr )
, m_processId( processId )
, m_logStdOutErr( logStdOutErr )
@@ -79,20 +81,23 @@ void RimProcessMonitor::error( QProcess::ProcessError error )
//--------------------------------------------------------------------------------------------------
void RimProcessMonitor::finished( int exitCode, QProcess::ExitStatus exitStatus )
{
if ( !m_logStdOutErr ) return;
QString finishStr;
switch ( exitStatus )
if ( m_logStdOutErr )
{
case QProcess::NormalExit:
finishStr = QString( "Normal exit, code %1" ).arg( exitCode );
break;
case QProcess::CrashExit:
default:
finishStr = QString( "Crash exit, code %1" ).arg( exitCode );
break;
QString finishStr;
switch ( exitStatus )
{
case QProcess::NormalExit:
finishStr = QString( "Normal exit, code %1" ).arg( exitCode );
break;
case QProcess::CrashExit:
default:
finishStr = QString( "Crash exit, code %1" ).arg( exitCode );
break;
}
RiaLogging::debug( addPrefix( finishStr ) );
}
RiaLogging::debug( addPrefix( finishStr ) );
RimProcessQueue::onProcessFinished( m_processId );
}
//--------------------------------------------------------------------------------------------------
@@ -175,7 +180,7 @@ QStringList RimProcessMonitor::stdErr() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimProcessMonitor::setProcessId( int processId )
void RimProcessMonitor::setProcessId( size_t processId )
{
m_processId = processId;
}
@@ -28,13 +28,13 @@ class RimProcessMonitor : public QObject
Q_OBJECT
public:
explicit RimProcessMonitor( int processId, bool logStdOutErr = true );
explicit RimProcessMonitor( size_t processId, bool logStdOutErr = true );
void clearStdOutErr();
QStringList stdOut() const;
QStringList stdErr() const;
void setProcessId( int processId );
void setProcessId( size_t processId );
signals:
@@ -47,7 +47,7 @@ public slots:
protected:
QString addPrefix( QString message );
int m_processId;
size_t m_processId;
bool m_logStdOutErr;
QStringList m_stdOut;
QStringList m_stdErr;
@@ -0,0 +1,183 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimProcessQueue.h"
#include "RimProcess.h"
#include "RiaPreferencesOpm.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimProcessQueue::RimProcessQueue()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimProcessQueue* RimProcessQueue::instance()
{
static RimProcessQueue theInstance;
return &theInstance;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimProcessQueue::queueProcess( RimProcess* process )
{
if ( process == nullptr ) return 0;
return instance()->internalQueueProcess( process );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimProcessQueue::stopProcess( size_t processId )
{
instance()->internalStopProcess( processId );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimProcessQueue::onProcessFinished( size_t processId )
{
instance()->internalOnProcessFinished( processId );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimProcessQueue::launchNextProcessIfPossible()
{
if ( m_waitingProcesses.empty() ) return;
if ( m_runningProcesses.size() >= RiaPreferencesOpm::current()->maxParallelJobs() ) return;
auto nextProcess = m_waitingProcesses.front();
m_waitingProcesses.pop_front();
if ( nextProcess->start() )
{
m_runningProcesses.push_back( nextProcess );
}
else
{
nextProcess->notifyErrorFinish();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimProcessQueue::internalQueueProcess( RimProcess* process )
{
QMutexLocker locker( &m_mutex );
m_waitingProcesses.push_back( process );
size_t processId = process->ID();
launchNextProcessIfPossible();
return processId;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimProcessQueue::internalStopProcess( size_t processId )
{
bool isWaitingProcess = false;
RimProcess* theProcess = nullptr;
{
QMutexLocker locker( &m_mutex );
for ( auto proc : m_waitingProcesses )
{
if ( proc->ID() == processId )
{
m_waitingProcesses.remove( proc );
theProcess = proc;
isWaitingProcess = true;
break;
}
}
if ( !isWaitingProcess )
{
for ( auto proc : m_runningProcesses )
{
if ( proc->ID() == processId )
{
theProcess = proc;
m_runningProcesses.remove( proc );
break;
}
}
}
launchNextProcessIfPossible();
}
if ( theProcess != nullptr )
{
if ( isWaitingProcess )
{
theProcess->notifyErrorFinish();
}
else
{
theProcess->terminate();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimProcessQueue::internalOnProcessFinished( size_t processId )
{
QMutexLocker locker( &m_mutex );
bool isWaitingProcess = false;
for ( auto proc : m_waitingProcesses )
{
if ( proc->ID() == processId )
{
m_waitingProcesses.remove( proc );
isWaitingProcess = true;
break;
}
}
if ( !isWaitingProcess )
{
for ( auto proc : m_runningProcesses )
{
if ( proc->ID() == processId )
{
m_runningProcesses.remove( proc );
break;
}
}
}
launchNextProcessIfPossible();
}
@@ -0,0 +1,48 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include <list>
#include <QMutex>
class RimProcess;
class RimProcessQueue
{
public:
static size_t queueProcess( RimProcess* process );
static void stopProcess( size_t processId );
static void onProcessFinished( size_t processId );
protected:
RimProcessQueue();
static RimProcessQueue* instance();
size_t internalQueueProcess( RimProcess* process );
void internalOnProcessFinished( size_t processId );
void internalStopProcess( size_t processId );
private:
void launchNextProcessIfPossible();
std::list<RimProcess*> m_waitingProcesses;
std::list<RimProcess*> m_runningProcesses;
QMutex m_mutex;
};