00001 /*************************************************************************** 00002 * * 00003 * (c) Art Tevs, MPI Informatik Saarbruecken * 00004 * mailto: <tevs@mpi-sb.mpg.de> * 00005 * * 00006 * This program is free software; you can redistribute it and/or modify * 00007 * it under the terms of the GNU General Public License as published by * 00008 * the Free Software Foundation; either version 2 of the License, or * 00009 * (at your option) any later version. * 00010 * * 00011 ***************************************************************************/ 00012 00013 00014 #ifndef _NR_I_TASK_H_ 00015 #define _NR_I_TASK_H_ 00016 00017 //---------------------------------------------------------------------------------- 00018 // Includes 00019 //---------------------------------------------------------------------------------- 00020 #include "Prerequisities.h" 00021 #include "Priority.h" 00022 #include "IThread.h" 00023 00024 namespace nrEngine{ 00025 00026 /** 00027 * Each task is defined through it's unique ID-number. The numbers are used 00028 * to access task through the kernel.<br> 00029 * There is no tasks with ID 0, because this number is reserved as for no id. 00030 * \ingroup kernel 00031 **/ 00032 typedef uint32 TaskId; 00033 00034 00035 /** 00036 * Each task can be in one of this states. It can be either stopped, running or paused (sleeping) 00037 * \ingroup kernel 00038 **/ 00039 typedef enum { 00040 //! Task is currently stopped. So it was either started nor paused 00041 TASK_STOPPED = 0, 00042 00043 //! Task is currently running, so updates of the task are done 00044 TASK_RUNNING = 1, 00045 00046 //! Task is sleeping. No updates but waiting on resume signal. 00047 TASK_PAUSED = 2, 00048 00049 } TaskState; 00050 00051 /** 00052 * Task can have on of the property defined. 00053 * \ingroup kernel 00054 **/ 00055 typedef enum { 00056 00057 //! Nothing specialy 00058 TASK_NONE = 0, 00059 00060 //! Does this task run in parallel, so it is a thread 00061 TASK_IS_THREAD = 1 << 1, 00062 00063 //! Should this task be executed only once or it is repeating 00064 TASK_RUN_ONCE = 1 << 2 00065 00066 } TaskProperty; 00067 00068 /** 00069 *Engine does support two types of task: system task and user tasks. System tasks 00070 * are task essential for engine working. They can not be handled by the user 00071 * application. User tasks are task handled by the application itself. 00072 * \ingroup kernel 00073 **/ 00074 typedef enum { 00075 //! Root tasks could not be stopped by user, only engine can do this 00076 TASK_SYSTEM, 00077 00078 //! User tasks are task defined by the user 00079 TASK_USER 00080 00081 } TaskType; 00082 00083 00084 /** 00085 * Each task is in the kernel in specified order. This enum describes all order/queue 00086 * positions of any task in the kernel system 00087 * \ingroup kernel 00088 **/ 00089 typedef enum { 00090 //! This number defines the a gap between two adjacent order numbers 00091 ORDER_STEP = 32768, 00092 00093 /** 00094 * This is a order position reserved for system tasks, that should realy run as first. 00095 * You can not add any user task on this order position. 00096 **/ 00097 ORDER_SYS_ROOT = 0, 00098 00099 //! First task 00100 ORDER_SYS_FIRST = 1 * ORDER_STEP, 00101 00102 //! Next after first 00103 ORDER_SYS_SECOND = 2 * ORDER_STEP, 00104 00105 //! Next after first 00106 ORDER_SYS_THIRD = 3 * ORDER_STEP, 00107 00108 //! Next after first 00109 ORDER_SYS_FOURTH = 4 * ORDER_STEP, 00110 00111 //! Next after first 00112 ORDER_SYS_FIVETH = 5 * ORDER_STEP, 00113 00114 //! Greatest order border for system task (only ordering) 00115 ORDER_SYS_LAST = 6 * ORDER_STEP, 00116 00117 //! Task will be updated as first 00118 ORDER_FIRST = 7 * ORDER_STEP, 00119 00120 //! Next after the first 00121 ORDER_ULTRA_HIGH = 8 * ORDER_STEP, 00122 00123 //! Next smaller order number to the ultra high value 00124 ORDER_VERY_HIGH = 9 * ORDER_STEP, 00125 00126 //! Next after very high 00127 ORDER_HIGH = 10 * ORDER_STEP, 00128 00129 //! Default order number (Center point) 00130 ORDER_NORMAL = 11 * ORDER_STEP, 00131 00132 //! Later update as of the normal position 00133 ORDER_LOW = 12 * ORDER_STEP, 00134 00135 //! Updating next after low 00136 ORDER_VERY_LOW = 13 * ORDER_STEP, 00137 00138 //! Ultra low order by comparison to the normal value 00139 ORDER_ULTRA_LOW = 14 * ORDER_STEP, 00140 00141 //! Task having this order will be updated as last 00142 ORDER_LAST = 15 * ORDER_STEP 00143 00144 } TaskOrder; 00145 00146 //! Each component of the engine/application does run as tasks in the Kernel 00147 /** 00148 * \par 00149 * ITask - is an interface for tasks of our kernel system. Kernel runs all 00150 * specified and added task in their queue order (think on OS). So ITask 00151 * Interface defines functions to be implemented by each task. 00152 * 00153 * \par 00154 * There is a rule to understanding order numbers: 00155 * - Each order-number is a number 00156 * - Bigger number - task will be executed later 00157 * - Between each defined default order-numbers there is ORDER_STEP numbers available 00158 * - There is no guarantee that two or more tasks with same order would be 00159 * executed in next cycle in same order 00160 * - you can increase/decrease the order by substracting/adding of any number to any 00161 * predefined order state 00162 * - Task names must be unique 00163 * 00164 * \par 00165 * Sometimes tasks depends on each other. So for example on task should be 00166 * executed before another one. One way to solve this problem is to set the 00167 * order number of each task manually. Other way is to use the dependencies 00168 * infrastructure provided by task interface. You can setup task through it's 00169 * ids to define tasks on which this one depends. Kernel will try to solve all 00170 * dependencies and will bring all dependeable tasks in the right order according 00171 * to their order numbers. If there is circular dependencies, so both tasks will not 00172 * be added to the kernel's task list and an error code according to that problem will 00173 * be given back.<br> 00174 * - if task A depends on task B, so task A will be updated after the task B. 00175 * - if task A depends on task B and order number of task A is greater than B, 00176 * so B will be updated before A, but the ordering number will not be changed. 00177 * 00178 * \par 00179 * Each task should support error handling routines. Tasks are running in the try-catch 00180 * block. If kernel will catch any error by updating the task, the \a ITask::taskHandleError() 00181 * function will be called. You have either to handle the error there or to pass the 00182 * error back. Kernel will then send up the error along the function stack.<br> 00183 * If the task couldn't handle the error it will be stopped and will be removed from 00184 * the kernel task list. So each task object should be able to release all used memory 00185 * also if there was an error before in the task update function.<br> 00186 * This infrastructure allows us to catch up and to handle errors produced by the tasks. 00187 * We can also remove tasks which couldn't handle their own errors. If you implement 00188 * the task in the right way, then we can guarantee that no tasks would crash the kernel. 00189 * 00190 * \ingroup kernel 00191 **/ 00192 class _NRExport ITask : public IThread{ 00193 public: 00194 00195 /** 00196 * Virtual destructor to allow to derive new classes from this one 00197 **/ 00198 virtual ~ITask(); 00199 00200 /** 00201 * One task is smaller than another one if it's order number is smaller 00202 **/ 00203 bool operator < (const ITask &t); 00204 00205 /** 00206 * Two tasks are the same if their order number are equal or they are the same objects 00207 **/ 00208 bool operator == (const ITask &t); 00209 00210 bool operator <= (const ITask &t); 00211 bool operator > (const ITask &t); 00212 bool operator >= (const ITask &t); 00213 bool operator != (const ITask &t); 00214 00215 00216 /** 00217 * In each cycle of our game loop this method will be called if task was added to our kernel. 00218 **/ 00219 virtual Result updateTask() = 0; 00220 00221 /** 00222 * Stop the task before task will be killed. Each derived object should 00223 * release used memory here or in the destructor. 00224 **/ 00225 virtual Result stopTask() { return OK; } 00226 00227 /** 00228 * This method will be called by the kernel as soon as the task is prepared 00229 * to run. So it will be started and in the next cycle it will be updated. 00230 **/ 00231 virtual Result onStartTask() { return OK; } 00232 00233 /** 00234 * This method will be called by kernel when the kernel will try to add 00235 * this task into the kernel's pipeline. If this method return other value 00236 * then OK, so the task will not be added into pipeline. 00237 **/ 00238 virtual Result onAddTask() { return OK; } 00239 00240 /** 00241 * Will be executed on waiking up the task from sleeping. 00242 **/ 00243 virtual Result onResumeTask() { return OK; } 00244 00245 /** 00246 * Kernel will call this method if task will be aked to go for sleeping. 00247 **/ 00248 virtual Result onSuspendTask() { return OK; } 00249 00250 /** 00251 * Task should return his name. Name must not be unique. We will need this to read 00252 * Log-Files in simple way. 00253 **/ 00254 const char* getTaskName(){ return _taskName.c_str(); } 00255 00256 /** 00257 * Get order number of this task 00258 **/ 00259 NR_FORCEINLINE TaskOrder getTaskOrder() const { return _taskOrder; } 00260 00261 /** 00262 * Get id number of the task 00263 **/ 00264 NR_FORCEINLINE TaskId getTaskID() const { return _taskID; } 00265 00266 /** 00267 * Get the type of the task. 00268 **/ 00269 NR_FORCEINLINE TaskType getTaskType() const { return _taskType; } 00270 00271 /** 00272 * Get the state in which the task is 00273 **/ 00274 NR_FORCEINLINE TaskState getTaskState() const { return _taskState; } 00275 00276 /** 00277 * Get task property 00278 **/ 00279 NR_FORCEINLINE TaskProperty getTaskProperty() const { return _taskProperty; } 00280 00281 #if 0 00282 /** 00283 * Add a new task id of a task on which one this task depends. 00284 * 00285 * @param id Unique ID of a task on which one this depends 00286 * @return either OK or: 00287 * - 00288 **/ 00289 Result addTaskDependency(TaskId id); 00290 00291 /** 00292 * Overloaded function that allows to add a dependency from the task itself 00293 **/ 00294 Result addTaskDependency(const ITask& task); 00295 00296 /** 00297 * Overloded function that allows to add a dependency from the pointer on a task 00298 **/ 00299 Result addTaskDependency(const ITask* pTask); 00300 #endif 00301 00302 /** 00303 * Add a new task on which one this task depends. 00304 * 00305 * @param task Smart poitner to the task 00306 * @return either OK or: 00307 **/ 00308 Result addTaskDependency(SharedPtr<ITask> task); 00309 00310 /** 00311 * Add a new task on which one this task depends. 00312 * 00313 * @param id Unique ID of a task on which one this depends 00314 * @return either OK or: 00315 * - 00316 **/ 00317 Result addTaskDependency(TaskId id); 00318 00319 /** 00320 * Add a new task on which one this task depends. 00321 * 00322 * @param name Unique name of a task on which one this depends 00323 * @return either OK or: 00324 * - 00325 **/ 00326 Result addTaskDependency(const std::string& name); 00327 00328 protected: 00329 00330 //! Setup the name of the task 00331 void setTaskName(const std::string& name); 00332 00333 /** 00334 * Define default variables. Set also default task order to ORDER_NORMAL 00335 * and define the current state of the task to TASK_STOPPED. 00336 **/ 00337 ITask(); 00338 00339 /** 00340 * You are allowed to specify the task name as a parameter for the task. 00341 * Because no one can create directly the ITask instance, so only derived 00342 * classes can setup their names. 00343 **/ 00344 ITask(const std::string& name); 00345 00346 private: 00347 00348 //! Kernel should have the full access to the task data. So kernel is our friend 00349 friend class Kernel; 00350 00351 //! Engine should also get full access to running tasks, to allow setting up system tasks 00352 friend class Engine; 00353 00354 bool _taskCanKill; // we can kill this task in next system cycle 00355 TaskState _taskState; 00356 TaskId _taskID; // from kernel given task ID (unique) 00357 TaskOrder _taskOrder; // order number of our tasks 00358 TaskType _taskType; // type of the task 00359 TaskProperty _taskProperty; // task property 00360 //uint32 _updateCounter; // how often was this task updated 00361 00362 bool _orderChanged; 00363 std::string _taskName; 00364 00365 //! Used by the kernel 00366 int32 _taskGraphColor; 00367 00368 //! This list does store all tasks on which one this depends 00369 std::list< SharedPtr<ITask> > _taskDependencies; 00370 00371 //! Set the task type. This method is private, so only friends can set the type 00372 void setTaskType(TaskType type); 00373 00374 //! Only friends are allowed to set the id of the task 00375 void setTaskID(TaskId id); 00376 00377 //! Friends are also allowed to set the task state 00378 void setTaskState(TaskState state); 00379 00380 //! Set new order number for this task 00381 void setTaskOrder(TaskOrder order); 00382 00383 //! Set property of the task 00384 void setTaskProperty(TaskProperty property); 00385 00386 void init(); 00387 void _noticeSuspend(); 00388 void _noticeResume(); 00389 void _noticeUpdate(); 00390 void _noticeStop(); 00391 00392 }; 00393 00394 //! Empty task does not affect anything. It can helps to group tasks by making htem depends on this task 00395 /** 00396 * This class representing a simple task which does not do anything. 00397 * This is an empty task wich can be used as placeholder or root task containing 00398 * only children (task on which one it depends).<br> 00399 * This technique can help us to combine tasks in groups by using of this 00400 * empty tasks. 00401 * \ingroup kernel 00402 **/ 00403 class _NRExport EmptyTask : public ITask{ 00404 public: 00405 EmptyTask() : ITask() {} 00406 virtual ~EmptyTask() {} 00407 00408 //! Do nothing just return OK 00409 virtual Result updateTask() { return OK; } 00410 00411 }; 00412 00413 }; // end namespace 00414 #endif //_NR...