On Oct 29, 2009, at 9:18 AM, <[email protected]> <[email protected] > wrote:

In audio.c/wodPause:
    /* The order of the following operations is important since we
can't hold
* the mutex while we make an Audio Unit call. Stop the Audio Unit
before
     * setting the PAUSED state.
That's indeed what the code below does.

     * Although we can be in PAUSED
     * state with the Audio Unit still running,
Quite to the contrary, PAUSED state is only set after the unit stopped.

     * that's harmless because the
     * render callback will just produce silence.
I can't comment on that.

The code is (error checking not shown):
    status = AudioOutputUnitStop(wwo->audioUnit);
    OSSpinLockLock(&wwo->lock);
    if (wwo->state == WINE_WS_PLAYING)
        wwo->state = WINE_WS_PAUSED;
    OSSpinLockUnlock(&wwo->lock);


In audio.c/wodRestart:
     * In wodRestart, the order is reversed.
Indeed.

     * This guarantees that we can't get into a situation where the
state is
     * PLAYING but the Audio Unit isn't running.
Quite to the contrary, PLAYING state is reached yet
AudioOutputUnitStart is only called a few instructions afterwards.
Likewise above, it's stopped and PLAYING state is only unset afterwards.

    OSSpinLockLock(&wwo->lock);
    if (wwo->state == WINE_WS_PAUSED)
        wwo->state = WINE_WS_PLAYING;
    OSSpinLockUnlock(&wwo->lock);
    status = AudioOutputUnitStart(wwo->audioUnit);

Is my understanding wrong?
Does the code need be changed to reflect the comments?
Do the comments need correction?

The comments could probably use clarification. The guarantee is meant to hold with respect to two separate threads racing -- one is doing wodPause, the other wodRestart. We want it to be impossible for that scenario to leave the audio unit stopped but the state as PLAYING. It's acceptable for the audio unit to be left running but the state as PAUSED.

The difficulty is that we can't hold the lock on the wave-out instance while we call AudioOutputUnitStop/Start. The Audio Unit calls have their own mutex, so those two calls are guaranteed to be atomic with respect to each other. And we use wwo->lock to make sure that the state updates are atomic with respect to each other. But we can't make the entirety of wodPause atomic with respect to wodRestart, or vice versa, because it would deadlock. So, the solution is to use careful ordering of actions.

The possible sequences are:

1)
Thread A: au stop
Thread A: state -> PAUSED
Thread B: state -> PLAYING
Thread B: au start
Result: PLAYING and au running

2)
Thread A: au stop
Thread B: state -> PLAYING
Thread A: state -> PAUSED
Thread B: au start
Result: PAUSED and au running (acceptable, though not ideal)

3)
Thread A: au stop
Thread B: state -> PLAYING
Thread B: au start
Thread A: state -> PAUSED
Result: PAUSED and au running (ditto)

4)
Thread B: state -> PLAYING
Thread A: au stop
Thread A: state -> PAUSED
Thread B: au start
Result: PAUSED and au running (ditto)

5)
Thread B: state -> PLAYING
Thread A: au stop
Thread B: au start
Thread A: state -> PAUSED
Result: PAUSED and au running (ditto)

6)
Thread B: state -> PLAYING
Thread B: au start
Thread A: au stop
Thread A: state -> PAUSED
Result: PAUSED and au not running


Regards,
Ken



Reply via email to