You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
6.6 KiB
C++

/* === This file is part of Calamares - <https://calamares.io> ===
*
[libcalamares] Update SPDX identifiers. Update CppJob.h Update CalamaresConfig.h.in Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.cpp Update CalamaresConfig.h.in Update CppJob.cpp Update CppJob.h Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.h Update ProcessJob.cpp Update ProcessJob.h Update PythonHelper.cpp Update PythonJob.cpp Update PythonJob.h Update PythonHelper.h Update PythonJobApi.cpp Update PythonJobApi.h Update Settings.cpp Update Settings.h Update GeoIPJSON.cpp Update GeoIPJSON.h Update GeoIPTests.cpp Update GeoIPTests.h Update GeoIPXML.cpp Update GeoIPXML.h Update Handler.cpp Update Handler.h Update Interface.cpp Update Interface.h Update test_geoip.cpp Update CountryData_p.cpp Update Label.cpp Update Label.h Update LabelModel.cpp Update LabelModel.h Update CountryData_p.cpp Update CountryData_p.cpp Update Lookup.cpp Update Lookup.h Update Tests.cpp Update Tests.h Update TimeZone.cpp Update TimeZone.h Update TranslatableConfiguration.cpp Update TranslatableConfiguration.h Update ZoneData_p.cxxtr Update cldr-extractor.py Update zone-extractor.py Update Actions.h Update Actions.h Update Descriptor.h Update InstanceKey.cpp Update Module.cpp Update Module.h Update Requirement.cpp Update RequirementsChecker.h Update RequirementsModel.cpp Update RequirementsModel.h Update Tests.cpp Update Manager.cpp Update Manager.h Update Tests.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update KPMTests.cpp Update FileSystem.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update Mount.cpp Update Mount.h Update PartitionIterator.cpp Update PartitionIterator.h Update PartitionIterator.h Update PartitionQuery.cpp Update PartitionQuery.h Update PartitionSize.cpp Update PartitionSize.h Update Sync.cpp Update Sync.h Update Tests.cpp Update Tests.h Update BoostPython.h Update CalamaresUtilsSystem.cpp Update CalamaresUtilsSystem.h Update CommandList.cpp Update CommandList.h Update Dirs.cpp Update Dirs.h Update Entropy.cpp Update Entropy.h Update Entropy.cpp Update Logger.cpp Update Logger.h Update NamedEnum.h Update NamedSuffix.h Update PluginFactory.cpp Update PluginFactory.h Update RAII.h Update RAII.h Update Retranslator.cpp Update Retranslator.h Update String.cpp Update String.h Update TestPaths.cpp Update Tests.cpp Update Tests.h Update UMask.cpp Update UMask.h Update Units.h Update Variant.cpp Update Variant.h Update Yaml.cpp Update Yaml.h Update moc-warnings.h
6 years ago
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
[libcalamares] Update SPDX identifiers. Update CppJob.h Update CalamaresConfig.h.in Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.cpp Update CalamaresConfig.h.in Update CppJob.cpp Update CppJob.h Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.h Update ProcessJob.cpp Update ProcessJob.h Update PythonHelper.cpp Update PythonJob.cpp Update PythonJob.h Update PythonHelper.h Update PythonJobApi.cpp Update PythonJobApi.h Update Settings.cpp Update Settings.h Update GeoIPJSON.cpp Update GeoIPJSON.h Update GeoIPTests.cpp Update GeoIPTests.h Update GeoIPXML.cpp Update GeoIPXML.h Update Handler.cpp Update Handler.h Update Interface.cpp Update Interface.h Update test_geoip.cpp Update CountryData_p.cpp Update Label.cpp Update Label.h Update LabelModel.cpp Update LabelModel.h Update CountryData_p.cpp Update CountryData_p.cpp Update Lookup.cpp Update Lookup.h Update Tests.cpp Update Tests.h Update TimeZone.cpp Update TimeZone.h Update TranslatableConfiguration.cpp Update TranslatableConfiguration.h Update ZoneData_p.cxxtr Update cldr-extractor.py Update zone-extractor.py Update Actions.h Update Actions.h Update Descriptor.h Update InstanceKey.cpp Update Module.cpp Update Module.h Update Requirement.cpp Update RequirementsChecker.h Update RequirementsModel.cpp Update RequirementsModel.h Update Tests.cpp Update Manager.cpp Update Manager.h Update Tests.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update KPMTests.cpp Update FileSystem.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update Mount.cpp Update Mount.h Update PartitionIterator.cpp Update PartitionIterator.h Update PartitionIterator.h Update PartitionQuery.cpp Update PartitionQuery.h Update PartitionSize.cpp Update PartitionSize.h Update Sync.cpp Update Sync.h Update Tests.cpp Update Tests.h Update BoostPython.h Update CalamaresUtilsSystem.cpp Update CalamaresUtilsSystem.h Update CommandList.cpp Update CommandList.h Update Dirs.cpp Update Dirs.h Update Entropy.cpp Update Entropy.h Update Entropy.cpp Update Logger.cpp Update Logger.h Update NamedEnum.h Update NamedSuffix.h Update PluginFactory.cpp Update PluginFactory.h Update RAII.h Update RAII.h Update Retranslator.cpp Update Retranslator.h Update String.cpp Update String.h Update TestPaths.cpp Update Tests.cpp Update Tests.h Update UMask.cpp Update UMask.h Update Units.h Update Variant.cpp Update Variant.h Update Yaml.cpp Update Yaml.h Update moc-warnings.h
6 years ago
*
*/
#include "JobQueue.h"
#include "CalamaresConfig.h"
#include "GlobalStorage.h"
#include "Job.h"
#include "utils/Logger.h"
#include <QMutex>
#include <QMutexLocker>
#include <QThread>
namespace Calamares
{
struct WeightedJob
{
/** @brief Cumulative weight **before** this job starts
*
* This is calculated as jobs come in.
*/
qreal cumulative = 0.0;
/** @brief Weight of the job within the module's jobs
*
* When a list of jobs is added from a particular module,
* the jobs are weighted relative to that module's overall weight
* **and** the other jobs in the list, so that each job
* gets its share:
* ( job-weight / total-job-weight ) * module-weight
*/
qreal weight = 0.0;
job_ptr job;
};
using WeightedJobList = QList< WeightedJob >;
class JobThread : public QThread
{
public:
JobThread( JobQueue* queue )
: QThread( queue )
, m_queue( queue )
, m_jobIndex( 0 )
{
}
virtual ~JobThread() override;
void finalize()
{
Q_ASSERT( m_runningJobs->isEmpty() );
QMutexLocker qlock( &m_enqueMutex );
QMutexLocker rlock( &m_runMutex );
std::swap( m_runningJobs, m_queuedJobs );
m_overallQueueWeight
= m_runningJobs->isEmpty() ? 0.0 : ( m_runningJobs->last().cumulative + m_runningJobs->last().weight );
if ( m_overallQueueWeight < 1 )
{
m_overallQueueWeight = 1.0;
}
}
void enqueue( int moduleWeight, const JobList& jobs )
{
QMutexLocker qlock( &m_enqueMutex );
qreal cumulative
= m_queuedJobs->isEmpty() ? 0.0 : ( m_queuedJobs->last().cumulative + m_queuedJobs->last().weight );
qreal totalJobWeight
= std::accumulate( jobs.cbegin(), jobs.cend(), qreal( 0.0 ), []( qreal total, const job_ptr& j ) {
return total + j->getJobWeight();
} );
if ( totalJobWeight < 1 )
{
totalJobWeight = 1.0;
}
for ( const auto& j : jobs )
{
qreal jobContribution = ( j->getJobWeight() / totalJobWeight ) * moduleWeight;
m_queuedJobs->append( WeightedJob { cumulative, jobContribution, j } );
cumulative += jobContribution;
}
}
void run() override
{
QMutexLocker rlock( &m_runMutex );
bool failureEncountered = false;
QString message; ///< Filled in with errors
QString details;
m_jobIndex = 0;
for ( const auto& jobitem : *m_runningJobs )
{
if ( failureEncountered && !jobitem.job->isEmergency() )
{
cDebug() << "Skipping non-emergency job" << jobitem.job->prettyName();
}
else
{
emitProgress( 0.0 ); // 0% for *this job*
cDebug() << "Starting" << ( failureEncountered ? "EMERGENCY JOB" : "job" ) << jobitem.job->prettyName()
<< '(' << ( m_jobIndex + 1 ) << '/' << m_runningJobs->count() << ')';
connect( jobitem.job.data(), &Job::progress, this, &JobThread::emitProgress );
auto result = jobitem.job->exec();
if ( !failureEncountered && !result )
{
// so this is the first failure
failureEncountered = true;
message = result.message();
details = result.details();
}
QThread::msleep( 16 ); // Very brief rest before reporting the job as complete
emitProgress( 1.0 ); // 100% for *this job*
}
m_jobIndex++;
}
if ( failureEncountered )
{
QMetaObject::invokeMethod(
m_queue, "failed", Qt::QueuedConnection, Q_ARG( QString, message ), Q_ARG( QString, details ) );
}
else
{
emitProgress( 1.0 );
}
QMetaObject::invokeMethod( m_queue, "finish", Qt::QueuedConnection );
}
void emitProgress( qreal percentage ) const
{
percentage = qBound( 0.0, percentage, 1.0 );
QString message;
qreal progress = 0.0;
if ( m_jobIndex < m_runningJobs->count() )
{
const auto& jobitem = m_runningJobs->at( m_jobIndex );
progress = ( jobitem.cumulative + jobitem.weight * percentage ) / m_overallQueueWeight;
message = jobitem.job->prettyStatusMessage();
}
else
{
progress = 1.0;
message = tr( "Done" );
}
QMetaObject::invokeMethod(
m_queue, "progress", Qt::QueuedConnection, Q_ARG( qreal, progress ), Q_ARG( QString, message ) );
}
private:
QMutex m_runMutex;
QMutex m_enqueMutex;
std::unique_ptr< WeightedJobList > m_runningJobs = std::make_unique< WeightedJobList >();
std::unique_ptr< WeightedJobList > m_queuedJobs = std::make_unique< WeightedJobList >();
JobQueue* m_queue;
int m_jobIndex = 0; ///< Index into m_runningJobs
qreal m_overallQueueWeight = 0.0; ///< cumulation when **all** the jobs are done
};
JobThread::~JobThread() {}
JobQueue* JobQueue::s_instance = nullptr;
JobQueue*
JobQueue::instance()
{
if ( !s_instance )
{
cWarning() << "Getting nullptr JobQueue instance.";
}
return s_instance;
}
JobQueue::JobQueue( QObject* parent )
: QObject( parent )
, m_thread( new JobThread( this ) )
, m_storage( new GlobalStorage( this ) )
{
Q_ASSERT( !s_instance );
s_instance = this;
}
JobQueue::~JobQueue()
{
if ( m_thread->isRunning() )
{
m_thread->terminate();
if ( !m_thread->wait( 300 ) )
{
cError() << "Could not terminate job thread (expect a crash now).";
}
delete m_thread;
}
delete m_storage;
}
void
JobQueue::start()
{
Q_ASSERT( !m_thread->isRunning() );
m_thread->finalize();
m_finished = false;
m_thread->start();
}
void
JobQueue::enqueue( int moduleWeight, const JobList& jobs )
{
Q_ASSERT( !m_thread->isRunning() );
m_thread->enqueue( moduleWeight, jobs );
emit queueChanged( jobs ); // FIXME: bogus
}
void
JobQueue::finish()
{
m_finished = true;
emit finished();
}
GlobalStorage*
JobQueue::globalStorage() const
{
return m_storage;
}
} // namespace Calamares