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 * \defgroup kernel Kernel (Heart) of the nrEngine 00015 * 00016 * As you know each operating system has got a kernel. This kernel run tasks 00017 * (user programms) to allow to do a lot of things. So our engine is nothing 00018 * else as a little operating system. Here we implement kernel of the engine. 00019 * This kernel runs tasks in there priority order, so you can run for example 00020 * display task as last task and input as first. Kernel has also support to freeze 00021 * tasks (suspend) and resume them from sleeping. To run application written 00022 * for nrEngine it is better if you use this system. Actually kernel is nothing else 00023 * than the old style game loop. Kernel runs tasks in a one big loop until there 00024 * is no more tasks. 00025 * 00026 */ 00027 00028 #ifndef _NR_C_KERNEL_H_ 00029 #define _NR_C_KERNEL_H_ 00030 00031 00032 //---------------------------------------------------------------------------------- 00033 // Includes 00034 //---------------------------------------------------------------------------------- 00035 #include "Prerequisities.h" 00036 #include "ITask.h" 00037 00038 00039 namespace nrEngine { 00040 00041 //! Heart of our engine system, where tasks are running 00042 /** 00043 * \par 00044 * Kernel - is the heart of our engine. Kernel is something like the oldschool 00045 * Game Loop. You can add, remove, suspend or resume tasks from kernel's pipeline 00046 * Tasks would be executed in their order - smaller order number comes before 00047 * greater order number. 00048 * 00049 * \par 00050 * What should tasks supports: 00051 * - They should implement the \a ITask Interface 00052 * - They should be loaded and be ready to start before adding to kernels pipeline 00053 * - In each game cycle \a ITask::updateTask() Method will be executed 00054 * - By suspending or resuming the Tasks \a ITask::onSuspendTask()/onResumeTask() Method will be executed 00055 * - Before task will be removed \a ITask::stopTask() Method will executed 00056 * - Each taskID is unique and is greater than 0 00057 * - There is no TaskId wich will be given twice 00058 * 00059 * \par 00060 * Tasks can depends on each other, so one task should be updated before another one. 00061 * This can be defined through task dependencies functions. Sometimes you also 00062 * need to execute a task more than once per one kernel tick. This also can be 00063 * specified by executing of appropriate functions.<br> 00064 * Sleeping tasks will not be sorted in into the kernel's pipeline if any task 00065 * will be added, that depends on such one. If the task will be resumed than the sorting 00066 * will be done. So be warned that it is possible to retrieve a circularity 00067 * error code during the waking up. 00068 * 00069 * \par 00070 * In our Engine their exisists system and user tasks. System tasks are not accessable through the 00071 * user, because they are essential for engine's work. So to allow the engine to use 00072 * same functions for task access, we define a boolean lock variable. If this variable 00073 * is true, so all accesses to the kernel are done through the engine. The kernel 00074 * can give then all tasks engine ask about. Locking of the kernel can only be done 00075 * through the engine, so the user is not able to do the locking.<br> 00076 * Better way to implement this is to have other programming language that allows 00077 * "sandboxes" or similar techniques to distinguish between engine's own and user calls. 00078 * 00079 * \note: Try to optimise this kernel by implementing O(1) Scheduler or something 00080 * else if kernel loop/pipeline need a lot of time for updating. 00081 * Better if you implement supporting of multithreaded Kernel, so we can 00082 * support Multiple-CPU-System to increase game speed 00083 * \ingroup kernel 00084 **/ 00085 class _NRExport Kernel{ 00086 public: 00087 00088 /** 00089 * Define if a kernel should send special task events on the 00090 * engines default channel. Task events are used to inform the application 00091 * and the engine, that state of certain tasks is changed. 00092 * 00093 * Actually the engine does not need this events, cause engine's modules 00094 * does not depend on each other, so they do not need this information. 00095 * However your application could require this messages to react to 00096 * certain conditions, i.e. clock task is sleeping 00097 * 00098 * @param bSend if true the events would be sent, if false so kernel is silent 00099 **/ 00100 void sendEvents(bool bSend = true) { bSendEvents = bSend; } 00101 00102 /** 00103 * If true, so the kernel is currently inform teh application about 00104 * task state changes by sending messages over the engine's default communication 00105 * channel. 00106 **/ 00107 bool isSendingEvents() const { return bSendEvents; } 00108 00109 /** 00110 * Executes the kernel (old school main loop :-) 00111 * Before main loop is started all tasks will be intialized by calling task 00112 * function \a ITask::taskStart() . If taskStart returns error, so this task will be 00113 * excluded from execution task list. 00114 * \return OK or error code: 00115 * - ERROR_UNKNOWN for unknown error 00116 * 00117 * \par 00118 * If you get an error code back, so you can probably found more useful information 00119 * in a log file filled out by the engine's kernel. 00120 **/ 00121 Result Execute(); 00122 00123 /** 00124 * Executes all task according to their order. After that 00125 * return back. If you call this function for the first time, 00126 * so the task will start before updating. 00127 * \see ITask manual to understand how tasks works 00128 **/ 00129 Result OneTick(); 00130 00131 00132 /** 00133 * Add the given task into our kernel pipeline (main loop) 00134 * Before task will be added it's \a ITask::taskInit()) function will be executed 00135 * The returned task id number can be used to access to the task through kernel. 00136 * 00137 * 00138 * \param task - is a smart pointer to an object implementing ITask-Interface 00139 * \param order Order number for this task (default is ORDER_NORMAL) 00140 * \param property Specifiy the special property of the task 00141 * \return task id of added task or 0 if task could not be added 00142 * 00143 **/ 00144 TaskId AddTask (SharedPtr<ITask> task, TaskOrder order = ORDER_NORMAL, TaskProperty property = TASK_NONE); 00145 00146 /** 00147 * Remove the task from our game loop (pipeline). 00148 * 00149 * \param id - id of a task to be removed 00150 * \return OK or error code: 00151 * - ERROR_UNKNOWN for unknown error 00152 * - KERNEL_NO_TASK_FOUND if no such task was found 00153 * - KERNEL_NO_RIGHTS if you try to remove a system task 00154 **/ 00155 Result RemoveTask (TaskId id); 00156 00157 00158 /** 00159 * This method will start a task with given id. If task could be started, 00160 * so it will be updated in next cycles. Otherwise task state will be set 00161 * to stopped. If the task is already running it will be not started again. 00162 * If task is paused, so it will be resumed through \a ResumeTask() method. 00163 * 00164 * @param id Unique id of the task to be started 00165 * @return either OK or: 00166 * - UNKNOWN_ERROR 00167 * - KERNEL_NO_TASK_FOUND 00168 * - KERNEL_NO_RIGHTS if you try to remove a system task 00169 **/ 00170 //Result StartTask (TaskId id); 00171 00172 00173 /** 00174 * Suspend task to prevent it from update. Task will get to sleep. 00175 * 00176 * \param id - id of a task to be suspended 00177 * \return OK or error code: 00178 * - ERROR_UNKNOWN for unknown error 00179 * - KERNEL_NO_TASK_FOUND if such a task was not found 00180 * - KERNEL_NO_RIGHTS if you try to remove a system task 00181 **/ 00182 Result SuspendTask (TaskId id); 00183 00184 /** 00185 * Resume task from sleeping. 00186 * 00187 * \param id - id of a task to waik 00188 * \return OK or error code: 00189 * - ERROR_UNKNOWN for unknown error 00190 * - KERNEL_NO_TASK_FOUND if such a task was not found 00191 * - KERNEL_NO_RIGHTS if you try to remove a system task 00192 **/ 00193 Result ResumeTask (TaskId id); 00194 00195 00196 /** 00197 * Remove and kill all tasks from the kernel's task list. If no tasks are in the 00198 * list, so the kernel will stop executing. 00199 * 00200 * \return OK or error code: 00201 * - ERROR_UNKNOWN for unknown error 00202 **/ 00203 Result StopExecution(); 00204 00205 00206 /** 00207 * Changes the order number of task with given id 00208 * \param id id of a task 00209 * \param order New order number for the task 00210 * \return OK or error code: 00211 * - ERROR_UNKNOWN for unknown error 00212 * - KERNEL_NO_TASK_FOUND if such a task was not found 00213 * - KERNEL_NO_RIGHTS if you try to remove a system task 00214 **/ 00215 Result ChangeTaskOrder(TaskId id, TaskOrder order = ORDER_NORMAL); 00216 00217 00218 /** 00219 * Returns smart pointer to a task with the given id. 00220 * 00221 * \param id ID of the task 00222 * \return smart pointer to the task or NULL if no such task found or task is system task 00223 **/ 00224 SharedPtr<ITask> getTaskByID(TaskId id); 00225 00226 /** 00227 * Get the task wich has the same name as the given one. 00228 * 00229 * \param name Unique name of the task 00230 * \return smart poitner to the task or to the NULL if no such task found or task is system task 00231 **/ 00232 SharedPtr<ITask> getTaskByName(const std::string& name); 00233 00234 protected: 00235 00236 //! Here kernel does store all currently running tasks 00237 std::list< SharedPtr<ITask> > taskList; 00238 00239 //! Here kernel store all tasks that are sleeping now. 00240 std::list< SharedPtr<ITask> > pausedTaskList; 00241 00242 //! Get information about lock state of the kernel 00243 bool areSystemTasksAccessable() { return _bSystemTasksAccessable; } 00244 00245 private: 00246 00247 /** 00248 * Define the kernel's list names containing tasks. 00249 **/ 00250 enum { 00251 //! Task that are currently running are stored here 00252 TL_RUNNING = 1 << 1, 00253 00254 //! Tasks that are sleeping are stored in this list 00255 TL_SLEEPING = 1 << 2 00256 00257 }; 00258 00259 //! Engine is allowed to create instances of that object 00260 friend class Engine; 00261 00262 /** 00263 * Clear all lists and initialize internal variables. 00264 **/ 00265 Kernel(); 00266 00267 /** 00268 * Release all used memory and remove all tasks from the kernel. Each task is 00269 * added to the kernel's taks list through shared pointers, so by removing 00270 * tasks of the kernel's list the pointer reference number will be decreased. 00271 * 00272 * The destructor is defined as virtual, so you can create your own kernel class 00273 * if you have to. 00274 */ 00275 ~Kernel(); 00276 00277 00278 typedef std::list< SharedPtr<ITask> >::iterator PipelineIterator; 00279 00280 /** 00281 * Find the task by task ID. 00282 * 00283 * \param id is the id of task 00284 * \param it Iterator pointing to the element 00285 * \param useList declare in which lists should be searched for the task 00286 * \return true if such one was found 00287 * \note This Function runs in O(N) so optimize this if you know how 00288 **/ 00289 bool _getTaskByID(TaskId id, PipelineIterator& it, int32 useList = TL_RUNNING); 00290 00291 /** 00292 * Find the task by task's name. 00293 * 00294 * \param name is the name of task 00295 * \param it Iterator pointing to the element 00296 * \param useList declare in which lists should be searched for the task 00297 * \return true if such one was found 00298 * \note This Function runs in O(N) so optimize this if you know how 00299 **/ 00300 bool _getTaskByName(const std::string& name, PipelineIterator& it, int32 useList = TL_RUNNING); 00301 00302 /** 00303 * Get the list containing all smart pointers of tasks that are in kernel's 00304 * execution list. 00305 **/ 00306 const std::list< SharedPtr<ITask> >& getTaskList(){return taskList;} 00307 00308 00309 /** 00310 * Get list of all paused tasks that are running in the kernel. Paused tasks are tasks 00311 * which were suspended through \a SuspendTask() method. 00312 **/ 00313 const std::list< SharedPtr<ITask> >& getPausedTaskList(){return pausedTaskList;} 00314 00315 /** 00316 * Solving of dependencies is needed to bring up the task 00317 * list in the right order. Each task that depends on another one, should be 00318 * later in the list, as the one on which depends. 00319 * 00320 * \param retTasks In this vector there will be task ids stored according to the 00321 * error code given back (for OK no task ids, circularity - all task ids 00322 * that builds the circle, task missing - task ids which are missing, ...) 00323 * 00324 * \return either OK or: 00325 * - KERNEL_CIRCUIT_DEPENDENCIES 00326 * - KERNEL_TASK_MISSING 00327 * - BAD_PARAMETERS if the given pointer is null or the vector does contain data 00328 **/ 00329 //Result _solveDependencies(std::vector<taskID>* retTasks); 00330 00331 /** 00332 * This method will start a one cycle in the kernel loop. So it will prepare 00333 * the variables needed by the next task function, which will return next task 00334 * in the kernel's task pipeline 00335 * \return error code if any occurs 00336 **/ 00337 Result _loopStartCycle(); 00338 00339 /** 00340 * This method will give us the next task in the kernel's pipeline which should be updated. 00341 * This function also solve the dependencies of the tasks by the depth first search 00342 * algorithm. So you will get an iterator from the kernel'S task list containing the 00343 * task to be updated as next. If iterator is equal to the end, so there is no more 00344 * tasks in the kernel's list 00345 * \return error code if any occurs (i.e. circularity or task missing) 00346 **/ 00347 Result _loopGetNextTask(PipelineIterator it, PipelineIterator& result, int depth = 0); 00348 00349 /** 00350 * This method should be called after the cycle is ended. This will bring the 00351 * variables in the right state, so works can be done again later. 00352 **/ 00353 Result _loopEndCycle(); 00354 00355 /** 00356 * Prepare the kernel to run tasks. This will create a root task and add 00357 * all necessary dependencies. 00358 **/ 00359 void prepareRootTask(); 00360 00361 00362 /** 00363 * This function will start each task and set it's state to running, 00364 * if starting was successful otherwise the task will not run. 00365 **/ 00366 void startTasks(); 00367 00368 //! Lock the kernel, so kernel gices us access to system tasks 00369 void lockSystemTasks(); 00370 00371 //! Unlock the kernel, so it close access to the system tasks 00372 void unlockSystemTasks(); 00373 00374 //! Try to start a given task 00375 Result _taskStart(SharedPtr<ITask>& task); 00376 00377 //! Stop the given task 00378 Result _taskStop(SharedPtr<ITask>& task); 00379 00380 //! Last given task id. Is used to generate new ids for newly added tasks 00381 TaskId lastTaskID; 00382 00383 //! If true, so we know that all tasks are started and a list of the tasks is sorted. Only for internal use. 00384 bool bTaskStarted; 00385 00386 //! Check whenever we have already added a root task 00387 bool bInitializedRoot; 00388 00389 //! Root task 00390 SharedPtr<ITask> mRootTask; 00391 00392 //! If it is true, so the kernel is locked for engine access. All next operations until unlock, can access to system tasks 00393 bool _bSystemTasksAccessable; 00394 00395 //! Used by the _loop* Methods 00396 static PipelineIterator _loopIterator; 00397 00398 //! Should kernel send events if states of tasks are changed 00399 bool bSendEvents; 00400 }; 00401 00402 }; // end Namespace 00403 00404 #endif //_NR...