Skip to content

Commit

Permalink
lib/ovs-thread: Avoid atomic read in ovsthread_once_start().
Browse files Browse the repository at this point in the history
We can use a normal bool and rely on the mutex_lock/unlock and an
atomic_thread_fence for synchronization.

Also flip the return value of ovsthread_once_start__() to match the
one of ovsthread_once_start().

Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
  • Loading branch information
Jarno Rajahalme committed Aug 29, 2014
1 parent 6f00886 commit 9230662
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 18 deletions.
14 changes: 10 additions & 4 deletions lib/ovs-thread.c
Expand Up @@ -367,17 +367,23 @@ bool
ovsthread_once_start__(struct ovsthread_once *once)
{
ovs_mutex_lock(&once->mutex);
if (!ovsthread_once_is_done__(once)) {
return false;
/* Mutex synchronizes memory, so we get the current value of 'done'. */
if (!once->done) {
return true;
}
ovs_mutex_unlock(&once->mutex);
return true;
return false;
}

void
ovsthread_once_done(struct ovsthread_once *once)
{
atomic_store(&once->done, true);
/* We need release semantics here, so that the following store may not
* be moved ahead of any of the preceding initialization operations.
* A release atomic_thread_fence provides that prior memory accesses
* will not be reordered to take place after the following store. */
atomic_thread_fence(memory_order_release);
once->done = true;
ovs_mutex_unlock(&once->mutex);
}

Expand Down
22 changes: 8 additions & 14 deletions lib/ovs-thread.h
Expand Up @@ -533,13 +533,13 @@ void *ovsthread_getspecific(ovsthread_key_t);
*/

struct ovsthread_once {
atomic_bool done;
bool done; /* Non-atomic, false negatives possible. */
struct ovs_mutex mutex;
};

#define OVSTHREAD_ONCE_INITIALIZER \
{ \
ATOMIC_VAR_INIT(false), \
false, \
OVS_MUTEX_INITIALIZER, \
}

Expand All @@ -549,16 +549,7 @@ void ovsthread_once_done(struct ovsthread_once *once)
OVS_RELEASES(once->mutex);

bool ovsthread_once_start__(struct ovsthread_once *once)
OVS_TRY_LOCK(false, once->mutex);

static inline bool
ovsthread_once_is_done__(struct ovsthread_once *once)
{
bool done;

atomic_read_relaxed(&once->done, &done);
return done;
}
OVS_TRY_LOCK(true, once->mutex);

/* Returns true if this is the first call to ovsthread_once_start() for
* 'once'. In this case, the caller should perform whatever initialization
Expand All @@ -570,8 +561,11 @@ ovsthread_once_is_done__(struct ovsthread_once *once)
static inline bool
ovsthread_once_start(struct ovsthread_once *once)
{
return OVS_UNLIKELY(!ovsthread_once_is_done__(once)
&& !ovsthread_once_start__(once));
/* We may be reading 'done' at the same time as the first thread
* is writing on it, or we can be using a stale copy of it. The
* worst that can happen is that we call ovsthread_once_start__()
* once when strictly not necessary. */
return OVS_UNLIKELY(!once->done && ovsthread_once_start__(once));
}

/* Thread ID.
Expand Down

0 comments on commit 9230662

Please sign in to comment.