1 #pragma once 2 3 #ifndef TTHREAD_H 4 #define TTHREAD_H 5 6 #include "tsmartpointer.h" 7 8 #include <QThread> 9 10 #undef DVAPI 11 #undef DVVAR 12 #ifdef TNZCORE_EXPORTS 13 #define DVAPI DV_EXPORT_API 14 #define DVVAR DV_EXPORT_VAR 15 #else 16 #define DVAPI DV_IMPORT_API 17 #define DVVAR DV_IMPORT_VAR 18 #endif 19 20 namespace TThread { 21 22 //------------------------------------------------------------------------------ 23 24 //! Initializes all TThread namespace components. 25 void DVAPI init(); 26 27 //! Closes all TThread components as soon as possible in a safe manner. 28 //! \sa Executor::shutdown() method 29 void DVAPI shutdown(); 30 31 //------------------------------------------------------------------------------ 32 33 // Forward declarations 34 class ExecutorId; // Private 35 class Runnable; 36 37 } // namespace TThread 38 #ifdef _WIN32 39 template class DVAPI TSmartPointerT<TThread::Runnable>; 40 #endif 41 namespace TThread { 42 43 typedef TSmartPointerT<Runnable> RunnableP; 44 45 //------------------------------------------------------------------------------ 46 47 /*! 48 Runnable class is the abstract model for user-defined tasks which can be 49 scheduled for execution into separate application threads. 50 51 A user must first implement the run() method to provide a task with the 52 very execution code. Worker threads created internally by the task manager 53 will 54 take ownership of the task and make it run() at the most appropriate time, 55 depending on the task load, insertion time and its scheduling priority. 56 \n \n 57 The scheduling priority of a task can be set by reimplementing the 58 \b schedulingPriority() method. Tasks whose scheduling priority is higher are 59 always started before compared to the others added by the same executor. 60 \n \n 61 The hosting thread's running priority may also be set reimplementing the 62 runningPriority() method; see QThread class documentation in Qt manual. 63 \n \n 64 A task's load is an important property that should be reimplemented in all 65 resource-consuming tasks. It enables the user to declare the approximate 66 CPU load produced by a task, allowing the task manager to effectively 67 calculate the most appropriate moment to run it. 68 69 \warning \n All built-in signals about the Runnable class are emitted in a 70 mutex-protected environment in order to make sure that signal emission is 71 consistent 72 with the task status - in other words, so that \a queued controller-emitted 73 canceled() 74 and terminated() signals are not delivered \a before started() or \a after 75 finished() and exception() worker-emitted signals. 76 77 \warning Thus, setting up blocking connections or blocking direct slots is \b 78 not \b 79 supported and will typically result in deadlocks; furthermore, the same also 80 applies to 81 slot calls to the Executor API, which would have the aforementioned mutex to 82 be 83 locked again. 84 85 \warning In case the above blocking strategies are mandatory, the user should 86 add 87 custom signals to be emitted inside the mutex-free run() block, just before or 88 after 89 the actual code to be executed, like this: 90 91 \code 92 void MyTask::run() 93 { 94 try 95 { 96 emit myStarted(this); 97 theRunCode(); 98 emit myFinished(this); 99 } 100 catch(...) 101 { 102 emit myException(this); 103 throw; 104 } 105 } 106 \endcode 107 \code 108 .. 109 MyTask* myTask = new MyTask; 110 connect(myTask, SIGNAL(myStarted(TThread::RunnableP)), myTask, 111 SLOT(onStarted(TThread::RunnableP)), 112 Qt::BlockingQueuedConnection) //theRunCode() waits for onStarted() to 113 complete 114 .. 115 \endcode 116 117 \sa Executor class. 118 */ 119 class DVAPI Runnable : public QObject, public TSmartObject { 120 Q_OBJECT 121 DECLARE_CLASS_CODE 122 123 ExecutorId *m_id; 124 125 int m_load; 126 int m_schedulingPriority; 127 128 friend class Executor; // Needed to confront Executor's and Runnable's ids 129 friend class ExecutorImp; // The internal task manager needs full control 130 // over the task 131 friend class Worker; // Workers force tasks to emit state signals 132 133 public: 134 Runnable(); 135 virtual ~Runnable(); 136 137 //! The central code of the task that is executed by a worker thread. 138 virtual void run() = 0; 139 140 virtual int taskLoad(); 141 virtual int schedulingPriority(); 142 virtual QThread::Priority runningPriority(); 143 144 Q_SIGNALS: 145 146 void started(TThread::RunnableP sender); 147 void finished(TThread::RunnableP sender); 148 void exception(TThread::RunnableP sender); 149 void canceled(TThread::RunnableP sender); 150 void terminated(TThread::RunnableP sender); 151 152 public Q_SLOTS: 153 154 virtual void onStarted(TThread::RunnableP sender); 155 virtual void onFinished(TThread::RunnableP sender); 156 virtual void onException(TThread::RunnableP sender); 157 virtual void onCanceled(TThread::RunnableP sender); 158 virtual void onTerminated(TThread::RunnableP sender); 159 160 private: 161 inline bool needsAccumulation(); 162 inline bool customConditions(); 163 }; 164 165 //------------------------------------------------------------------------------ 166 167 /*! 168 Executor class provides an effective way for planning the execution of 169 user-defined tasks that require separate working threads. 170 171 When an application needs to perform a resource-consuming task, it is often 172 a good idea to dedicate a separate thread for it, especially in GUI 173 applications; 174 however, doing so eventually raises the problem of managing such intensive 175 tasks in a way that constantly ensures the correct use of the machine 176 resources - 177 so that in any given time the CPU usage is maximal, but not overloaded. 178 Additional requests by the user may arise, including preferenced ordering 179 among tasks, the necessity of salvaging some CPU resources or threads for 180 incoming tasks, and so on. 181 \n \n 182 The TThread namespace API contains two main classes, \b Runnable and \b 183 Executor, 184 which provide a way for implementing consistent threading strategies. 185 \n \n 186 In order to use the Executor class it is first necessary to install the thread 187 manager 188 into application code by calling the static method init() appropriately. 189 \n \n 190 Executors are then used to submit - or eventually remove - tasks for execution 191 into a 192 separate working thread, by means of the \b addTask(), \b removeTask() and \b 193 cancelAll() methods. 194 Each Executor's visibility is always limited to the tasks that it submits - 195 so calling removeTask() or cancelAll() only affects the tasks previously 196 added by that same Executor - which easily reflects the idea of an Executor 197 representing 198 a group of tasks. 199 \n \n 200 Basic control over the execution strategy for the group of tasks submitted by 201 an Executor 202 can be acquired using the setMaxActiveTasks() and setMaxActiveLoad() methods, 203 both granting 204 the possibility to bound the execution of tasks to custom maximum conditions. 205 For example, use setMaxActiveTasks(1) to force the execution of 1 task only at 206 a time, 207 or setMaxActiveLoad(100) to set a single CPU core available for the group. 208 209 \sa \b Runnable class documentation. 210 */ 211 class DVAPI Executor { 212 ExecutorId *m_id; 213 214 friend class ExecutorImp; 215 216 public: 217 Executor(); 218 ~Executor(); 219 220 static void init(); 221 static void shutdown(); 222 223 void addTask(RunnableP task); 224 void removeTask(RunnableP task); 225 void cancelAll(); 226 227 void setMaxActiveTasks(int count); 228 void setMaxActiveLoad(int load); 229 230 int maxActiveTasks() const; 231 int maxActiveLoad() const; 232 233 void setDedicatedThreads(bool dedicated, bool persistent = true); 234 235 private: 236 // not implemented 237 Executor &operator=(const Executor &); 238 Executor(const Executor &); 239 }; 240 241 } // namespace TThread 242 243 #endif // TTHREAD_H 244