Avoiding the atomic read may help if a function using ovsthread_once_start() is ever called in a loop, as the new 'maybe_not_done' can be kept in a register. The atomic read will still be done as long as 'maybe_not_done' is true. Since 'maybe_not_done' is not an atomic variable, whis may happen indefinitely, but if a loop starts with 'maybe_not_done' as false, no atomic read operations are needed.
Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com> --- lib/ovs-thread.c | 11 ++++++++++- lib/ovs-thread.h | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/ovs-thread.c b/lib/ovs-thread.c index 8fd5c32..9e260b7 100644 --- a/lib/ovs-thread.c +++ b/lib/ovs-thread.c @@ -371,7 +371,16 @@ ovsthread_once_start__(struct ovsthread_once *once) void ovsthread_once_done(struct ovsthread_once *once) { - atomic_store(&once->done, true); + /* We need release semantics here, so that neither the atomic nor + * the non-atomic store may be moved ahead of any of the preceding + * initialization operations. An atomic release-store could allow + * the following non-atomic store to be moved ahead of the preceding + * initialization operations. A release atomic_thread_fence + * provides that prior memory accesses will not be reordered to + * take place after either of the stores. */ + atomic_thread_fence(memory_order_release); + once->maybe_not_done = false; + atomic_store_relaxed(&once->done, true); ovs_mutex_unlock(&once->mutex); } diff --git a/lib/ovs-thread.h b/lib/ovs-thread.h index 962e867..1d072b1 100644 --- a/lib/ovs-thread.h +++ b/lib/ovs-thread.h @@ -533,12 +533,14 @@ void *ovsthread_getspecific(ovsthread_key_t); */ struct ovsthread_once { + bool maybe_not_done; /* Non-atomic, false positives possible. */ atomic_bool done; struct ovs_mutex mutex; }; #define OVSTHREAD_ONCE_INITIALIZER \ { \ + true, \ ATOMIC_VAR_INIT(false), \ OVS_MUTEX_INITIALIZER, \ } @@ -570,7 +572,10 @@ 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) + /* Avoiding the atomic_read may help if a function using this is ever + * called in a loop, as 'maybe_not_done' can be kept in a register. */ + return OVS_UNLIKELY(once->maybe_not_done + && !ovsthread_once_is_done__(once) && !ovsthread_once_start__(once)); } -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev