views:

116

answers:

2

I have 2 threads in my application, a game update thread and render/IO/main thread. My update thread updates the game state, and the render thread renders the scene based on the updated values of the game state models and a few other variables stored inside an object (gameEngine).

The render thread gets executed while the game thread is still updating, which is a problem, so it appeared to me the solution is to use @synchronized like this:

        @synchronized(gameEngine)
        {
            [gameEngine update];

            nextUpdate = now + GAME_UPDATE_INTERVAL;

            gameEngine.lastGameUpdateInterval = now - lastUpdate;
            gameEngine.lastGameUpdateTime = now;
            lastUpdate = now;
        }

But the render thread still accesses the gameEngine object between -update and the last 3 lines of the block. Why is this?

+6  A: 

@synchronized does not block other threads from accessing gameEngine. It just blocks other @synchronized with the same object. That means in

// thread A:
@synchronized(a) {
   do_A(a);
}
...
// thread B:
do_B(a);

do_A and do_B can happen together, but in

// thread A:
@synchronized(a) {
   do_A(a);
}
...
// thread B:
@syncrhonized(a) {
   do_B(a);
}

do_A and do_B will always be executed sequentially.

KennyTM
This is exactly what I was doing and what I needed to do to fix it.
hyn
A: 

You shouldn't be locking the gameEngine -- @KennyTM explained what I believe is the correct answer to your question, however implementing it would lead to only either the game engine or the renderer being able to execute at a given time, making it essentially single threaded. What you should be doing is using an immutable state object, which may be a ivar of gameEngine, but should be nonatomic, where in the render function you grab the state like so

State *state = [[gameEngine state] retain];

and then use state, releasing it when done. When the gameEngine performs an update, it should not mutate the data in its state ivar, which might be being used by the renderer, but may make a copy of it. To set the state, it should

State *oldState = state;
state = newState; //newState should have a retainCount of 1
[oldState release];

because if you release oldState before setting state to newState then the renderer might get oldState, which was just deallocated, leading to Bad Things.

Jared P
-1, you have a race condition that could lead to the renderer having a reference to a deallocated object. `[[gameEngine state] retain]` is not atomic. If the renderer gets interrupted after the return from `gameEngine state` but before sending retain and the game engine is allowed to fully update state including `[oldState release]` before the renderer gets scheduled to do the retain, the object could be deallocated.
JeremyP
he safe way of achieving what you want is to make `-state` an atomic property. This leads to a small section of synchronized code but the single threaded part is effectively limited to the time it takes to send retain and autorelease to the ivar.
JeremyP
even if state was atomic, that just means that -[gameEngine state] would return a valid reference, but could disappear in before retain is called. That's not to say I'm right -- far from it, thank you for calling out my race condition, but because of the way state is set, -[gameEngine state] is guaranteed to always have a retain count of at least one.
Jared P