Max Kellermann <[EMAIL PROTECTED]> wrote:
> On 2008/09/27 07:24, Eric Wong <[EMAIL PROTECTED]> wrote:
> > Neither notify nor condition are optimal for the one thread per audio
> > output stuff.
> > 
> > Instead, use one global condition variable and multiple mutexes (one mutex
> > per output) and pthread_cond_broadcast().
> [...]
> > playAudio()/flushAudioBuffer() will then call
> > pthread_cond_broadcast(&global_ao_cond).   I would do this after
> > verifying that at least of the ao->mutex-es is unlocked; indicating that
> > there's at least one playable output.
> 
> I considered using pthread_cond_broadcast(), but postponed that
> decision for now, since I am quite comfortable with having full
> control over which audio outputs I am going to wake up.  Looping with
> pthread_cond_signal() isn't worse than looping to check if mutexes are
> locked...

But you wouldn't know if a notify_signal() even succeeded in delivering
a signal or not (I haven't fully read the rest of the output rewrite).
And the eventual response could be a wakeup from much earlier
notify_signal() call, couldn't it?

Perhaps notify_signal() should return EBUSY if pending is already set?

> > Currently, it seems that playAudio() still waits until all the outputs
> > have played.  This seems wrong to me.  If some outputs are blocking
> > others from completing, it should be skipped over (and drop audio) to
> > not affect other outputs.
> 
> That's right.  That's were my patches are not feature complete yet.
> I'm implementing one thing at a time, starting with sending all
> outputs to a separate thread - further refinements will come, or
> somebody else will implement them if I don't have the time soon
> enough.  At the current patch level, the infrastructure for threaded
> audio outputs is there, but no "real" advantages yet.

Adding a user-configurable async flag to each audio_output (defaulting
to off for backwards compatibility) for playback could be useful.  This
way users can choose which outputs they value over others.

enable/disable/close/open should always be async, though; just
playback should be (optionally) synchronous.

If a user only has one output enabled, then it should of course
be synchronous.

> > A note about notify:
> > 
> >   The pending flag in notify has always bugged me as it's totally
> >   unnecessary.  pthread_cond_wait() atomically unlocks the mutex when it
> >   is called and starts waiting.  This is for allowing a signalling
> >   thread to check to see if the waiter is signal-able.   So attempting
> >   to lock the mutex that is passed to pthread_cond_wait is already
> >   enough to know if there's a thread waiting on that condition variable.
> 
> That is correct if you rely on synchronous notification.  I am not
> sure yet if that is a good thing for MPD.  I think my current approach
> (asynchronous notifications) is better: send a signal to a thread,
> which it will work on whenever it's ready to do so, and meanwhile the
> calling thread can move on, do something else, or notify other
> threads.  You cannot do that without a "pending" flag, since the
> pthread_cond_t itself is stateless, it forgets the signal if nobody
> was listening.

synchronous notification is much easier in some cases.  It is much
nicer for some "fast" things such as dropping the output buffer when
skipping tracks; but I agree that async is much better in many cases,
too...

condition.c doesn't actually enforce synchronous or asynchronous
operation: The 'pending' flag could just be the enum foo_action tied to
a particular structure (condition.c being much more freeform does allow
more places to screw it up, however :)

> Just think about MPD sending data to 10 outputs, and one of them is
> blocking, having its notify.mutex still locked, and the player thread
> waits for that mutex: MPD will stall, it cannot send audio data to the
> other 9 outputs - deadlock.  With my asynchronous approach, MPD can
> send the signal to all 10 threads, and see which one responds quickly,
> disabling those who don't.

I would just use pthread_mutex_trylock() and then broadcast if I get
locks from the required set of outputs...

I don't think automatically disabling outputs on timeouts/errors is good
policy, though.  We should make a best effort to send to an output
unless a user disables it manually; including retrying the
open/initialize (unless it's the only output enabled by the user).

> You were right that my notify.c library contained a race condition,
> which I was able to trigger during debugging.  I am now (since patch
> 58554e14) using notify's mutex solely to protect the "pending" flag.
> Please review if you find another race in there..

No races, but one bug and the aforementioned issue:

A definite bug: pthread_cond_wait is subject to spurious wakeups so you
need to restart pthread_cond_wait if pending is not set.
assert(notify->pending) is wrong after pthread_cond_wait.

Again, there should be a way of preventing notify_signal()
from clobbering/merging its own signals; so EBUSY or just
assert(!pending)...

-- 
Eric Wong

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Musicpd-dev-team mailing list
Musicpd-dev-team@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team

Reply via email to