diff --git a/Makefile.am b/Makefile.am index 193b19ea9..488aed25b 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 23224323c..e1e728912 100644 --- a/lib/ovs-thread.c +++ b/lib/ovs-thread.c @@ -88,4 +88,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 0a5a43372..05461e201 100644 --- a/lib/ovs-thread.h +++ b/lib/ovs-thread.h @@ -18,6 +18,7 @@ #define OVS_THREAD_H 1 #include +#include "ovs-atomic.h" #include "util.h" /* glibc has some non-portable mutex types and initializers: @@ -289,5 +290,80 @@ void xpthread_create(pthread_t *, pthread_attr_t *, void *(*)(void *), void *); return NAME##_set_unsafe(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 */