diff options
author | David Henningsson <david.henningsson@canonical.com> | 2012-05-18 22:29:41 +0200 |
---|---|---|
committer | Arun Raghavan <arun.raghavan@collabora.co.uk> | 2012-07-03 17:18:24 +0530 |
commit | ac1a466f219389e543e2edd75dd5ea6cb86ef3f7 (patch) | |
tree | 2622fbbddf0ccddbd6ddaf668e4b051e6458abfa | |
parent | 1c055d70b09fd63156cdc8a49989977031e0f4af (diff) |
once: Fix race causing pa_once to sometimes run twice
There was a race in the existing code that could cause the pa_once code
to be run twice, see:
http://lists.freedesktop.org/archives/pulseaudio-discuss/2012-April/013354.html
Therefore the existing implementation was rewritten to instead look like
the reference implementation here:
http://www.hpl.hp.com/research/linux/atomic_ops/example.php4
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
-rw-r--r-- | src/pulsecore/once.c | 43 | ||||
-rw-r--r-- | src/pulsecore/once.h | 8 |
2 files changed, 17 insertions, 34 deletions
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c index 4e509e0c..30b35a6c 100644 --- a/src/pulsecore/once.c +++ b/src/pulsecore/once.c @@ -24,46 +24,33 @@ #endif #include <pulsecore/macro.h> -#include <pulsecore/mutex.h> #include "once.h" +/* See http://www.hpl.hp.com/research/linux/atomic_ops/example.php4 for the + * reference algorithm used here. */ + pa_bool_t pa_once_begin(pa_once *control) { + pa_mutex *m; + pa_assert(control); if (pa_atomic_load(&control->done)) return FALSE; - pa_atomic_inc(&control->ref); - /* Caveat: We have to make sure that the once func has completed * before returning, even if the once func is not actually * executed by us. Hence the awkward locking. */ - for (;;) { - pa_mutex *m; - - if ((m = pa_atomic_ptr_load(&control->mutex))) { - - /* The mutex is stored in locked state, hence let's just - * wait until it is unlocked */ - pa_mutex_lock(m); - - pa_assert(pa_atomic_load(&control->done)); - - pa_once_end(control); - return FALSE; - } - - pa_assert_se(m = pa_mutex_new(FALSE, FALSE)); - pa_mutex_lock(m); - - if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m)) - return TRUE; + m = pa_static_mutex_get(&control->mutex, FALSE, FALSE); + pa_mutex_lock(m); + if (pa_atomic_load(&control->done)) { pa_mutex_unlock(m); - pa_mutex_free(m); + return FALSE; } + + return TRUE; } void pa_once_end(pa_once *control) { @@ -71,15 +58,11 @@ void pa_once_end(pa_once *control) { pa_assert(control); + pa_assert(!pa_atomic_load(&control->done)); pa_atomic_store(&control->done, 1); - pa_assert_se(m = pa_atomic_ptr_load(&control->mutex)); + m = pa_static_mutex_get(&control->mutex, FALSE, FALSE); pa_mutex_unlock(m); - - if (pa_atomic_dec(&control->ref) <= 1) { - pa_assert_se(pa_atomic_ptr_cmpxchg(&control->mutex, m, NULL)); - pa_mutex_free(m); - } } /* Not reentrant -- how could it be? */ diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h index edc81881..a478d1ff 100644 --- a/src/pulsecore/once.h +++ b/src/pulsecore/once.h @@ -23,16 +23,16 @@ ***/ #include <pulsecore/atomic.h> +#include <pulsecore/mutex.h> typedef struct pa_once { - pa_atomic_ptr_t mutex; - pa_atomic_t ref, done; + pa_static_mutex mutex; + pa_atomic_t done; } pa_once; #define PA_ONCE_INIT \ { \ - .mutex = PA_ATOMIC_PTR_INIT(NULL), \ - .ref = PA_ATOMIC_INIT(0), \ + .mutex = PA_STATIC_MUTEX_INIT, \ .done = PA_ATOMIC_INIT(0) \ } |