ITask.h

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...

Generated on Wed Sep 12 23:19:42 2007 for nrEngine by  doxygen 1.5.1