I am considering using the following game engine design pattern, but I am unsure if it's a good idea or not:
- Each major task (drawing, physics, logic, networking, disk I/O) will have its own isolated thread or subprocess
- Each of these threads can be put to sleep if it is running too quickly, so as to not consume an excess of CPU resources
- Threads can only talk to each other through some well-defined interface to avoid messy, buggy, thread-unsafe conditions from occurring
- This approach enables parallelism and scalability, but causes a thread safety problem: what if one thread is accessing data that another thread is currently processing?
- We could introduce fine-grained locking, but it still can't be guaranteed that the collective states of all of a thread's data objects are coherent.
- To solve this, collections of objects are maintained inside state objects, and state objects are double-buffered. The intent of this is to ensure that other threads only see a valid, coherent set of data. If one thread happens to be processing data that another thread wants to read, then the other thread reads from a buffered state, which is guaranteed not to be written to.
- However, buffering these states introduces a delay where one thread sees old data from the last cycle of the other thread.
- Another problem might occur when a reading thread has a slower "tick rate" than a writing thread: the writing thread may have flipped the state buffer multiple times, while the reading thread is still processing one "tick" using the same state. However this can be solved using OO references.
- Pipelining should be achievable using this approach, because the draw thread could be drawing information that entered the pipeline 30 milliseconds ago, while more information is being processed
- Ultimately, what I'd like to see is completely decoupled processing for all of the tasks in a game, implemented in a way that helps leverage multi-core environments, and maybe leaves some future expansion room for scaling the server side of things amongst multiple machines. I am aware that this is probably overkill for most purposes :-)
What are the criticisms or pitfalls of this approach? Is this the "right" way to do things? Am I thinking about this backwards?