238 lines
6.4 KiB
C++
238 lines
6.4 KiB
C++
/* This file is part of Clementine.
|
|
|
|
Clementine 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.
|
|
|
|
Clementine 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 for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef BACKGROUNDTHREAD_H
|
|
#define BACKGROUNDTHREAD_H
|
|
|
|
#include <QThread>
|
|
#include <QtDebug>
|
|
#include <QWaitCondition>
|
|
#include <QMutexLocker>
|
|
#include <QCoreApplication>
|
|
|
|
#include <boost/shared_ptr.hpp>
|
|
|
|
#ifdef Q_OS_LINUX
|
|
# include <sys/syscall.h>
|
|
#endif
|
|
#ifdef Q_OS_DARWIN
|
|
# include <sys/resource.h>
|
|
#endif
|
|
|
|
// These classes are a bit confusing because they're trying to do so many
|
|
// things:
|
|
// * Run a worker in a background thread
|
|
// * ... or maybe run it in the same thread if we're in a test
|
|
// * Use interfaces throughout, so the implementations can be mocked
|
|
// * Create concrete implementations of the interfaces when threads start
|
|
//
|
|
// The types you should use throughout your header files are:
|
|
// BackgroundThread<InterfaceType>
|
|
// BackgroundThreadFactory<InterfaceType>
|
|
//
|
|
// You should allow callers to set their own factory (which might return mocks
|
|
// of your interface), and default to using a:
|
|
// BackgroundThreadFactoryImplementation<InterfaceType, DerivedType>
|
|
|
|
|
|
// This is the base class. We need one because moc doesn't like templated
|
|
// classes. This also deals with anything that doesn't depend on the type of
|
|
// the worker.
|
|
class BackgroundThreadBase : public QThread {
|
|
Q_OBJECT
|
|
public:
|
|
BackgroundThreadBase(QObject* parent = 0);
|
|
|
|
// Borrowed from schedutils
|
|
enum IoPriority {
|
|
IOPRIO_CLASS_NONE = 0,
|
|
IOPRIO_CLASS_RT,
|
|
IOPRIO_CLASS_BE,
|
|
IOPRIO_CLASS_IDLE,
|
|
};
|
|
|
|
void set_io_priority(IoPriority priority) { io_priority_ = priority; }
|
|
void set_cpu_priority(QThread::Priority priority) { cpu_priority_ = priority; }
|
|
|
|
virtual void Start(bool block = false);
|
|
|
|
// Creates a new QObject in this thread synchronously.
|
|
// The class T needs to have a Q_INVOKABLE constructor that takes a single
|
|
// QObject* argument.
|
|
template <typename T>
|
|
T* CreateInThread();
|
|
|
|
signals:
|
|
void Initialised();
|
|
|
|
protected:
|
|
struct CreateInThreadRequest {
|
|
CreateInThreadRequest(const QMetaObject& meta_object)
|
|
: meta_object_(meta_object), object_(NULL) {}
|
|
|
|
const QMetaObject& meta_object_;
|
|
QObject* object_;
|
|
|
|
QWaitCondition wait_condition_;
|
|
QMutex mutex_;
|
|
};
|
|
|
|
struct CreateInThreadEvent : public QEvent {
|
|
CreateInThreadEvent(CreateInThreadRequest* req);
|
|
|
|
static int sEventType;
|
|
|
|
CreateInThreadRequest* req_;
|
|
};
|
|
|
|
class ObjectCreator : public QObject {
|
|
public:
|
|
bool event(QEvent *);
|
|
};
|
|
|
|
|
|
int SetIOPriority();
|
|
static int gettid();
|
|
|
|
enum {
|
|
IOPRIO_WHO_PROCESS = 1,
|
|
IOPRIO_WHO_PGRP,
|
|
IOPRIO_WHO_USER,
|
|
};
|
|
static const int IOPRIO_CLASS_SHIFT = 13;
|
|
|
|
IoPriority io_priority_;
|
|
QThread::Priority cpu_priority_;
|
|
|
|
QWaitCondition started_wait_condition_;
|
|
QMutex started_wait_condition_mutex_;
|
|
|
|
ObjectCreator* object_creator_;
|
|
};
|
|
|
|
// This is the templated class that stores and returns the worker object.
|
|
template <typename InterfaceType>
|
|
class BackgroundThread : public BackgroundThreadBase {
|
|
public:
|
|
BackgroundThread(QObject* parent = 0);
|
|
~BackgroundThread();
|
|
|
|
boost::shared_ptr<InterfaceType> Worker() const { return worker_; }
|
|
|
|
protected:
|
|
boost::shared_ptr<InterfaceType> worker_;
|
|
};
|
|
|
|
// This class actually creates an implementation of the worker object
|
|
template <typename InterfaceType, typename DerivedType>
|
|
class BackgroundThreadImplementation : public BackgroundThread<InterfaceType> {
|
|
public:
|
|
BackgroundThreadImplementation(QObject* parent = 0);
|
|
|
|
protected:
|
|
void run();
|
|
};
|
|
|
|
|
|
// This is a pure virtual factory for creating threads.
|
|
template <typename InterfaceType>
|
|
class BackgroundThreadFactory {
|
|
public:
|
|
virtual ~BackgroundThreadFactory() {}
|
|
virtual BackgroundThread<InterfaceType>* GetThread(QObject* parent) = 0;
|
|
};
|
|
|
|
// This implementation of the factory returns a BackgroundThread that creates
|
|
// the right derived types...
|
|
template <typename InterfaceType, typename DerivedType>
|
|
class BackgroundThreadFactoryImplementation : public BackgroundThreadFactory<InterfaceType> {
|
|
public:
|
|
BackgroundThread<InterfaceType>* GetThread(QObject* parent) {
|
|
return new BackgroundThreadImplementation<InterfaceType, DerivedType>(parent);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
T* BackgroundThreadBase::CreateInThread() {
|
|
// Create the request and lock it.
|
|
CreateInThreadRequest req(T::staticMetaObject);
|
|
QMutexLocker l(&req.mutex_);
|
|
|
|
// Post an event to the thread. It will create the object and signal the
|
|
// wait condition. Ownership of the event is transferred to Qt.
|
|
CreateInThreadEvent* event = new CreateInThreadEvent(&req);
|
|
QCoreApplication::postEvent(object_creator_, event);
|
|
|
|
req.wait_condition_.wait(&req.mutex_);
|
|
|
|
return static_cast<T*>(req.object_);
|
|
}
|
|
|
|
template <typename InterfaceType>
|
|
BackgroundThread<InterfaceType>::BackgroundThread(QObject *parent)
|
|
: BackgroundThreadBase(parent)
|
|
{
|
|
}
|
|
|
|
template <typename InterfaceType>
|
|
BackgroundThread<InterfaceType>::~BackgroundThread() {
|
|
if (isRunning()) {
|
|
if (boost::shared_ptr<InterfaceType> w = worker_)
|
|
w->Stop();
|
|
|
|
quit();
|
|
if (wait(1000))
|
|
return;
|
|
terminate();
|
|
wait(1000);
|
|
}
|
|
}
|
|
|
|
template <typename InterfaceType, typename DerivedType>
|
|
BackgroundThreadImplementation<InterfaceType, DerivedType>::
|
|
BackgroundThreadImplementation(QObject* parent)
|
|
: BackgroundThread<InterfaceType>(parent)
|
|
{
|
|
}
|
|
|
|
|
|
template <typename InterfaceType, typename DerivedType>
|
|
void BackgroundThreadImplementation<InterfaceType, DerivedType>::run() {
|
|
this->SetIOPriority();
|
|
|
|
this->worker_.reset(new DerivedType);
|
|
this->object_creator_ = new BackgroundThreadBase::ObjectCreator;
|
|
|
|
{
|
|
// Tell the calling thread that we've initialised the worker.
|
|
QMutexLocker l(&this->started_wait_condition_mutex_);
|
|
this->started_wait_condition_.wakeAll();
|
|
}
|
|
|
|
emit this->Initialised();
|
|
QThread::exec();
|
|
|
|
delete this->object_creator_;
|
|
this->worker_.reset();
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // BACKGROUNDTHREAD_H
|