Gaffer Docs

Cooperative Multitasking

Perhaps the simplest form of concurrency is achieved with cooperative multitasking. The idea is to have some lightweight processes that get execution time and are responsible for doing their work and returning quickly. In other words, it is not a pre-emptive system, like the heavy weight threads of an operating system. This pattern is very memory efficient, highly performant, and switching between processes is safe since the processes themselves are exiting when it is safe to do so.

The Main Game Loop

The traditional main game loop is an example of this pattern. Suppose you have a Process class with an update method. The structure of the loop is to iterate over these processes and call their update method.

For example, here's some pseudo-code:

while(running) {
    delta = timeSinceLastUpdate();

    for process in processes
    {
        process.update(delta)
    }

    renderFrame();
}

As long as each process "cooperates", this pattern can be effective and simple. As the game developers, we have complete control over these processes, so we can ensure they are well behaved and execute quickly.

Multicore Performance

When users only had single core machines, this pattern worked extrememly well. The primary flaw here is that only one thread is executing, so we at most can utilize one core. A user on a multi-core machine will see one core spiked at 100% and the other cores idle.

Suppose, however, that we run a loop such as this on each core. Obviously, only one will call renderFrame() since there is only one display, but the other parts of the loop are all runnable in multiple threads. Gaffer's APE Pattern employs exactly this strategy.