2016-05-17 03:04:11 +12:00
|
|
|
#pragma once
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
#ifndef TTHREAD_H
|
|
|
|
#define TTHREAD_H
|
|
|
|
|
|
|
|
#include "tsmartpointer.h"
|
|
|
|
|
|
|
|
#include <QThread>
|
|
|
|
|
|
|
|
#undef DVAPI
|
|
|
|
#undef DVVAR
|
|
|
|
#ifdef TNZCORE_EXPORTS
|
|
|
|
#define DVAPI DV_EXPORT_API
|
|
|
|
#define DVVAR DV_EXPORT_VAR
|
|
|
|
#else
|
|
|
|
#define DVAPI DV_IMPORT_API
|
|
|
|
#define DVVAR DV_IMPORT_VAR
|
|
|
|
#endif
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
namespace TThread {
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//! Initializes all TThread namespace components.
|
|
|
|
void DVAPI init();
|
|
|
|
|
|
|
|
//! Closes all TThread components as soon as possible in a safe manner.
|
|
|
|
//! \sa Executor::shutdown() method
|
|
|
|
void DVAPI shutdown();
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
// Forward declarations
|
|
|
|
class ExecutorId; // Private
|
2016-03-19 06:57:51 +13:00
|
|
|
class Runnable;
|
|
|
|
|
2017-05-19 22:20:33 +12:00
|
|
|
} // namespace TThread
|
|
|
|
#ifdef _WIN32
|
|
|
|
template class DVAPI TSmartPointerT<TThread::Runnable>;
|
2016-03-19 06:57:51 +13:00
|
|
|
#endif
|
2017-05-19 22:20:33 +12:00
|
|
|
namespace TThread {
|
|
|
|
|
2016-03-19 06:57:51 +13:00
|
|
|
typedef TSmartPointerT<Runnable> RunnableP;
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Runnable class is the abstract model for user-defined tasks which can be
|
|
|
|
scheduled for execution into separate application threads.
|
|
|
|
|
|
|
|
A user must first implement the run() method to provide a task with the
|
2016-06-15 18:43:10 +12:00
|
|
|
very execution code. Worker threads created internally by the task manager
|
|
|
|
will
|
2016-03-19 06:57:51 +13:00
|
|
|
take ownership of the task and make it run() at the most appropriate time,
|
|
|
|
depending on the task load, insertion time and its scheduling priority.
|
|
|
|
\n \n
|
|
|
|
The scheduling priority of a task can be set by reimplementing the
|
|
|
|
\b schedulingPriority() method. Tasks whose scheduling priority is higher are
|
|
|
|
always started before compared to the others added by the same executor.
|
|
|
|
\n \n
|
|
|
|
The hosting thread's running priority may also be set reimplementing the
|
|
|
|
runningPriority() method; see QThread class documentation in Qt manual.
|
|
|
|
\n \n
|
|
|
|
A task's load is an important property that should be reimplemented in all
|
|
|
|
resource-consuming tasks. It enables the user to declare the approximate
|
|
|
|
CPU load produced by a task, allowing the task manager to effectively
|
|
|
|
calculate the most appropriate moment to run it.
|
|
|
|
|
|
|
|
\warning \n All built-in signals about the Runnable class are emitted in a
|
2016-06-15 18:43:10 +12:00
|
|
|
mutex-protected environment in order to make sure that signal emission is
|
|
|
|
consistent
|
|
|
|
with the task status - in other words, so that \a queued controller-emitted
|
|
|
|
canceled()
|
2016-03-19 06:57:51 +13:00
|
|
|
and terminated() signals are not delivered \a before started() or \a after
|
|
|
|
finished() and exception() worker-emitted signals.
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
\warning Thus, setting up blocking connections or blocking direct slots is \b
|
|
|
|
not \b
|
|
|
|
supported and will typically result in deadlocks; furthermore, the same also
|
|
|
|
applies to
|
|
|
|
slot calls to the Executor API, which would have the aforementioned mutex to
|
|
|
|
be
|
2016-03-19 06:57:51 +13:00
|
|
|
locked again.
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
\warning In case the above blocking strategies are mandatory, the user should
|
|
|
|
add
|
|
|
|
custom signals to be emitted inside the mutex-free run() block, just before or
|
|
|
|
after
|
2016-03-19 06:57:51 +13:00
|
|
|
the actual code to be executed, like this:
|
|
|
|
|
|
|
|
\code
|
|
|
|
void MyTask::run()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
emit myStarted(this);
|
|
|
|
theRunCode();
|
|
|
|
emit myFinished(this);
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
emit myException(this);
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
\endcode
|
|
|
|
\code
|
|
|
|
..
|
|
|
|
MyTask* myTask = new MyTask;
|
2016-06-15 18:43:10 +12:00
|
|
|
connect(myTask, SIGNAL(myStarted(TThread::RunnableP)), myTask,
|
|
|
|
SLOT(onStarted(TThread::RunnableP)),
|
|
|
|
Qt::BlockingQueuedConnection) //theRunCode() waits for onStarted() to
|
|
|
|
complete
|
2016-03-19 06:57:51 +13:00
|
|
|
..
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
\sa Executor class.
|
|
|
|
*/
|
2016-06-15 18:43:10 +12:00
|
|
|
class DVAPI Runnable : public QObject, public TSmartObject {
|
|
|
|
Q_OBJECT
|
|
|
|
DECLARE_CLASS_CODE
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
ExecutorId *m_id;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int m_load;
|
|
|
|
int m_schedulingPriority;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
friend class Executor; // Needed to confront Executor's and Runnable's ids
|
|
|
|
friend class ExecutorImp; // The internal task manager needs full control
|
|
|
|
// over the task
|
2016-06-20 14:23:05 +12:00
|
|
|
friend class Worker; // Workers force tasks to emit state signals
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
public:
|
2016-06-15 18:43:10 +12:00
|
|
|
Runnable();
|
|
|
|
virtual ~Runnable();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//! The central code of the task that is executed by a worker thread.
|
|
|
|
virtual void run() = 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
virtual int taskLoad();
|
|
|
|
virtual int schedulingPriority();
|
|
|
|
virtual QThread::Priority runningPriority();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
Q_SIGNALS:
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void started(TThread::RunnableP sender);
|
|
|
|
void finished(TThread::RunnableP sender);
|
|
|
|
void exception(TThread::RunnableP sender);
|
|
|
|
void canceled(TThread::RunnableP sender);
|
|
|
|
void terminated(TThread::RunnableP sender);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
public Q_SLOTS:
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
virtual void onStarted(TThread::RunnableP sender);
|
|
|
|
virtual void onFinished(TThread::RunnableP sender);
|
|
|
|
virtual void onException(TThread::RunnableP sender);
|
|
|
|
virtual void onCanceled(TThread::RunnableP sender);
|
|
|
|
virtual void onTerminated(TThread::RunnableP sender);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
private:
|
2016-06-15 18:43:10 +12:00
|
|
|
inline bool needsAccumulation();
|
|
|
|
inline bool customConditions();
|
2016-03-19 06:57:51 +13:00
|
|
|
};
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Executor class provides an effective way for planning the execution of
|
|
|
|
user-defined tasks that require separate working threads.
|
|
|
|
|
|
|
|
When an application needs to perform a resource-consuming task, it is often
|
2016-06-15 18:43:10 +12:00
|
|
|
a good idea to dedicate a separate thread for it, especially in GUI
|
|
|
|
applications;
|
2016-03-19 06:57:51 +13:00
|
|
|
however, doing so eventually raises the problem of managing such intensive
|
2016-06-15 18:43:10 +12:00
|
|
|
tasks in a way that constantly ensures the correct use of the machine
|
|
|
|
resources -
|
2016-03-19 06:57:51 +13:00
|
|
|
so that in any given time the CPU usage is maximal, but not overloaded.
|
|
|
|
Additional requests by the user may arise, including preferenced ordering
|
|
|
|
among tasks, the necessity of salvaging some CPU resources or threads for
|
|
|
|
incoming tasks, and so on.
|
|
|
|
\n \n
|
2016-06-15 18:43:10 +12:00
|
|
|
The TThread namespace API contains two main classes, \b Runnable and \b
|
|
|
|
Executor,
|
2016-03-19 06:57:51 +13:00
|
|
|
which provide a way for implementing consistent threading strategies.
|
|
|
|
\n \n
|
2016-06-15 18:43:10 +12:00
|
|
|
In order to use the Executor class it is first necessary to install the thread
|
|
|
|
manager
|
2016-03-19 06:57:51 +13:00
|
|
|
into application code by calling the static method init() appropriately.
|
|
|
|
\n \n
|
2016-06-15 18:43:10 +12:00
|
|
|
Executors are then used to submit - or eventually remove - tasks for execution
|
|
|
|
into a
|
|
|
|
separate working thread, by means of the \b addTask(), \b removeTask() and \b
|
|
|
|
cancelAll() methods.
|
2016-03-19 06:57:51 +13:00
|
|
|
Each Executor's visibility is always limited to the tasks that it submits -
|
|
|
|
so calling removeTask() or cancelAll() only affects the tasks previously
|
2016-06-15 18:43:10 +12:00
|
|
|
added by that same Executor - which easily reflects the idea of an Executor
|
|
|
|
representing
|
2016-03-19 06:57:51 +13:00
|
|
|
a group of tasks.
|
|
|
|
\n \n
|
2016-06-15 18:43:10 +12:00
|
|
|
Basic control over the execution strategy for the group of tasks submitted by
|
|
|
|
an Executor
|
|
|
|
can be acquired using the setMaxActiveTasks() and setMaxActiveLoad() methods,
|
|
|
|
both granting
|
2016-03-19 06:57:51 +13:00
|
|
|
the possibility to bound the execution of tasks to custom maximum conditions.
|
2016-06-15 18:43:10 +12:00
|
|
|
For example, use setMaxActiveTasks(1) to force the execution of 1 task only at
|
|
|
|
a time,
|
2016-03-19 06:57:51 +13:00
|
|
|
or setMaxActiveLoad(100) to set a single CPU core available for the group.
|
|
|
|
|
|
|
|
\sa \b Runnable class documentation.
|
|
|
|
*/
|
2016-06-15 18:43:10 +12:00
|
|
|
class DVAPI Executor {
|
|
|
|
ExecutorId *m_id;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
friend class ExecutorImp;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
public:
|
2016-06-15 18:43:10 +12:00
|
|
|
Executor();
|
|
|
|
~Executor();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
static void init();
|
|
|
|
static void shutdown();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void addTask(RunnableP task);
|
|
|
|
void removeTask(RunnableP task);
|
|
|
|
void cancelAll();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void setMaxActiveTasks(int count);
|
|
|
|
void setMaxActiveLoad(int load);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int maxActiveTasks() const;
|
|
|
|
int maxActiveLoad() const;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void setDedicatedThreads(bool dedicated, bool persistent = true);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
private:
|
2016-06-15 18:43:10 +12:00
|
|
|
// not implemented
|
|
|
|
Executor &operator=(const Executor &);
|
|
|
|
Executor(const Executor &);
|
2016-03-19 06:57:51 +13:00
|
|
|
};
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
} // namespace TThread
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
#endif // TTHREAD_H
|