Acked-by: Ethan Jackson <et...@nicira.com>
On Wed, Jun 19, 2013 at 1:17 PM, Ben Pfaff <b...@nicira.com> wrote: > pthread_once() is portable but it does not allow passing any parameters to > the initialization function, which is often inconvenient, because it means > that the function can only access data declared at file scope. This commit > introduces an alternative with a more convenient interface. > > Signed-off-by: Ben Pfaff <b...@nicira.com> > --- > Makefile.am | 12 ++++---- > lib/ovs-thread.c | 18 +++++++++++++ > lib/ovs-thread.h | 76 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 100 insertions(+), 6 deletions(-) > > diff --git a/Makefile.am b/Makefile.am > index 193b19e..488aed2 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -185,17 +185,17 @@ config-h-check: > fi > .PHONY: config-h-check > > -# Check that "struct vlog_ratelimit" is always declared "static". > -ALL_LOCAL += rate-limit-check > -rate-limit-check: > +# Check that certain data structures are always declared "static". > +ALL_LOCAL += static-check > +static-check: > @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1 && \ > - git --no-pager grep -n -E '^[ ]+struct vlog_rate_limit.*=' > $(srcdir); \ > + git --no-pager grep -n -E '^[ ]+(struct > vlog_rate_limit|pthread_once_t|struct ovsthread_once).*=' $(srcdir); \ > then \ > echo "See above for list of violations of the rule that "; \ > - echo "'struct vlog_rate_limit' must always be 'static'"; \ > + echo "certain data structures must always be 'static'"; \ > exit 1; \ > fi > -.PHONY: rate-limit-check > +.PHONY: static-check > > # Check that assert.h is not used outside a whitelist of files. > ALL_LOCAL += check-assert-h-usage > diff --git a/lib/ovs-thread.c b/lib/ovs-thread.c > index f48a659..d69ec5e 100644 > --- a/lib/ovs-thread.c > +++ b/lib/ovs-thread.c > @@ -105,4 +105,22 @@ xpthread_create(pthread_t *threadp, pthread_attr_t *attr, > ovs_abort(error, "pthread_create failed"); > } > } > + > +bool > +ovsthread_once_start__(struct ovsthread_once *once) > +{ > + xpthread_mutex_lock(&once->mutex); > + if (!ovsthread_once_is_done__(once)) { > + return false; > + } > + xpthread_mutex_unlock(&once->mutex); > + return true; > +} > + > +void OVS_RELEASES(once) > +ovsthread_once_done(struct ovsthread_once *once) > +{ > + atomic_store(&once->done, true); > + xpthread_mutex_unlock(&once->mutex); > +} > #endif > diff --git a/lib/ovs-thread.h b/lib/ovs-thread.h > index 752e44f..4779030 100644 > --- a/lib/ovs-thread.h > +++ b/lib/ovs-thread.h > @@ -18,6 +18,7 @@ > #define OVS_THREAD_H 1 > > #include <pthread.h> > +#include "ovs-atomic.h" > #include "util.h" > > /* glibc has some non-portable mutex types and initializers: > @@ -279,5 +280,80 @@ void xpthread_create(pthread_t *, pthread_attr_t *, void > *(*)(void *), void *); > return NAME##_set__(value); \ > } > > +/* Convenient once-only execution. > + * > + * > + * Problem > + * ======= > + * > + * POSIX provides pthread_once_t and pthread_once() as primitives for > running a > + * set of code only once per process execution. They are used like this: > + * > + * static void run_once(void) { ...initialization... } > + * static pthread_once_t once = PTHREAD_ONCE_INIT; > + * ... > + * pthread_once(&once, run_once); > + * > + * pthread_once() does not allow passing any parameters to the initialization > + * function, which is often inconvenient, because it means that the function > + * can only access data declared at file scope. > + * > + * > + * Solution > + * ======== > + * > + * Use ovsthread_once, like this, instead: > + * > + * static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; > + * > + * if (ovsthread_once_start(&once)) { > + * ...initialization... > + * ovsthread_once_done(&once); > + * } > + */ > + > +struct ovsthread_once { > + atomic_bool done; > + pthread_mutex_t mutex; > +}; > + > +#define OVSTHREAD_ONCE_INITIALIZER \ > + { \ > + ATOMIC_VAR_INIT(false), \ > + PTHREAD_ADAPTIVE_MUTEX_INITIALIZER, \ > + } > + > +static inline bool ovsthread_once_start(struct ovsthread_once *); > +void ovsthread_once_done(struct ovsthread_once *once) OVS_RELEASES(once); > + > +bool ovsthread_once_start__(struct ovsthread_once *); > + > +static inline bool > +ovsthread_once_is_done__(const struct ovsthread_once *once) > +{ > + bool done; > + > + atomic_read_explicit(&once->done, &done, memory_order_relaxed); > + return done; > +} > + > +/* Returns true if this is the first call to ovsthread_once_start() for > + * 'once'. In this case, the caller should perform whatever initialization > + * actions it needs to do, then call ovsthread_once_done() for 'once'. > + * > + * Returns false if this is not the first call to ovsthread_once_start() for > + * 'once'. In this case, the call will not return until after > + * ovsthread_once_done() has been called. */ > +static inline bool > +ovsthread_once_start(struct ovsthread_once *once) > +{ > + return OVS_UNLIKELY(!ovsthread_once_is_done__(once) > + && !ovsthread_once_start__(once)); > +} > + > +#ifdef __CHECKER__ > +#define ovsthread_once_start(ONCE) \ > + ((ONCE)->done ? false : ({ OVS_ACQUIRE(ONCE); true; })) > +#endif > > #endif /* ovs-thread.h */ > -- > 1.7.2.5 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev X-CudaMail-Whitelist-To: dev@openvswitch.org _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev