|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#ifdef _DEBUG
|
|
Toshihiro Shimizu |
890ddd |
#define _STLP_DEBUG 1
|
|
Toshihiro Shimizu |
890ddd |
#endif
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// TnzCore includes
|
|
Toshihiro Shimizu |
890ddd |
#include "tthreadmessage.h"
|
|
Toshihiro Shimizu |
890ddd |
#include "tsystem.h"
|
|
Toshihiro Shimizu |
890ddd |
#include "tatomicvar.h"
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#include "tthread.h"
|
|
Campbell Barton |
d0e335 |
#include "tthreadp.h"
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// STL includes
|
|
Toshihiro Shimizu |
890ddd |
#include <set></set>
|
|
Toshihiro Shimizu |
890ddd |
#include <deque></deque>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// tcg includes
|
|
Toshihiro Shimizu |
890ddd |
#include "tcg/tcg_pool.h"
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Qt includes
|
|
Toshihiro Shimizu |
890ddd |
#include <qmultimap></qmultimap>
|
|
Toshihiro Shimizu |
890ddd |
#include <qmutablemapiterator></qmutablemapiterator>
|
|
Toshihiro Shimizu |
890ddd |
#include <qmutex></qmutex>
|
|
Toshihiro Shimizu |
890ddd |
#include <qwaitcondition></qwaitcondition>
|
|
Toshihiro Shimizu |
890ddd |
#include <qmetatype></qmetatype>
|
|
Toshihiro Shimizu |
890ddd |
#include <qcoreapplication></qcoreapplication>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==============================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==================================================
|
|
Toshihiro Shimizu |
890ddd |
// Paradigms of the Executor tasks management
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Basics:
|
|
Toshihiro Shimizu |
890ddd |
// * Tasks added by Executors are always stored in a global QMultiMap first -
|
|
Toshihiro Shimizu |
890ddd |
// ordering primarily being the schedulingPriority(), and insertion instant
|
|
Toshihiro Shimizu |
890ddd |
// (implicit) when they have the same scheduling priority.
|
|
Toshihiro Shimizu |
890ddd |
// * The QMultiMap needs to be reverse-iterated to do that, since values with
|
|
Toshihiro Shimizu |
890ddd |
// the same key are ordered from the most recent to the oldest one (see Qt's
|
|
Toshihiro Shimizu |
890ddd |
// manual).
|
|
Toshihiro Shimizu |
890ddd |
// * Worker threads are stored in a global set.
|
|
Toshihiro Shimizu |
890ddd |
// * When a task is added or a task has been performed, the workers list is
|
|
Toshihiro Shimizu |
890ddd |
// refreshed, possibly adding new Workers for some executable tasks.
|
|
Shinya Kitaoka |
120a6e |
// * When a worker ends a task, it automatically takes a new one before
|
|
Shinya Kitaoka |
120a6e |
// refreshing
|
|
Toshihiro Shimizu |
890ddd |
// the workers list. If no task can be taken, by default the thread exits and
|
|
Toshihiro Shimizu |
890ddd |
// invokes its own destruction.
|
|
Toshihiro Shimizu |
890ddd |
// * The thread may instead be put to rest if explicitly told by the user with
|
|
Toshihiro Shimizu |
890ddd |
// the appropriate method.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Default execution conditions:
|
|
Shinya Kitaoka |
120a6e |
// * A task is executable if, by default, its task load added to the sum of
|
|
Shinya Kitaoka |
120a6e |
// that of
|
|
Shinya Kitaoka |
120a6e |
// all other active tasks does not exceed the available resources of the
|
|
Shinya Kitaoka |
120a6e |
// system
|
|
Toshihiro Shimizu |
890ddd |
// (i.e.: 100 * # of cores of the machine).
|
|
Shinya Kitaoka |
120a6e |
// In other words, in every instant of execution, the sum of all active
|
|
Shinya Kitaoka |
120a6e |
// task's loads
|
|
Toshihiro Shimizu |
890ddd |
// never exceeds the available machine resources.
|
|
Shinya Kitaoka |
120a6e |
// * When such default execution condition is not met when attempting to take
|
|
Shinya Kitaoka |
120a6e |
// the
|
|
Shinya Kitaoka |
120a6e |
// task, no other task is taken instead - we wait until enough resources have
|
|
Shinya Kitaoka |
120a6e |
// been
|
|
Toshihiro Shimizu |
890ddd |
// freed before attempting to take the same task again.
|
|
Toshihiro Shimizu |
890ddd |
// In other words, the default execution condition is *BLOCKING*.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Custom execution conditions:
|
|
Shinya Kitaoka |
120a6e |
// * The user may decide to impose more tight conditions for tasks added by a
|
|
Shinya Kitaoka |
120a6e |
// certain
|
|
Toshihiro Shimizu |
890ddd |
// Executor. Let's call such conditions 'custom' conditions.
|
|
Shinya Kitaoka |
120a6e |
// * Custom conditions are always tested *AFTER* the default ones (on the same
|
|
Shinya Kitaoka |
120a6e |
// task).
|
|
Toshihiro Shimizu |
890ddd |
// This is necessary to enforce the global scheduling priorities mechanism.
|
|
Shinya Kitaoka |
120a6e |
// * When no task of a certain executor is active, custom conditions are always
|
|
Shinya Kitaoka |
120a6e |
// considered
|
|
Toshihiro Shimizu |
890ddd |
// satisfied.
|
|
Shinya Kitaoka |
120a6e |
// * If custom conditions are not met, we enqueue the task in an
|
|
Shinya Kitaoka |
120a6e |
// Executor-private queue
|
|
Shinya Kitaoka |
120a6e |
// for later execution and remove it from the global tasks queue - making it
|
|
Shinya Kitaoka |
120a6e |
// possible
|
|
Shinya Kitaoka |
120a6e |
// to perform other tasks before our custom-failed one (such operation will
|
|
Shinya Kitaoka |
120a6e |
// be called
|
|
Toshihiro Shimizu |
890ddd |
// "ACCUMULATION").
|
|
Shinya Kitaoka |
120a6e |
// In other words, custom conditions are *NOT BLOCKING* among different
|
|
Shinya Kitaoka |
120a6e |
// Executors.
|
|
Shinya Kitaoka |
120a6e |
// * Tasks in the last task's Executor-private queue are always polled before
|
|
Shinya Kitaoka |
120a6e |
// those in
|
|
Shinya Kitaoka |
120a6e |
// the global queue by Workers which ended a task, and this is done in a
|
|
Shinya Kitaoka |
120a6e |
// *BLOCKING* way
|
|
Toshihiro Shimizu |
890ddd |
// inside the same Executor.
|
|
Shinya Kitaoka |
120a6e |
// * When an Executor-private tasks queue is not empty, all the other tasks
|
|
Shinya Kitaoka |
120a6e |
// added by the
|
|
Toshihiro Shimizu |
890ddd |
// same executor are scheduled in the queue.
|
|
Shinya Kitaoka |
120a6e |
// In other words, the order of execution is always that of global insertion,
|
|
Shinya Kitaoka |
120a6e |
// *inside
|
|
Toshihiro Shimizu |
890ddd |
// the same executor*.
|
|
Shinya Kitaoka |
120a6e |
// * Tasks polled from an Executor-private queue (which therefore satisfied
|
|
Shinya Kitaoka |
120a6e |
// custom conditions)
|
|
Shinya Kitaoka |
120a6e |
// may still fail with default execution conditions. If that happens, we put
|
|
Shinya Kitaoka |
120a6e |
// the task
|
|
Shinya Kitaoka |
120a6e |
// back into the global queue with highest possible priority (timedOut) and
|
|
Shinya Kitaoka |
120a6e |
// the worker dies.
|
|
Shinya Kitaoka |
120a6e |
// Tasks with this special priority are polled *before every other task*. So,
|
|
Shinya Kitaoka |
120a6e |
// again, default
|
|
Toshihiro Shimizu |
890ddd |
// conditions are *BLOCKING*.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Thread-safety:
|
|
luz paz |
6454c4 |
// * Most of the following code is mutex-protected, although it may not seem
|
|
luz paz |
6454c4 |
// like it - indeed,
|
|
Shinya Kitaoka |
120a6e |
// only *one* mutex is locked and unlocked all of the time. This 'transition
|
|
Shinya Kitaoka |
120a6e |
// mutex' is
|
|
Shinya Kitaoka |
120a6e |
// the key to thread-safety: we're considered to lie in a 'transition state'
|
|
Shinya Kitaoka |
120a6e |
// if we are
|
|
Shinya Kitaoka |
120a6e |
// operating outside the run() of some task - which covers almost the
|
|
Shinya Kitaoka |
120a6e |
// totality of the code.
|
|
Shinya Kitaoka |
120a6e |
// * The transition mutex is *not* recursive. The reason is that threads con
|
|
Shinya Kitaoka |
120a6e |
// not wait on
|
|
Shinya Kitaoka |
120a6e |
// QWaitConditions if the mutex is recursive. That makes it necessary (and
|
|
Shinya Kitaoka |
120a6e |
// welcome) to put
|
|
Shinya Kitaoka |
120a6e |
// mutex lockers in strategic points of the code - in many low-level
|
|
Shinya Kitaoka |
120a6e |
// functions no mutex
|
|
Shinya Kitaoka |
120a6e |
// is locked *because some caller already did it before*. If you're modifying
|
|
Shinya Kitaoka |
120a6e |
// the code
|
|
Shinya Kitaoka |
120a6e |
// always trace back the callers of a function before inserting misleading
|
|
Shinya Kitaoka |
120a6e |
// mutex lockers.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==============================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==================
|
|
Toshihiro Shimizu |
890ddd |
// TODO list
|
|
Toshihiro Shimizu |
890ddd |
//------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// * Improve dedicated threads support: make sure that tasks added to a
|
|
Shinya Kitaoka |
120a6e |
// dedicated
|
|
Shinya Kitaoka |
120a6e |
// executor are directly moved to the accumulation queue. The
|
|
Shinya Kitaoka |
120a6e |
// setDedicatedThreads()
|
|
Toshihiro Shimizu |
890ddd |
// method must therefore react accordingly.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// * It could be possible to implement a dependency-based mechanism...
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// * Should the hosting thread wait for worker ones upon ExecutorImp
|
|
Shinya Kitaoka |
120a6e |
// destruction??
|
|
Toshihiro Shimizu |
890ddd |
// It could be a problem on some forever-waiting tasks...
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// * Ricontrolla che con le ultime modifiche gli ExecutorId rimangano quando si
|
|
Shinya Kitaoka |
120a6e |
// passano
|
|
Toshihiro Shimizu |
890ddd |
// i puntatori in takeTask e refreshAss..
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==============================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
using namespace TThread;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
DEFINE_CLASS_CODE(TThread::Runnable, 21)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==============================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==========================================
|
|
Toshihiro Shimizu |
890ddd |
// Global init() initializer function
|
|
Toshihiro Shimizu |
890ddd |
//------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void TThread::init() {
|
|
Shinya Kitaoka |
120a6e |
Executor::init();
|
|
Shinya Kitaoka |
120a6e |
TThreadMessageDispatcher::init();
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void TThread::shutdown() { Executor::shutdown(); }
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==============================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
namespace TThread {
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==============================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//===========================
|
|
Toshihiro Shimizu |
890ddd |
// Worker Thread class
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! A Worker is a specialized QThread that continuously polls Runnable
|
|
Toshihiro Shimizu |
890ddd |
//! tasks from a global execution queue to make them work.
|
|
Shinya Kitaoka |
d1f6c4 |
class Worker final : public QThread {
|
|
Toshihiro Shimizu |
890ddd |
public:
|
|
Shinya Kitaoka |
120a6e |
RunnableP m_task;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
TSmartPointerT<executorid> m_master;</executorid>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
bool m_exit;
|
|
Shinya Kitaoka |
120a6e |
QWaitCondition m_waitCondition;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
Worker();
|
|
Shinya Kitaoka |
120a6e |
~Worker();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
473e70 |
void run() override;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void takeTask();
|
|
Shinya Kitaoka |
120a6e |
inline bool canAdopt(const RunnableP &task);
|
|
Shinya Kitaoka |
120a6e |
inline void adoptTask(RunnableP &task);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void rest();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void updateCountsOnTake();
|
|
Shinya Kitaoka |
120a6e |
inline void updateCountsOnRelease();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void onFinish();
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//========================
|
|
Toshihiro Shimizu |
890ddd |
// ExecutorId class
|
|
Toshihiro Shimizu |
890ddd |
//------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Contains all the executor data that need to persist until all tasks
|
|
Toshihiro Shimizu |
890ddd |
//! added by the executor have been processed. Upon creation of an Executor
|
|
Toshihiro Shimizu |
890ddd |
//! object, it instantiates one ExecutorId for both storing the Executor's data
|
|
Toshihiro Shimizu |
890ddd |
//! sensible to the underlying code of the Executor manager, and to identify all
|
|
Toshihiro Shimizu |
890ddd |
//! tasks added through it - by copying the smart pointer to the id into each
|
|
Toshihiro Shimizu |
890ddd |
//! added task.
|
|
Toshihiro Shimizu |
890ddd |
//! \sa Executor and Runnable class.
|
|
Shinya Kitaoka |
d1f6c4 |
class ExecutorId final : public TSmartObject {
|
|
Toshihiro Shimizu |
890ddd |
public:
|
|
Shinya Kitaoka |
120a6e |
size_t m_id;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int m_activeTasks;
|
|
Shinya Kitaoka |
120a6e |
int m_maxActiveTasks;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int m_activeLoad;
|
|
Shinya Kitaoka |
120a6e |
int m_maxActiveLoad;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
bool m_dedicatedThreads;
|
|
Shinya Kitaoka |
120a6e |
bool m_persistentThreads;
|
|
Shinya Kitaoka |
120a6e |
std::deque<worker *=""> m_sleepings;</worker>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
ExecutorId();
|
|
Shinya Kitaoka |
120a6e |
~ExecutorId();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void accumulate(const RunnableP &task);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void newWorker(RunnableP &task);
|
|
Shinya Kitaoka |
120a6e |
void refreshDedicatedList();
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//===========================
|
|
Toshihiro Shimizu |
890ddd |
// Executor::Imp class
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! ExecutorImp both manages the allocation of worker threads for the
|
|
Toshihiro Shimizu |
890ddd |
//! execution of Runnable tasks and centralizes the tasks collection.
|
|
Toshihiro Shimizu |
890ddd |
//! One process only hosts one instance of the ExecutorImp class as a
|
|
Toshihiro Shimizu |
890ddd |
//! a global variable that needs to be allocated in an application-lasting
|
|
Toshihiro Shimizu |
890ddd |
//! and event-looped thread - typically the main thread in GUI applications.
|
|
Shinya Kitaoka |
120a6e |
class ExecutorImp {
|
|
Toshihiro Shimizu |
890ddd |
public:
|
|
Shinya Kitaoka |
120a6e |
QMultiMap<int, runnablep=""> m_tasks;</int,>
|
|
Shinya Kitaoka |
120a6e |
std::set<worker *=""> m_workers; // Used just for debugging purposes</worker>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
tcg::indices_pool<> m_executorIdPool;
|
|
Shinya Kitaoka |
120a6e |
std::vector<uchar> m_waitingFlagsPool;</uchar>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int m_activeLoad;
|
|
Shinya Kitaoka |
120a6e |
int m_maxLoad;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
QMutex m_transitionMutex; // Workers' transition mutex
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
ExecutorImp();
|
|
Shinya Kitaoka |
120a6e |
~ExecutorImp();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void insertTask(int schedulingPriority, RunnableP &task);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void refreshAssignments();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline bool isExecutable(RunnableP &task);
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
} // namespace TThread
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//===========================
|
|
Toshihiro Shimizu |
890ddd |
// Global variables
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
namespace {
|
|
Shinya Kitaoka |
120a6e |
ExecutorImp *globalImp = 0;
|
|
Toshihiro Shimizu |
890ddd |
ExecutorImpSlots *globalImpSlots = 0;
|
|
Shinya Kitaoka |
120a6e |
bool shutdownVar = false;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=============================
|
|
Toshihiro Shimizu |
890ddd |
// ExecutorImp methods
|
|
Toshihiro Shimizu |
890ddd |
//-----------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
ExecutorImp::ExecutorImp()
|
|
Shinya Kitaoka |
120a6e |
: m_activeLoad(0)
|
|
Shinya Kitaoka |
120a6e |
, m_maxLoad(TSystem::getProcessorCount() * 100)
|
|
Shinya Kitaoka |
120a6e |
, m_transitionMutex() // NOTE: We'll wait on this mutex - so it can't be
|
|
Shinya Kitaoka |
120a6e |
// recursive
|
|
Shinya Kitaoka |
120a6e |
{}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
ExecutorImp::~ExecutorImp() {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// A task is executable <==> its load allows it. The task load is considered
|
|
Shinya Kitaoka |
120a6e |
// fixed until another isExecutable() call is made again - in case it may
|
|
Shinya Kitaoka |
120a6e |
// change in time.
|
|
Shinya Kitaoka |
120a6e |
inline bool ExecutorImp::isExecutable(RunnableP &task) {
|
|
Shinya Kitaoka |
120a6e |
return m_activeLoad + task->m_load <= m_maxLoad;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void ExecutorImp::insertTask(int schedulingPriority, RunnableP &task) {
|
|
Shinya Kitaoka |
120a6e |
task->m_schedulingPriority = schedulingPriority;
|
|
Shinya Kitaoka |
120a6e |
m_tasks.insert(schedulingPriority, task);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//========================
|
|
Toshihiro Shimizu |
890ddd |
// Runnable methods
|
|
Toshihiro Shimizu |
890ddd |
//------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
Runnable::Runnable() : TSmartObject(m_classCode), m_id(0) {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
Runnable::~Runnable() {
|
|
Shinya Kitaoka |
120a6e |
if (m_id) m_id->release(); // see Executor::addTask()
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
//! Returns the predicted CPU load generated by the task, expressed in
|
|
Shinya Kitaoka |
120a6e |
//! percentage
|
|
Shinya Kitaoka |
120a6e |
//! of core usage (that is, 100 is intended to fully occupy one processing
|
|
Shinya Kitaoka |
120a6e |
//! core).
|
|
Shinya Kitaoka |
120a6e |
//! Appropriate task load calibration is an important step to take when
|
|
Shinya Kitaoka |
120a6e |
//! implementing
|
|
Toshihiro Shimizu |
890ddd |
//! a new task; for this purpose, remember some rules to follow:
|
|
Toshihiro Shimizu |
890ddd |
//!
|
|
Shinya Kitaoka |
120a6e |
//! In every moment, the task manager ensures that the overall sum of the
|
|
Shinya Kitaoka |
120a6e |
//! active
|
|
Shinya Kitaoka |
120a6e |
//! task's load does not exceed the number of machine's processing cores
|
|
Shinya Kitaoka |
120a6e |
//! multiplied
|
|
Shinya Kitaoka |
120a6e |
//! by 100. This condition is \a blocking with respect to the execution of
|
|
Shinya Kitaoka |
120a6e |
//! any other
|
|
Shinya Kitaoka |
120a6e |
//! task - meaning that when a task is about to be executed the task manager
|
|
Shinya Kitaoka |
120a6e |
//! \a waits
|
|
Toshihiro Shimizu |
890ddd |
//! until enough CPU resources are available to make it run.
|
|
Shinya Kitaoka |
120a6e |
//! In particular, observe that a task's load \b never has to exceed the
|
|
Shinya Kitaoka |
120a6e |
//! total CPU
|
|
Shinya Kitaoka |
120a6e |
//! resources - doing so would surely result in a block of your application.
|
|
Shinya Kitaoka |
120a6e |
//! The number
|
|
Shinya Kitaoka |
120a6e |
//! of available cores can be accessed via the \b
|
|
Shinya Kitaoka |
120a6e |
//! TSystem::getProcessorCount() or
|
|
Toshihiro Shimizu |
890ddd |
//! \b QThread::idealThreadCount().
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
//! The task load is considered constant for the duration of the task.
|
|
Shinya Kitaoka |
120a6e |
//! Changing its
|
|
Shinya Kitaoka |
120a6e |
//! value does not affect the task manager in any way once the task has been
|
|
Shinya Kitaoka |
120a6e |
//! started.
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
//! The default task load is 0, representing a very light task. If the task
|
|
Shinya Kitaoka |
120a6e |
//! load
|
|
Shinya Kitaoka |
120a6e |
//! is 0 the condition at point 1 always succeeds - so the task is always
|
|
Shinya Kitaoka |
120a6e |
//! executed when
|
|
Shinya Kitaoka |
120a6e |
//! encountered. Observe that a long succession of 0 task loads leads to the
|
|
Shinya Kitaoka |
120a6e |
//! creation of
|
|
Shinya Kitaoka |
120a6e |
//! a proportional number of threads simultaneously running to dispatch it;
|
|
Shinya Kitaoka |
120a6e |
//! if this is
|
|
Toshihiro Shimizu |
890ddd |
//! a problem, consider the use of \b Executor::setMaxActiveTasks()
|
|
Toshihiro Shimizu |
890ddd |
//! to make only a certain number of tasks being executed at the same time.
|
|
Toshihiro Shimizu |
890ddd |
//!
|
|
Shinya Kitaoka |
120a6e |
int Runnable::taskLoad() { return 0; }
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Returns the priority value used to schedule a task for execution. Tasks
|
|
Toshihiro Shimizu |
890ddd |
//! with higher priority start before tasks with lower priority. The default
|
|
Toshihiro Shimizu |
890ddd |
//! value returned is 5 (halfway from 0 to 10) - but any value other than
|
|
Toshihiro Shimizu |
890ddd |
//! (std::numeric_limits<int>::max)() is acceptable.</int>
|
|
Shinya Kitaoka |
120a6e |
int Runnable::schedulingPriority() { return 5; }
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Returns the QThread::Priority used by worker threads when they adopt
|
|
Toshihiro Shimizu |
890ddd |
//! the task. The default value returned is QThread::Normal.
|
|
Shinya Kitaoka |
120a6e |
QThread::Priority Runnable::runningPriority() {
|
|
Shinya Kitaoka |
120a6e |
return QThread::NormalPriority;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline bool Runnable::customConditions() {
|
|
Shinya Kitaoka |
120a6e |
return (m_id->m_activeTasks < m_id->m_maxActiveTasks) &&
|
|
Shinya Kitaoka |
120a6e |
(m_id->m_activeLoad + m_load <= m_id->m_maxActiveLoad);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\fn void Runnable::started(RunnableP sender)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
This signal is emitted from working threads just before the run() code
|
|
Toshihiro Shimizu |
890ddd |
is executed. Observe that the passed smart pointer ensures the survival of
|
|
Toshihiro Shimizu |
890ddd |
the emitting task for the time required by connected slots execution.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\warning The started(), finished() and exception() signals are emitted in
|
|
Toshihiro Shimizu |
890ddd |
a mutex-protected environment in order to provide the correct sequence of
|
|
Toshihiro Shimizu |
890ddd |
emissions (i.e. so that canceled() and terminated() controller-emitted signals
|
|
Toshihiro Shimizu |
890ddd |
are either delivered after started() and before finished() or exception(),
|
|
Toshihiro Shimizu |
890ddd |
or \a instead of them).
|
|
Shinya Kitaoka |
120a6e |
\warning Thus, setting up blocking connections or \a direct slots that contain
|
|
Shinya Kitaoka |
120a6e |
a
|
|
Toshihiro Shimizu |
890ddd |
blocking instruction or even calls to the Executor API (which would definitely
|
|
Toshihiro Shimizu |
890ddd |
try to relock the aforementioned mutex) is dangerous and could result in an
|
|
Toshihiro Shimizu |
890ddd |
application freeze.
|
|
Shinya Kitaoka |
120a6e |
\warning In case it's necessary to use blocking features, they should be
|
|
Shinya Kitaoka |
120a6e |
enforced
|
|
Toshihiro Shimizu |
890ddd |
through custom signals to be invoked manually in the run() method, outside the
|
|
Toshihiro Shimizu |
890ddd |
mutex.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\sa \b finished and \b exception signals.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\fn void Runnable::finished(RunnableP sender)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
The \b finished signal is emitted from working threads once the run()
|
|
Toshihiro Shimizu |
890ddd |
code is returned without unmanaged exceptions.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\sa \b started and \b exception signals.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\fn void Runnable::exception(RunnableP sender)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
The \b exception signal is emitted from working threads whenever an
|
|
Toshihiro Shimizu |
890ddd |
untrapped exception is found within the run() method.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\sa \b started and \b finished signals.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\fn void Runnable::canceled(RunnableP sender)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
The \b canceled signal is emitted from the controller thread whenever
|
|
Toshihiro Shimizu |
890ddd |
a task which is currently under the task manager's control is canceled
|
|
Toshihiro Shimizu |
890ddd |
by the user (the signal is emitted from the thread invoking the cancel).
|
|
Toshihiro Shimizu |
890ddd |
Observe that tasks under execution are not stopped by the task manager
|
|
Toshihiro Shimizu |
890ddd |
when they are canceled, but the signal is emitted anyway - helping the
|
|
Toshihiro Shimizu |
890ddd |
user to stop the actual execution of the run() code in advance.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\sa \b Executor::removeTask and \b Executor::cancelAll methods.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\fn void Runnable::terminated(RunnableP sender)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
The \b terminated signal is emitted from the controller thread when
|
|
Toshihiro Shimizu |
890ddd |
the Executor components are shutting down inside a call to
|
|
Toshihiro Shimizu |
890ddd |
Executor::shutdown(). Implementing a slot connected to this signal
|
|
Toshihiro Shimizu |
890ddd |
helps the user in controlling the flow of an Executor-multithreaded
|
|
Toshihiro Shimizu |
890ddd |
application when it is shutting down - for example, it can be imposed
|
|
Toshihiro Shimizu |
890ddd |
that the application must wait for the task to be finished, print logs
|
|
Toshihiro Shimizu |
890ddd |
or similar.
|
|
Toshihiro Shimizu |
890ddd |
This signal is always preceded by a canceled() signal, informing all
|
|
luz paz |
6454c4 |
active tasks that they should begin quitting on their own in a 'soft'
|
|
Toshihiro Shimizu |
890ddd |
way before brute termination may occur.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\sa \b Executor::shutdown static method and \b Runnable::canceled signal.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Convenience slot for the started() signal - so it's not necessary to declare
|
|
Toshihiro Shimizu |
890ddd |
//! the task in a header file for moc'ing each time. You must both reimplement
|
|
Toshihiro Shimizu |
890ddd |
//! \b and connect it to the started() signal to make it work.
|
|
Shinya Kitaoka |
120a6e |
void Runnable::onStarted(RunnableP) {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! The analogous of onStarted() for the finished() signal.
|
|
Shinya Kitaoka |
120a6e |
void Runnable::onFinished(RunnableP) {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! The analogous of onStarted() for the exception() signal.
|
|
Shinya Kitaoka |
120a6e |
void Runnable::onException(RunnableP) {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! The analogous of onStarted() for the canceled() signal.
|
|
Shinya Kitaoka |
120a6e |
void Runnable::onCanceled(RunnableP) {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! The analogous of onStarted() for the terminated() signal.
|
|
Shinya Kitaoka |
120a6e |
void Runnable::onTerminated(RunnableP) {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==========================
|
|
Toshihiro Shimizu |
890ddd |
// ExecutorId methods
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
ExecutorId::ExecutorId()
|
|
Shinya Kitaoka |
120a6e |
: m_activeTasks(0)
|
|
Shinya Kitaoka |
120a6e |
, m_maxActiveTasks(1)
|
|
Shinya Kitaoka |
120a6e |
, m_activeLoad(0)
|
|
Shinya Kitaoka |
120a6e |
, m_maxActiveLoad((std::numeric_limits<int>::max)())</int>
|
|
Shinya Kitaoka |
120a6e |
, m_dedicatedThreads(false)
|
|
Shinya Kitaoka |
120a6e |
, m_persistentThreads(false) {
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
m_id = globalImp->m_executorIdPool.acquire();
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_waitingFlagsPool.resize(globalImp->m_executorIdPool.size());
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
ExecutorId::~ExecutorId() {
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
if (m_dedicatedThreads) {
|
|
Shinya Kitaoka |
120a6e |
m_persistentThreads = 0;
|
|
Shinya Kitaoka |
120a6e |
refreshDedicatedList();
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_executorIdPool.release(m_id);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Make sure that sleeping workers are eliminated properly if the permanent
|
|
Shinya Kitaoka |
120a6e |
// workers count decreases.
|
|
Shinya Kitaoka |
120a6e |
void ExecutorId::refreshDedicatedList() {
|
|
Shinya Kitaoka |
120a6e |
// QMutexLocker transitionLocker(&globalImp->m_transitionMutex); //Already
|
|
Shinya Kitaoka |
120a6e |
// covered
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
if (!m_dedicatedThreads || !m_persistentThreads) {
|
|
Shinya Kitaoka |
120a6e |
// Release all sleeping workers
|
|
Shinya Kitaoka |
120a6e |
// Wake them - they will exit on their own
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
unsigned int i, size = m_sleepings.size();
|
|
Shinya Kitaoka |
120a6e |
for (i = 0; i < size; ++i) {
|
|
Shinya Kitaoka |
120a6e |
m_sleepings[i]->m_exit = true;
|
|
Shinya Kitaoka |
120a6e |
m_sleepings[i]->m_waitCondition.wakeOne();
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
m_sleepings.clear();
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//===========================
|
|
Toshihiro Shimizu |
890ddd |
// Worker methods
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
Worker::Worker() : QThread(), m_task(0), m_master(0), m_exit(true) {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
Worker::~Worker() {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void Worker::run() {
|
|
Shinya Kitaoka |
120a6e |
// Ensure atomicity of worker's state transitions
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker sl(&globalImp->m_transitionMutex);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
if (shutdownVar) return;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
for (;;) {
|
|
Shinya Kitaoka |
120a6e |
// Run the taken task
|
|
Shinya Kitaoka |
120a6e |
setPriority(m_task->runningPriority());
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
try {
|
|
Shinya Kitaoka |
120a6e |
Q_EMIT m_task->started(m_task);
|
|
Shinya Kitaoka |
120a6e |
sl.unlock();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
m_task->run();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
sl.relock();
|
|
Shinya Kitaoka |
120a6e |
Q_EMIT m_task->finished(m_task);
|
|
Shinya Kitaoka |
120a6e |
} catch (...) {
|
|
Shinya Kitaoka |
120a6e |
sl.relock(); // throw must be in the run() block
|
|
Shinya Kitaoka |
120a6e |
Q_EMIT m_task->exception(m_task);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
updateCountsOnRelease();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
if (shutdownVar) return;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Get the next task
|
|
Shinya Kitaoka |
120a6e |
takeTask();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
if (!m_task) {
|
|
Shinya Kitaoka |
120a6e |
onFinish();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
if (!m_exit && !shutdownVar) {
|
|
Shinya Kitaoka |
120a6e |
// Put the worker to sleep
|
|
Shinya Kitaoka |
120a6e |
m_waitCondition.wait(sl.mutex());
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Upon thread destruction the wait condition is implicitly woken up.
|
|
Shinya Kitaoka |
120a6e |
// If this is the case, m_task == 0 and we return.
|
|
Shinya Kitaoka |
120a6e |
if (!m_task || shutdownVar) return;
|
|
Shinya Kitaoka |
120a6e |
} else
|
|
Shinya Kitaoka |
120a6e |
return;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void Worker::updateCountsOnTake() {
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_activeLoad += m_task->m_load;
|
|
Shinya Kitaoka |
120a6e |
m_task->m_id->m_activeLoad += m_task->m_load;
|
|
Shinya Kitaoka |
120a6e |
++m_task->m_id->m_activeTasks;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void Worker::updateCountsOnRelease() {
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_activeLoad -= m_task->m_load;
|
|
Shinya Kitaoka |
120a6e |
m_task->m_id->m_activeLoad -= m_task->m_load;
|
|
Shinya Kitaoka |
120a6e |
--m_task->m_id->m_activeTasks;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void Worker::onFinish() {
|
|
Shinya Kitaoka |
120a6e |
if (m_master && m_master->m_dedicatedThreads &&
|
|
Shinya Kitaoka |
120a6e |
m_master->m_persistentThreads) {
|
|
Shinya Kitaoka |
120a6e |
m_exit = false;
|
|
Shinya Kitaoka |
120a6e |
m_master->m_sleepings.push_back(this);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Unlock the mutex - since eventually invoked ~ExecutorId will relock it...
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_transitionMutex.unlock();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
m_master =
|
|
Shinya Kitaoka |
120a6e |
0; // Master may be destroyed here - and m_exit= true for all sleepings
|
|
Shinya Kitaoka |
120a6e |
// in that case
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_transitionMutex.lock();
|
|
Shinya Kitaoka |
120a6e |
} else {
|
|
Shinya Kitaoka |
120a6e |
m_exit = true;
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_workers.erase(this);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//===========================
|
|
Toshihiro Shimizu |
890ddd |
// Executor methods
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
Executor::Executor() : m_id(new ExecutorId) { m_id->addRef(); }
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
Executor::~Executor() { m_id->release(); }
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! This static method declares the use of the Executor's task manager into
|
|
Toshihiro Shimizu |
890ddd |
//! the application code. Be sure to use it according to the following rules:
|
|
Toshihiro Shimizu |
890ddd |
//!
|
|
Toshihiro Shimizu |
890ddd |
//! Only QCoreApplications or QApplications may use Executors.
|
|
Shinya Kitaoka |
120a6e |
//! This method must be invoked in a thread which performs constant Qt
|
|
Shinya Kitaoka |
120a6e |
//! event
|
|
Toshihiro Shimizu |
890ddd |
//! processing - like the main loop of interactive GUI applications.
|
|
Toshihiro Shimizu |
890ddd |
//! No task processing is allowed after event processing stops.
|
|
Toshihiro Shimizu |
890ddd |
//!
|
|
Shinya Kitaoka |
120a6e |
void Executor::init() {
|
|
Shinya Kitaoka |
120a6e |
// If no global ExecutorImp exists, allocate it now. You may not move this
|
|
Shinya Kitaoka |
120a6e |
// to a static declaration, since ExecutorImpSlots's connections must be
|
|
Shinya Kitaoka |
120a6e |
// made once the QCoreApplication has been constructed.
|
|
Shinya Kitaoka |
120a6e |
if (!globalImp) {
|
|
Shinya Kitaoka |
120a6e |
globalImp = new ExecutorImp;
|
|
Shinya Kitaoka |
120a6e |
globalImpSlots = new ExecutorImpSlots;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
qRegisterMetaType<tthread::runnablep>("TThread::RunnableP");</tthread::runnablep>
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
//! This static method, which \b must be invoked in the controller thread,
|
|
Shinya Kitaoka |
120a6e |
//! declares
|
|
Shinya Kitaoka |
120a6e |
//! termination of all Executor-based components, forcing the execution of tasks
|
|
Shinya Kitaoka |
120a6e |
//! submitted
|
|
Toshihiro Shimizu |
890ddd |
//! by any Executor to quit as soon as possible in a safe way.
|
|
Shinya Kitaoka |
120a6e |
//! When the shutdown method is invoked, the task manager first emits a
|
|
Shinya Kitaoka |
120a6e |
//! canceled()
|
|
Shinya Kitaoka |
120a6e |
//! signal for all the tasks that were submitted to it, independently from the
|
|
Shinya Kitaoka |
120a6e |
//! Executor that
|
|
Shinya Kitaoka |
120a6e |
//! performed the submission; then, tasks that are still active once all the
|
|
Shinya Kitaoka |
120a6e |
//! cancellation signals
|
|
Shinya Kitaoka |
120a6e |
//! were delivered further receive a terminated() signal informing them that
|
|
Shinya Kitaoka |
120a6e |
//! they must provide
|
|
Shinya Kitaoka |
120a6e |
//! code termination (or at least remain silent in a safe state until the
|
|
Shinya Kitaoka |
120a6e |
//! application quits).
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
//! \b NOTE: Observe that this method does not explicitly wait for all the tasks
|
|
Shinya Kitaoka |
120a6e |
//! to terminate - this depends
|
|
Shinya Kitaoka |
120a6e |
//! on the code connected to the terminated() signal and is under the user's
|
|
Shinya Kitaoka |
120a6e |
//! responsibility (see the
|
|
luz paz |
6454c4 |
//! remarks specified in started() signal description); if this is the intent
|
|
Shinya Kitaoka |
120a6e |
//! and the terminated slot
|
|
Shinya Kitaoka |
120a6e |
//! is invoked in the controller thread, you should remember to implement a
|
|
Shinya Kitaoka |
120a6e |
//! local event loop in it (so that
|
|
Shinya Kitaoka |
120a6e |
//! event processing is still performed) and wait there until the first
|
|
Shinya Kitaoka |
120a6e |
//! finished() or catched()
|
|
Toshihiro Shimizu |
890ddd |
//! slot make it quit.
|
|
Shinya Kitaoka |
120a6e |
void Executor::shutdown() {
|
|
Shinya Kitaoka |
120a6e |
{
|
|
Shinya Kitaoka |
120a6e |
// Updating tasks list - lock against state transitions
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
shutdownVar = true;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Cancel all tasks - first the active ones
|
|
Shinya Kitaoka |
120a6e |
std::set<worker *="">::iterator it;</worker>
|
|
Shinya Kitaoka |
120a6e |
for (it = globalImp->m_workers.begin(); it != globalImp->m_workers.end();
|
|
Shinya Kitaoka |
120a6e |
++it) {
|
|
Shinya Kitaoka |
120a6e |
RunnableP task = (*it)->m_task;
|
|
Shinya Kitaoka |
120a6e |
if (task) Q_EMIT task->canceled(task);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Finally, deal with the global queue tasks
|
|
Shinya Kitaoka |
120a6e |
QMutableMapIterator<int, runnablep=""> jt(globalImp->m_tasks);</int,>
|
|
Shinya Kitaoka |
120a6e |
while (jt.hasNext()) {
|
|
Shinya Kitaoka |
120a6e |
jt.next();
|
|
Shinya Kitaoka |
120a6e |
RunnableP task = jt.value();
|
|
Shinya Kitaoka |
120a6e |
Q_EMIT task->canceled(task);
|
|
Shinya Kitaoka |
120a6e |
jt.remove();
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Now, send the terminate() signal to all active tasks
|
|
Shinya Kitaoka |
120a6e |
for (it = globalImp->m_workers.begin(); it != globalImp->m_workers.end();
|
|
Shinya Kitaoka |
120a6e |
++it) {
|
|
Shinya Kitaoka |
120a6e |
RunnableP task = (*it)->m_task;
|
|
Shinya Kitaoka |
120a6e |
if (task) Q_EMIT task->terminated(task);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Just placing a convenience processEvents() to make sure that queued slots
|
|
Shinya Kitaoka |
120a6e |
// invoked by the
|
|
Shinya Kitaoka |
120a6e |
// signals above are effectively invoked in this method - without having to
|
|
Shinya Kitaoka |
120a6e |
// return to an event loop.
|
|
Shinya Kitaoka |
120a6e |
QCoreApplication::processEvents();
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Specifies the use of dedicated threads for the Executor's task group.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! By default a worker thread attempts adoption of Runnable tasks
|
|
Toshihiro Shimizu |
890ddd |
//! without regard to the Executor that performed the submission. This helps
|
|
Toshihiro Shimizu |
890ddd |
//! in stabilizing the number of threads that are created and destroyed
|
|
Toshihiro Shimizu |
890ddd |
//! by the task manager - but may be a problem in some cases.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Using this method the user can explicitly tell the Executor to seize the
|
|
Toshihiro Shimizu |
890ddd |
//! ownership of worker threads assigned to its tasks, so that they will not
|
|
Toshihiro Shimizu |
890ddd |
//! try adoption of external tasks but instead remain focused on Executor's
|
|
Toshihiro Shimizu |
890ddd |
//! tasks only.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! An optional \b persistent parameter may be passed, which specifies if
|
|
Toshihiro Shimizu |
890ddd |
//! dedicated threads should remain sleeping or should rather die when no
|
|
Toshihiro Shimizu |
890ddd |
//! processable tasks from the Executor are found.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! This method is especially helpful in two occasions:
|
|
Toshihiro Shimizu |
890ddd |
//!
|
|
Toshihiro Shimizu |
890ddd |
//! The Executor's tasks use thread-specific data such as QThreadStorages,
|
|
Toshihiro Shimizu |
890ddd |
//! which may be recycled among different tasks.
|
|
Shinya Kitaoka |
120a6e |
//! The Executor receives tasks at a frequent rate, but mostly ends each
|
|
Shinya Kitaoka |
120a6e |
//! one before
|
|
Toshihiro Shimizu |
890ddd |
//! another one is submitted - resulting in a continuous thread turnover.
|
|
Toshihiro Shimizu |
890ddd |
//!
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void Executor::setDedicatedThreads(bool dedicated, bool persistent) {
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
m_id->m_dedicatedThreads = dedicated;
|
|
Shinya Kitaoka |
120a6e |
m_id->m_persistentThreads = persistent;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
m_id->refreshDedicatedList();
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Submits a task for execution. The task is executed according to
|
|
Toshihiro Shimizu |
890ddd |
//! its task load, insertion time and scheduling priority.
|
|
Shinya Kitaoka |
120a6e |
void Executor::addTask(RunnableP task) {
|
|
Shinya Kitaoka |
120a6e |
{
|
|
Shinya Kitaoka |
120a6e |
if (task->m_id) // Must be done outside transition lock, since eventually
|
|
Shinya Kitaoka |
120a6e |
task->m_id->release(); // invoked ~ExecutorId will lock it
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Updating tasks and workers list - lock against state transitions
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
task->m_id = m_id;
|
|
Shinya Kitaoka |
120a6e |
m_id->addRef();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
globalImp->insertTask(task->schedulingPriority(), task);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// If addTask is called in the main thread, the emit works directly -
|
|
Shinya Kitaoka |
120a6e |
// so it is necessary to unlock the mutex *before* emitting the refresh.
|
|
Shinya Kitaoka |
120a6e |
globalImpSlots->emitRefreshAssignments();
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Removes the given task from scheduled execution and emits its
|
|
Toshihiro Shimizu |
890ddd |
//! Runnable::canceled signal. Tasks already under execution are not
|
|
Toshihiro Shimizu |
890ddd |
//! stopped by this method - although the canceled signal is still emitted.
|
|
Shinya Kitaoka |
120a6e |
//! It has no effect if the task is not currently under the task manager's
|
|
Shinya Kitaoka |
120a6e |
//! control.
|
|
Toshihiro Shimizu |
890ddd |
//! \sa \b Runnable::canceled signal and the \b cancelAll method.
|
|
Shinya Kitaoka |
120a6e |
void Executor::removeTask(RunnableP task) {
|
|
Shinya Kitaoka |
120a6e |
// If the task does not belong to this Executor, quit.
|
|
Shinya Kitaoka |
120a6e |
if (task->m_id != m_id) return;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Updating tasks list - lock against state transitions
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Then, look in the global queue - if it is found, emiminate the task and
|
|
Shinya Kitaoka |
120a6e |
// send the canceled signal.
|
|
Shinya Kitaoka |
120a6e |
if (globalImp->m_tasks.remove(task->m_schedulingPriority, task)) {
|
|
Shinya Kitaoka |
120a6e |
Q_EMIT task->canceled(task);
|
|
Shinya Kitaoka |
120a6e |
return;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Finally, the task may be running - look in workers.
|
|
Shinya Kitaoka |
120a6e |
std::set<worker *=""> &workers = globalImp->m_workers;</worker>
|
|
Shinya Kitaoka |
120a6e |
std::set<worker *="">::iterator it;</worker>
|
|
Shinya Kitaoka |
120a6e |
for (it = workers.begin(); it != workers.end(); ++it)
|
|
Shinya Kitaoka |
120a6e |
if (task && (*it)->m_task == task) Q_EMIT task->canceled(task);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// No need to refresh - tasks were eventually decremented...
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Clears the task manager of all tasks added by this Executor and emits
|
|
Toshihiro Shimizu |
890ddd |
//! the Runnable::canceled signal for each of them. The same specifications
|
|
Toshihiro Shimizu |
890ddd |
//! described in the \b removeTask method apply here.
|
|
Toshihiro Shimizu |
890ddd |
//! \sa \b Runnable::canceled signal and the \b removeTask method.
|
|
Shinya Kitaoka |
120a6e |
void Executor::cancelAll() {
|
|
Shinya Kitaoka |
120a6e |
// Updating tasks list - lock against state transitions
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Clear the tasks chronologically. So, first check currently working
|
|
Shinya Kitaoka |
120a6e |
// tasks.
|
|
Shinya Kitaoka |
120a6e |
std::set<worker *="">::iterator it;</worker>
|
|
Shinya Kitaoka |
120a6e |
for (it = globalImp->m_workers.begin(); it != globalImp->m_workers.end();
|
|
Shinya Kitaoka |
120a6e |
++it) {
|
|
Shinya Kitaoka |
120a6e |
RunnableP task = (*it)->m_task;
|
|
Shinya Kitaoka |
120a6e |
if (task && task->m_id == m_id) Q_EMIT task->canceled(task);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Finally, clear the global tasks list from all tasks inserted by this
|
|
Shinya Kitaoka |
120a6e |
// executor
|
|
Shinya Kitaoka |
120a6e |
// NOTE: An easier way here?
|
|
Shinya Kitaoka |
120a6e |
QMutableMapIterator<int, runnablep=""> jt(globalImp->m_tasks);</int,>
|
|
Shinya Kitaoka |
120a6e |
while (jt.hasNext()) {
|
|
Shinya Kitaoka |
120a6e |
jt.next();
|
|
Shinya Kitaoka |
120a6e |
if (jt.value()->m_id == m_id) {
|
|
Shinya Kitaoka |
120a6e |
RunnableP task = jt.value();
|
|
Shinya Kitaoka |
120a6e |
Q_EMIT task->canceled(task);
|
|
Shinya Kitaoka |
120a6e |
jt.remove();
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Declares that only a certain number of tasks added by this Executor
|
|
Toshihiro Shimizu |
890ddd |
//! may be processed simultaneously. The default is 1 - meaning that tasks
|
|
Toshihiro Shimizu |
890ddd |
//! added to the executor are completely serialized. A negative task number
|
|
Toshihiro Shimizu |
890ddd |
//! disables any form of task serialization.
|
|
Toshihiro Shimizu |
890ddd |
//! \b NOTE: Currently, tasks that do not
|
|
Toshihiro Shimizu |
890ddd |
//! satisfy this condition avoid blocking execution of tasks not
|
|
Toshihiro Shimizu |
890ddd |
//! added by the same Executor - even if they were scheduled for later
|
|
Toshihiro Shimizu |
890ddd |
//! execution.
|
|
Shinya Kitaoka |
120a6e |
void Executor::setMaxActiveTasks(int maxActiveTasks) {
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (maxActiveTasks <= 0)
|
|
Shinya Kitaoka |
120a6e |
m_id->m_maxActiveTasks = (std::numeric_limits<int>::max)();</int>
|
|
Shinya Kitaoka |
120a6e |
else
|
|
Shinya Kitaoka |
120a6e |
m_id->m_maxActiveTasks = maxActiveTasks;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int Executor::maxActiveTasks() const {
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Shinya Kitaoka |
120a6e |
return m_id->m_maxActiveTasks;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//! Declares a maximum overall task load for the tasks added by this Executor.
|
|
Toshihiro Shimizu |
890ddd |
//! \b NOTE: The same remark for setMaxActiveTasks() holds here.
|
|
Shinya Kitaoka |
120a6e |
void Executor::setMaxActiveLoad(int maxActiveLoad) {
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
m_id->m_maxActiveLoad = maxActiveLoad;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int Executor::maxActiveLoad() const {
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Shinya Kitaoka |
120a6e |
return m_id->m_maxActiveLoad;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//==================================
|
|
Toshihiro Shimizu |
890ddd |
// ExecutorImpSlots methods
|
|
Toshihiro Shimizu |
890ddd |
//----------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
ExecutorImpSlots::ExecutorImpSlots() {
|
|
Shinya Kitaoka |
120a6e |
connect(this, SIGNAL(refreshAssignments()), this,
|
|
Shinya Kitaoka |
120a6e |
SLOT(onRefreshAssignments()));
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
ExecutorImpSlots::~ExecutorImpSlots() {}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void ExecutorImpSlots::emitRefreshAssignments() { Q_EMIT refreshAssignments(); }
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void ExecutorImpSlots::onRefreshAssignments() {
|
|
Shinya Kitaoka |
120a6e |
QMutexLocker transitionLocker(&globalImp->m_transitionMutex);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
globalImp->refreshAssignments();
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void ExecutorImpSlots::onTerminated() { delete QObject::sender(); }
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//=====================================================================
|
|
Toshihiro Shimizu |
890ddd |
// Task adoption methods
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void ExecutorId::newWorker(RunnableP &task) {
|
|
Shinya Kitaoka |
120a6e |
Worker *worker;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (m_sleepings.size()) {
|
|
Shinya Kitaoka |
120a6e |
worker = m_sleepings.front();
|
|
Shinya Kitaoka |
120a6e |
m_sleepings.pop_front();
|
|
Shinya Kitaoka |
120a6e |
worker->m_task = task;
|
|
Shinya Kitaoka |
120a6e |
worker->updateCountsOnTake();
|
|
Shinya Kitaoka |
120a6e |
worker->m_waitCondition.wakeOne();
|
|
Shinya Kitaoka |
120a6e |
} else {
|
|
Shinya Kitaoka |
120a6e |
worker = new Worker;
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_workers.insert(worker);
|
|
Shinya Kitaoka |
120a6e |
QObject::connect(worker, SIGNAL(finished()), globalImpSlots,
|
|
Shinya Kitaoka |
120a6e |
SLOT(onTerminated()));
|
|
Shinya Kitaoka |
120a6e |
worker->m_task = task;
|
|
Shinya Kitaoka |
120a6e |
worker->updateCountsOnTake();
|
|
Shinya Kitaoka |
120a6e |
worker->start();
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline void Worker::adoptTask(RunnableP &task) {
|
|
Shinya Kitaoka |
120a6e |
m_task = task;
|
|
Shinya Kitaoka |
120a6e |
updateCountsOnTake();
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// * Le task timedOut sono ex-accumulate che non soddisfano le condizioni
|
|
Toshihiro Shimizu |
890ddd |
// standard. Quindi sono bloccanti *ovunque*.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// * Il refresh dei worker sulle task accumulate *deve* avvenire:
|
|
Toshihiro Shimizu |
890ddd |
// -Se e solo se una task dello stesso executor finisce,
|
|
Toshihiro Shimizu |
890ddd |
// perche' e' l'unico caso in cui le custom conditions
|
|
Toshihiro Shimizu |
890ddd |
// vengono aggiornate
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// * Se un thread dedicato non puo' prendere una task estranea, non e' detto
|
|
Toshihiro Shimizu |
890ddd |
// che altri non possano!
|
|
Toshihiro Shimizu |
890ddd |
// * Le task che richiedono dedizione dovrebbero essere adottate da thread
|
|
Toshihiro Shimizu |
890ddd |
// gia' dedicati, se esistono! => Gli executor che richiedono thread dedicati
|
|
Toshihiro Shimizu |
890ddd |
// li creano a parte e non li condividono con nessuno: cioe', thread creati
|
|
Toshihiro Shimizu |
890ddd |
// senza dedizione non devono poter adottare task richiedenti dedizione!
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Assigns tasks polled from the id's accumulation queue (if id is given) and
|
|
Shinya Kitaoka |
120a6e |
// the global tasks queue.
|
|
Shinya Kitaoka |
120a6e |
// It works like:
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// a) First look if there exist tasks with timedOut priority and if so
|
|
Toshihiro Shimizu |
890ddd |
// try to take them out
|
|
Toshihiro Shimizu |
890ddd |
// b) Then look for tasks in the id's accumulation queue
|
|
Toshihiro Shimizu |
890ddd |
// c) Finally search in the remaining global tasks queue
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void ExecutorImp::refreshAssignments() {
|
|
Shinya Kitaoka |
120a6e |
// QMutexLocker transitionLocker(&globalImp->m_transitionMutex); //Already
|
|
Shinya Kitaoka |
120a6e |
// covered
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (m_tasks.isEmpty()) return;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Erase the id vector data
|
|
Shinya Kitaoka |
120a6e |
assert(m_executorIdPool.size() == m_waitingFlagsPool.size());
|
|
Shinya Kitaoka |
120a6e |
memset(&m_waitingFlagsPool.front(), 0, m_waitingFlagsPool.size());
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// c) Try with the global queue
|
|
Shinya Kitaoka |
120a6e |
int e, executorsCount = m_executorIdPool.acquiredSize();
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
int i, tasksCount = m_tasks.size();
|
|
Shinya Kitaoka |
120a6e |
QMultiMap<int, runnablep="">::iterator it;</int,>
|
|
Shinya Kitaoka |
120a6e |
for (i = 0, e = 0, it = m_tasks.end() - 1;
|
|
Shinya Kitaoka |
120a6e |
i < tasksCount && e < executorsCount; ++i, --it) {
|
|
Shinya Kitaoka |
120a6e |
// std::cout<< "global tasks-refreshAss" << std::endl;
|
|
Shinya Kitaoka |
120a6e |
// Take the task
|
|
Shinya Kitaoka |
120a6e |
RunnableP task = it.value();
|
|
Shinya Kitaoka |
120a6e |
task->m_load = task->taskLoad();
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
UCHAR &idWaitingForAnotherTask = m_waitingFlagsPool[task->m_id->m_id];
|
|
Shinya Kitaoka |
120a6e |
if (idWaitingForAnotherTask) continue;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!isExecutable(task)) break;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!task->customConditions()) {
|
|
Shinya Kitaoka |
120a6e |
++e;
|
|
Shinya Kitaoka |
120a6e |
idWaitingForAnotherTask = 1;
|
|
Shinya Kitaoka |
120a6e |
} else {
|
|
Shinya Kitaoka |
120a6e |
task->m_id->newWorker(task);
|
|
Shinya Kitaoka |
120a6e |
it = m_tasks.erase(it);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
inline bool Worker::canAdopt(const RunnableP &task) {
|
|
Shinya Kitaoka |
120a6e |
return task->m_id->m_sleepings.size() ==
|
|
Shinya Kitaoka |
120a6e |
0 && // Always prefer sleeping dedicateds if present
|
|
Shinya Kitaoka |
120a6e |
(!m_master || (m_master.getPointer() == task->m_id)); // If was seized
|
|
Shinya Kitaoka |
120a6e |
// by an
|
|
Shinya Kitaoka |
120a6e |
// Executor,
|
|
Shinya Kitaoka |
120a6e |
// ensure task
|
|
Shinya Kitaoka |
120a6e |
// compatibility
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//---------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Takes a task and assigns it to the worker in a way similar to the one above.
|
|
Shinya Kitaoka |
120a6e |
inline void Worker::takeTask() {
|
|
Shinya Kitaoka |
120a6e |
TSmartPointerT<executorid> oldId = m_task->m_id;</executorid>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// When a new task is taken, the old one's Executor may seize the worker
|
|
Shinya Kitaoka |
120a6e |
m_master = oldId->m_dedicatedThreads ? oldId : (TSmartPointerT<executorid>)0;</executorid>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// c) No accumulated task can be taken - look for a task in the global tasks
|
|
Shinya Kitaoka |
120a6e |
// queue.
|
|
Shinya Kitaoka |
120a6e |
// If the active load admits it, take the earliest task.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Free the old task. NOTE: This instruction MUST be performed OUTSIDE the
|
|
Shinya Kitaoka |
120a6e |
// mutex-protected environment -
|
|
Shinya Kitaoka |
120a6e |
// since user code may be executed upon task destruction - including the mutex
|
|
Shinya Kitaoka |
120a6e |
// relock!!
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_transitionMutex.unlock();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
m_task = 0;
|
|
Shinya Kitaoka |
120a6e |
oldId = TSmartPointerT<executorid>();</executorid>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
globalImp->m_transitionMutex.lock();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Erase the executor id status pool
|
|
Shinya Kitaoka |
120a6e |
tcg::indices_pool<> &executorIdPool = globalImp->m_executorIdPool;
|
|
Shinya Kitaoka |
120a6e |
std::vector<uchar> &waitingFlagsPool = globalImp->m_waitingFlagsPool;</uchar>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
assert(waitingFlagsPool.size() == globalImp->m_executorIdPool.size());
|
|
Shinya Kitaoka |
120a6e |
memset(&waitingFlagsPool.front(), 0, waitingFlagsPool.size());
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int e, executorsCount = executorIdPool.acquiredSize();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int i, tasksCount = globalImp->m_tasks.size();
|
|
Shinya Kitaoka |
120a6e |
QMultiMap<int, runnablep="">::iterator it;</int,>
|
|
Shinya Kitaoka |
120a6e |
for (i = 0, e = 0, it = globalImp->m_tasks.end() - 1;
|
|
Shinya Kitaoka |
120a6e |
i < tasksCount && e < executorsCount; ++i, --it) {
|
|
Shinya Kitaoka |
120a6e |
// std::cout<< "global tasks-takeTask" << std::endl;
|
|
Shinya Kitaoka |
120a6e |
// Take the first task
|
|
Shinya Kitaoka |
120a6e |
RunnableP task = it.value();
|
|
Shinya Kitaoka |
120a6e |
task->m_load = task->taskLoad();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
UCHAR &idWaitingForAnotherTask = waitingFlagsPool[task->m_id->m_id];
|
|
Shinya Kitaoka |
120a6e |
if (idWaitingForAnotherTask) continue;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
if (!globalImp->isExecutable(task)) break;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// In case the worker was captured for dedication, check the task
|
|
Shinya Kitaoka |
120a6e |
// compatibility.
|
|
Shinya Kitaoka |
120a6e |
if (!canAdopt(task)) {
|
|
Shinya Kitaoka |
120a6e |
// some other worker may still take the task...
|
|
Shinya Kitaoka |
120a6e |
globalImpSlots->emitRefreshAssignments();
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// Test its custom conditions
|
|
Shinya Kitaoka |
120a6e |
if (!task->customConditions()) {
|
|
Shinya Kitaoka |
120a6e |
++e;
|
|
Shinya Kitaoka |
120a6e |
idWaitingForAnotherTask = 1;
|
|
Shinya Kitaoka |
120a6e |
} else {
|
|
Shinya Kitaoka |
120a6e |
adoptTask(task);
|
|
Shinya Kitaoka |
120a6e |
it = globalImp->m_tasks.erase(it);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
globalImpSlots->emitRefreshAssignments();
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|