ovs-thread: Quiesce in xpthread_barrier_wait().
[sliver-openvswitch.git] / lib / ovs-thread.c
index 76cbbd3..f33f5f1 100644 (file)
@@ -22,6 +22,7 @@
 #include <unistd.h>
 #include "compiler.h"
 #include "hash.h"
+#include "ovs-rcu.h"
 #include "poll-loop.h"
 #include "socket-util.h"
 #include "util.h"
@@ -223,7 +224,12 @@ void
 ovs_mutex_cond_wait(pthread_cond_t *cond, const struct ovs_mutex *mutex_)
 {
     struct ovs_mutex *mutex = CONST_CAST(struct ovs_mutex *, mutex_);
-    int error = pthread_cond_wait(cond, &mutex->lock);
+    int error;
+
+    ovsrcu_quiesce_start();
+    error = pthread_cond_wait(cond, &mutex->lock);
+    ovsrcu_quiesce_end();
+
     if (OVS_UNLIKELY(error)) {
         ovs_abort(error, "pthread_cond_wait failed");
     }
@@ -234,7 +240,10 @@ xpthread_barrier_wait(pthread_barrier_t *barrier)
 {
     int error;
 
+    ovsrcu_quiesce_start();
     error = pthread_barrier_wait(barrier);
+    ovsrcu_quiesce_end();
+
     if (error && OVS_UNLIKELY(error != PTHREAD_BARRIER_SERIAL_THREAD)) {
         ovs_abort(error, "pthread_barrier_wait failed");
     }
@@ -264,6 +273,7 @@ ovsthread_wrapper(void *aux_)
     aux = *auxp;
     free(auxp);
 
+    ovsrcu_quiesce_end();
     return aux.start(aux.arg);
 }
 
@@ -277,6 +287,7 @@ xpthread_create(pthread_t *threadp, pthread_attr_t *attr,
 
     forbid_forking("multiple threads exist");
     multithreaded = true;
+    ovsrcu_quiesce_end();
 
     aux = xmalloc(sizeof *aux);
     aux->start = start;
@@ -307,6 +318,12 @@ ovsthread_once_done(struct ovsthread_once *once)
     ovs_mutex_unlock(&once->mutex);
 }
 \f
+bool
+single_threaded(void)
+{
+    return !multithreaded;
+}
+
 /* Asserts that the process has not yet created any threads (beyond the initial
  * thread).
  *
@@ -365,83 +382,53 @@ may_fork(void)
     return !must_not_fork;
 }
 \f
-/* ovsthread_counter.
- *
- * We implement the counter as an array of N_COUNTERS individual counters, each
- * with its own lock.  Each thread uses one of the counters chosen based on a
- * hash of the thread's ID, the idea being that, statistically, different
- * threads will tend to use different counters and therefore avoid
- * interfering with each other.
- *
- * Undoubtedly, better implementations are possible. */
-
-/* Basic counter structure. */
-struct ovsthread_counter__ {
-    struct ovs_mutex mutex;
-    unsigned long long int value;
-};
-
-/* Pad the basic counter structure to 64 bytes to avoid cache line
- * interference. */
-struct ovsthread_counter {
-    struct ovsthread_counter__ c;
-    char pad[ROUND_UP(sizeof(struct ovsthread_counter__), 64)
-             - sizeof(struct ovsthread_counter__)];
-};
+/* ovsthread_stats. */
 
-#define N_COUNTERS 16
-
-struct ovsthread_counter *
-ovsthread_counter_create(void)
+void
+ovsthread_stats_init(struct ovsthread_stats *stats)
 {
-    struct ovsthread_counter *c;
     int i;
 
-    c = xmalloc(N_COUNTERS * sizeof *c);
-    for (i = 0; i < N_COUNTERS; i++) {
-        ovs_mutex_init(&c[i].c.mutex);
-        c[i].c.value = 0;
+    ovs_mutex_init(&stats->mutex);
+    for (i = 0; i < ARRAY_SIZE(stats->buckets); i++) {
+        stats->buckets[i] = NULL;
     }
-    return c;
 }
 
 void
-ovsthread_counter_destroy(struct ovsthread_counter *c)
+ovsthread_stats_destroy(struct ovsthread_stats *stats)
 {
-    if (c) {
-        int i;
-
-        for (i = 0; i < N_COUNTERS; i++) {
-            ovs_mutex_destroy(&c[i].c.mutex);
-        }
-        free(c);
-    }
+    ovs_mutex_destroy(&stats->mutex);
 }
 
-void
-ovsthread_counter_inc(struct ovsthread_counter *c, unsigned long long int n)
+void *
+ovsthread_stats_bucket_get(struct ovsthread_stats *stats,
+                           void *(*new_bucket)(void))
 {
-    c = &c[hash_int(ovsthread_id_self(), 0) % N_COUNTERS];
-
-    ovs_mutex_lock(&c->c.mutex);
-    c->c.value += n;
-    ovs_mutex_unlock(&c->c.mutex);
+    unsigned int idx = ovsthread_id_self() & (ARRAY_SIZE(stats->buckets) - 1);
+    void *bucket = stats->buckets[idx];
+    if (!bucket) {
+        ovs_mutex_lock(&stats->mutex);
+        bucket = stats->buckets[idx];
+        if (!bucket) {
+            bucket = stats->buckets[idx] = new_bucket();
+        }
+        ovs_mutex_unlock(&stats->mutex);
+    }
+    return bucket;
 }
 
-unsigned long long int
-ovsthread_counter_read(const struct ovsthread_counter *c)
+size_t
+ovs_thread_stats_next_bucket(const struct ovsthread_stats *stats, size_t i)
 {
-    unsigned long long int sum;
-    int i;
-
-    sum = 0;
-    for (i = 0; i < N_COUNTERS; i++) {
-        ovs_mutex_lock(&c[i].c.mutex);
-        sum += c[i].c.value;
-        ovs_mutex_unlock(&c[i].c.mutex);
+    for (; i < ARRAY_SIZE(stats->buckets); i++) {
+        if (stats->buckets[i]) {
+            break;
+        }
     }
-    return sum;
+    return i;
 }
+
 \f
 /* Parses /proc/cpuinfo for the total number of physical cores on this system
  * across all CPU packages, not counting hyper-threads.