Posted 07 October 2012 - 01:03 PM
In my current project I felt a definite need to invest some of my critical thinking and programming skills into this issue, and the end result was basically a kernel that divided everything up into a grid of states/systems. The X/Y dimensions of this grid are the Systems and States of the engine/kernel.. Thus, systems like Render/Network/Physics, etc. with various 'states' that can be invoked across threads, and done so repeatedly according to a desired time interval.. Eg: Render_Setup, Render_Cycle, Render_Shutdown...
Each system I allow a min-interval and a max-interval. All systems attempt to reach a 'balance' so that their actual execution intervals are within their defined ranges. If one system is running plenty quick for a particlar state (eg: STATE_SETUP, STATE_STANDBY, STATE_INGAME, STATE_SHUTDOWN) and other systems aren't, it will simply increase the wait period until the next execution..
Along with all this is a neat ability to create logic-chains, where one state - depending on the result of each system executing for that state - determines what state to progress to next. So if the STATE_SETUP fails on one of the systems, it will go to STATE_SHUTDOWN.. If the setup for all systems succeeds then it can, otherwise, progress the state to STATE_STANDBY to display a menu, or whatever.
I just create a new thread for each system, unless the system specifies that it should run in the main thread (some things like hardware rendering don't much appreciate occurring outside the main thread) and each thread runs its own main-loop that determines if/when its corresponding system should execute. Integrating multithreading into the system is probably the trickiest part of the whole deal, but the key is to not over-think it..
As far as overhead is concerned, my primary goal when devising this setup was having the ability to control the use of resources by each system so as to provide the end-user an optimal experience as far as responsiveness is concerned, by enabling the core, or 'main loop', to skip the refresh of certain systems which can afford it..
If one system magically takes a bunch of time and causes other systems to fall behind, forcing them to execute well beyond their max-interval, what I do is take the total amount of elapsed time since the last refresh (for each system, individually), divide it up evenly, and execute the system however many times in an attempt to emulate proper execution intervals so as to allow the system to 'catch up'.
I'm not sure how well I explained this, but it works great and I'm happy with it.