source: nrEngine/include/Kernel.h @ 1

Revision 1, 14.6 KB checked in by art, 13 years ago (diff)
Line 
1/***************************************************************************
2 *                                                                         *
3 *   (c) Art Tevs, MPI Informatik Saarbruecken                             *
4 *       mailto: <tevs@mpi-sb.mpg.de>                                      *
5 *                                                                         *
6 *   This program is free software; you can redistribute it and/or modify  *
7 *   it under the terms of the GNU General Public License as published by  *
8 *   the Free Software Foundation; either version 2 of the License, or     *
9 *   (at your option) any later version.                                   *
10 *                                                                         *
11 ***************************************************************************/
12
13/*!
14 * \defgroup kernel Kernel (Heart) of the nrEngine
15 *
16 * As you know each operating system has got a kernel. This kernel run tasks
17 * (user programms) to allow to do a lot of things. So our engine is nothing
18 * else as a little operating system. Here we implement kernel of the engine.
19 * This kernel runs tasks in there priority order, so you can run for example
20 * display task as last task and input as first. Kernel has also support to freeze
21 * tasks (suspend) and resume them from sleeping. To run application written
22 * for nrEngine it is better if you use this system. Actually kernel is nothing else
23 * than the old style game loop. Kernel runs tasks in a one big loop until there
24 * is no more tasks.
25 *
26 */
27
28#ifndef _NR_C_KERNEL_H_
29#define _NR_C_KERNEL_H_
30
31
32//----------------------------------------------------------------------------------
33// Includes
34//----------------------------------------------------------------------------------
35#include "Prerequisities.h"
36#include "ITask.h"
37
38
39namespace nrEngine {
40
41        //! Heart of our engine system, where tasks are running
42        /**
43        * \par
44        * Kernel - is the heart of our engine. Kernel is something like the oldschool
45        * Game Loop. You can add, remove, suspend or resume tasks from kernel's pipeline
46        * Tasks would be executed in their order - smaller order number comes before
47        * greater order number.
48        *
49        * \par
50        * What should tasks supports:
51        *       - They should implement the \a ITask Interface
52        *       - They should be loaded and be ready to start before adding to kernels pipeline
53        *       - In each game cycle \a ITask::updateTask() Method will be executed
54        *       - By suspending or resuming the Tasks \a ITask::onSuspendTask()/onResumeTask() Method will be executed
55        *       - Before task will be removed \a ITask::stopTask() Method will executed
56        *       - Each taskID is unique and is greater than 0
57        *       - There is no TaskId wich will be given twice
58        *
59        * \par
60        *       Tasks can depends on each other, so one task should be updated before another one.
61        *       This can be defined through task dependencies functions. Sometimes you also
62        *       need to execute a task more than once per one kernel tick. This also can be
63        *       specified by executing of appropriate functions.<br>
64        *       Sleeping tasks will not be sorted in into the kernel's pipeline if any task
65        *       will be added, that depends on such one. If the task will be resumed than the sorting
66        *       will be done. So be warned that it is possible to retrieve a circularity
67        *       error code during the waking up.
68        *
69        * \par
70        *       In our Engine their exisists system and user tasks. System tasks are not accessable through the
71        *       user, because they are essential for engine's work. So to allow the engine to use
72        *       same functions for task access, we define a boolean lock variable. If this variable
73        *       is true, so all accesses to the kernel are done through the engine. The kernel
74        *       can give then all tasks engine ask about. Locking of the kernel can only be done
75        *       through the engine, so the user is not able to do the locking.<br>
76        *       Better way to implement this is to have other programming language that allows
77        *       "sandboxes" or similar techniques to distinguish between engine's own and user calls.
78        *
79        * \note: Try to optimise this kernel by implementing O(1) Scheduler or something
80        *                else if kernel loop/pipeline need a lot of time for updating.
81        *                Better if you implement supporting of multithreaded Kernel, so we can
82        *                support Multiple-CPU-System to increase game speed
83        * \ingroup kernel
84        **/
85        class _NRExport Kernel{
86        public:
87
88                /**
89                 * Define if a kernel should send special task events on the
90                 * engines default channel. Task events are used to inform the application
91                 * and the engine, that state of certain tasks is changed.
92                 *
93                 * Actually the engine does not need this events, cause engine's modules
94                 * does not depend on each other, so they do not need this information.
95                 * However your application could require this messages to react to
96                 * certain conditions, i.e. clock task is sleeping
97                 *
98                 * @param bSend if true the events would be sent, if false so kernel is silent
99                 **/
100                void sendEvents(bool bSend = true) { bSendEvents = bSend; }
101
102                /**
103                 * If true, so the kernel is currently inform teh application about
104                 * task state changes by sending messages over the engine's default communication
105                 * channel.
106                 **/
107                bool isSendingEvents() const { return bSendEvents; }
108
109                /**
110                 * Executes the kernel (old school main loop :-)
111                 * Before main loop is started all tasks will be intialized by calling task
112                 * function \a ITask::taskStart() . If taskStart returns error, so this task will be
113                 * excluded from execution task list.
114                 * \return OK or error code:
115                 *              - ERROR_UNKNOWN for unknown error
116                 *
117                 * \par
118                 *              If you get an error code back, so you can probably found more useful information
119                 *              in a log file filled out by the engine's kernel.
120                 **/
121                Result Execute();
122
123                /**
124                 * Executes all task according to their order. After that
125                 * return back. If you call this function for the first time,
126                 * so the task will start before updating.
127                 * \see ITask manual to understand how tasks works
128                 **/
129                Result OneTick();
130
131
132                /**
133                 * Add the given task into our kernel pipeline (main loop)
134                 * Before task will be added it's \a ITask::taskInit()) function will be executed
135                 * The returned task id number can be used to access to the task through kernel.
136                 *
137                 *
138                 * \param task - is a smart pointer to an object implementing ITask-Interface
139                 * \param order Order number for this task (default is ORDER_NORMAL)
140                 * \param property Specifiy the special property of the task
141                 * \return task id of added task or 0 if task could not be added
142                 *
143                 **/
144                TaskId AddTask (SharedPtr<ITask> task, TaskOrder order = ORDER_NORMAL, TaskProperty property = TASK_NONE);
145
146                /**
147                 * Remove the task from our game loop (pipeline).
148                 *
149                 * \param id - id of a task to be removed
150                 * \return OK or error code:
151                 *              - ERROR_UNKNOWN for unknown error
152                 *              - KERNEL_NO_TASK_FOUND if no such task was found
153                 *              - KERNEL_NO_RIGHTS if you try to remove a system task
154                 **/
155                Result RemoveTask       (TaskId id);
156
157
158                /**
159                * This method will start a task with given id. If task could be started,
160                * so it will be updated in next cycles. Otherwise task state will be set
161                * to stopped. If the task is already running it will be not started again.
162                * If task is paused, so it will be resumed through \a ResumeTask() method.
163                *
164                * @param id Unique id of the task to be started
165                * @return either OK or:
166                *               - UNKNOWN_ERROR
167                *               - KERNEL_NO_TASK_FOUND
168                *               - KERNEL_NO_RIGHTS if you try to remove a system task
169                **/
170                //Result StartTask      (TaskId id);
171
172
173                /**
174                 * Suspend task to prevent it from update. Task will get to sleep.
175                 *
176                 * \param id - id of a task to be suspended
177                 * \return OK or error code:
178                 *              - ERROR_UNKNOWN for unknown error
179                 *              - KERNEL_NO_TASK_FOUND if such a task was not found
180                 *              - KERNEL_NO_RIGHTS if you try to remove a system task
181                 **/
182                Result SuspendTask (TaskId id);
183
184                /**
185                 * Resume task from sleeping.
186                 *
187                 * \param id - id of a task to waik
188                 * \return OK or error code:
189                 *              - ERROR_UNKNOWN for unknown error
190                 *              - KERNEL_NO_TASK_FOUND if such a task was not found
191                 *              - KERNEL_NO_RIGHTS if you try to remove a system task
192                 **/
193                Result ResumeTask  (TaskId id);
194
195
196                /**
197                 * Remove and kill all tasks from the kernel's task list. If no tasks are in the
198                 * list, so the kernel will stop executing.
199                 *
200                 * \return OK or error code:
201                 *              - ERROR_UNKNOWN for unknown error
202                 **/
203                Result StopExecution();
204
205
206                /**
207                 * Changes the order number of task with given id
208                 * \param id  id of a task
209                 * \param order  New order number for the task
210                 * \return OK or error code:
211                 *              - ERROR_UNKNOWN for unknown error
212                 *              - KERNEL_NO_TASK_FOUND if such a task was not found
213                 *              - KERNEL_NO_RIGHTS if you try to remove a system task
214                 **/
215                Result ChangeTaskOrder(TaskId id, TaskOrder order = ORDER_NORMAL);
216
217
218                /**
219                 * Returns smart pointer to a task with the given id.
220                 *
221                 * \param id ID of the task
222                 * \return smart pointer to the task or NULL if no such task found or task is system task
223                 **/
224                SharedPtr<ITask> getTaskByID(TaskId id);
225
226                /**
227                 * Get the task wich has the same name as the given one.
228                 *
229                 * \param name Unique name of the task
230                 * \return smart poitner to the task or to the NULL if no such task found or task is system task
231                **/
232                SharedPtr<ITask> getTaskByName(const ::std::string& name);
233
234        protected:
235
236                //! Here kernel does store all currently running tasks
237                ::std::list< SharedPtr<ITask> > taskList;
238
239                //! Here kernel store all tasks that are sleeping now.
240                ::std::list< SharedPtr<ITask> > pausedTaskList;
241
242                //! Get information about lock state of the kernel
243                bool areSystemTasksAccessable() { return _bSystemTasksAccessable; }
244
245        private:
246               
247                /**
248                 * Define the kernel's list names containing tasks.
249                 **/
250                enum {
251                        //! Task that are currently running are stored here
252                        TL_RUNNING = 1 << 1,
253
254                        //! Tasks that are sleeping are stored in this list
255                        TL_SLEEPING = 1 << 2
256
257                };
258
259                //! Engine is allowed to create instances of that object
260                friend class Engine;
261
262                /**
263                 * Clear all lists and initialize internal variables.
264                 **/
265                Kernel();
266
267                /**
268                 * Release all used memory and remove all tasks from the kernel. Each task is
269                 * added to the kernel's taks list through shared pointers, so by removing
270                 * tasks of the kernel's list the pointer reference number will be decreased.
271                 *
272                 * The destructor is defined as virtual, so you can create your own kernel class
273                 * if you have to.
274                 */
275                ~Kernel();
276
277
278                typedef ::std::list< SharedPtr<ITask> >::iterator PipelineIterator;
279
280                /**
281                 * Find the task by task ID.
282                 *
283                 * \param id is the id of task
284                 * \param it Iterator pointing to the element
285                 * \param useList declare in which lists should be searched for the task
286                 * \return true if such one was found
287                 * \note This Function runs in O(N) so optimize this if you know how
288                **/
289                bool _getTaskByID(TaskId id, PipelineIterator& it, int32 useList = TL_RUNNING);
290
291                /**
292                 * Find the task by task's name.
293                 *
294                 * \param name is the name of task
295                 * \param it Iterator pointing to the element
296                 * \param useList declare in which lists should be searched for the task
297                 * \return true if such one was found
298                 * \note This Function runs in O(N) so optimize this if you know how
299                 **/
300                bool _getTaskByName(const ::std::string& name, PipelineIterator& it, int32 useList = TL_RUNNING);
301
302                /**
303                 * Get the list containing all smart pointers of tasks that are in kernel's
304                 * execution list.
305                 **/
306                const ::std::list< SharedPtr<ITask> >& getTaskList(){return taskList;}
307
308
309                /**
310                 * Get list of all paused tasks that are running in the kernel. Paused tasks are tasks
311                 * which were suspended through \a SuspendTask() method.
312                 **/
313                const ::std::list< SharedPtr<ITask> >& getPausedTaskList(){return pausedTaskList;}
314
315                /**
316                * Solving of dependencies is needed to bring up the task
317                * list in the right order. Each task that depends on another one, should be
318                * later in the list, as the one on which depends.
319                *
320                * \param retTasks In this vector there will be task ids stored according to the
321                *                               error code given back (for OK no task ids, circularity - all task ids
322                *                               that builds the circle, task missing - task ids which are missing, ...)
323                *
324                * \return either OK or:
325                *                       - KERNEL_CIRCUIT_DEPENDENCIES
326                *                       - KERNEL_TASK_MISSING
327                *                       - BAD_PARAMETERS if the given pointer is null or the vector does contain data
328                **/
329                //Result _solveDependencies(::std::vector<taskID>* retTasks);
330
331                /**
332                * This method will start a one cycle in the kernel loop. So it will prepare
333                * the variables needed by the next task function, which will return next task
334                * in the kernel's task pipeline
335                * \return error code if any occurs
336                **/
337                Result _loopStartCycle();
338
339                /**
340                * This method will give us the next task in the kernel's pipeline which should be updated.
341                * This function also solve the dependencies of the tasks by the depth first search
342                * algorithm. So you will get an iterator from the kernel'S task list containing the
343                * task to be updated as next. If iterator is equal to the end, so there is no more
344                * tasks in the kernel's list
345                * \return error code if any occurs (i.e. circularity or task missing)
346                **/
347                Result _loopGetNextTask(PipelineIterator it, PipelineIterator& result, int depth = 0);
348
349                /**
350                * This method should be called after the cycle is ended. This will bring the
351                * variables in the right state, so works can be done again later.
352                **/
353                Result _loopEndCycle();
354
355                /**
356                 * Prepare the kernel to run tasks. This will create a root task and add
357                 * all necessary dependencies.
358                 **/
359                void prepareRootTask();
360
361
362                /**
363                 * This function will start each task and set it's state to running,
364                 * if starting was successful otherwise the task will not run.
365                 **/
366                void startTasks();
367
368                //! Lock the kernel, so kernel gices us access to system tasks
369                void lockSystemTasks();
370
371                //! Unlock the kernel, so it close access to the system tasks
372                void unlockSystemTasks();
373
374                //! Try to start a given task
375                Result _taskStart(SharedPtr<ITask>& task);
376
377                //! Stop the given task
378                Result _taskStop(SharedPtr<ITask>& task);
379
380                //! Last given task id. Is used to generate new ids for newly added tasks
381                TaskId lastTaskID;
382
383                //! If true, so we know that all tasks are started and a list of the tasks is sorted. Only for internal use.
384                bool bTaskStarted;
385
386                //! Check whenever we have already added a root task
387                bool bInitializedRoot;
388
389                //! Root task
390                SharedPtr<ITask>        mRootTask;
391               
392                //! If it is true, so the kernel is locked for engine access. All next operations until unlock, can access to system tasks
393                bool _bSystemTasksAccessable;
394
395                //! Used by the _loop* Methods
396                static PipelineIterator _loopIterator;
397
398                //! Should kernel send events if states of tasks are changed
399                bool bSendEvents;
400        };
401
402}; // end Namespace
403
404#endif  //_NR...
Note: See TracBrowser for help on using the repository browser.