tests: Add bundle action test with buffer realloc.
[cascardo/ovs.git] / tests / test-atomic.c
index 24456d8..efc5053 100644 (file)
  */
 
 #include <config.h>
-
+#undef NDEBUG
+#include "fatal-signal.h"
 #include "ovs-atomic.h"
-#include "util.h"
 #include "ovstest.h"
+#include "ovs-thread.h"
+#include "timeval.h"
+#include "util.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(test_atomic);
 
 #define TEST_ATOMIC_TYPE(ATOMIC_TYPE, BASE_TYPE)        \
     {                                                   \
         ovs_assert(value == 8);                         \
     }
 
+#define TEST_ATOMIC_TYPE_EXPLICIT(ATOMIC_TYPE, BASE_TYPE,               \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW)   \
+    {                                                                   \
+        ATOMIC_TYPE x = ATOMIC_VAR_INIT(1);                             \
+        BASE_TYPE value, orig;                                          \
+                                                                        \
+        atomic_read_explicit(&x, &value, ORDER_READ);                   \
+        ovs_assert(value == 1);                                         \
+                                                                        \
+        atomic_store_explicit(&x, 2, ORDER_STORE);                      \
+        atomic_read_explicit(&x, &value, ORDER_READ);                   \
+        ovs_assert(value == 2);                                         \
+                                                                        \
+        atomic_init(&x, 3);                                             \
+        atomic_read_explicit(&x, &value, ORDER_READ);                   \
+        ovs_assert(value == 3);                                         \
+                                                                        \
+        atomic_add_explicit(&x, 1, &orig, ORDER_RMW);                   \
+        ovs_assert(orig == 3);                                          \
+        atomic_read_explicit(&x, &value, ORDER_READ);                   \
+        ovs_assert(value == 4);                                         \
+                                                                        \
+        atomic_sub_explicit(&x, 2, &orig, ORDER_RMW);                   \
+        ovs_assert(orig == 4);                                          \
+        atomic_read_explicit(&x, &value, ORDER_READ);                   \
+        ovs_assert(value == 2);                                         \
+                                                                        \
+        atomic_or_explicit(&x, 6, &orig, ORDER_RMW);                    \
+        ovs_assert(orig == 2);                                          \
+        atomic_read_explicit(&x, &value, ORDER_READ);                   \
+        ovs_assert(value == 6);                                         \
+                                                                        \
+        atomic_and_explicit(&x, 10, &orig, ORDER_RMW);                  \
+        ovs_assert(orig == 6);                                          \
+        atomic_read_explicit(&x, &value, ORDER_READ);                   \
+        ovs_assert(value == 2);                                         \
+                                                                        \
+        atomic_xor_explicit(&x, 10, &orig, ORDER_RMW);                  \
+        ovs_assert(orig == 2);                                          \
+        atomic_read_explicit(&x, &value, ORDER_READ);                   \
+        ovs_assert(value == 8);                                         \
+    }
+
+
+#define TEST_ATOMIC_ORDER(ORDER_READ, ORDER_STORE, ORDER_RMW)           \
+    {                                                                   \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_char, char,                    \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_uchar, unsigned char,          \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_schar, signed char,            \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_short, short,                  \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_ushort, unsigned short,        \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_int, int,                      \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint, unsigned int,            \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_long, long int,                \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_ulong, unsigned long int,      \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_llong, long long int,          \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_ullong, unsigned long long int, \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_size_t, size_t,                \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_ptrdiff_t, ptrdiff_t,          \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_intmax_t, intmax_t,            \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_uintmax_t, uintmax_t,          \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_intptr_t, intptr_t,            \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_uintptr_t, uintptr_t,          \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint8_t, uint8_t,              \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_int8_t, int8_t,                \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint16_t, uint16_t,            \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_int16_t, int16_t,              \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint32_t, uint32_t,            \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+        TEST_ATOMIC_TYPE_EXPLICIT(atomic_int32_t, int32_t,              \
+                                  ORDER_READ, ORDER_STORE, ORDER_RMW);  \
+    }
+
 static void
 test_atomic_flag(void)
 {
@@ -72,9 +172,185 @@ test_atomic_flag(void)
     ovs_assert(atomic_flag_test_and_set(&flag) == false);
 }
 
+static uint32_t a;
+
+struct atomic_aux {
+    ATOMIC(uint64_t) count;
+    uint32_t b;
+    ATOMIC(uint32_t *) data;
+    ATOMIC(uint64_t) data64;
+};
+
+static ATOMIC(struct atomic_aux *) paux = ATOMIC_VAR_INIT(NULL);
+static struct atomic_aux *auxes = NULL;
+
+#define ATOMIC_ITEM_COUNT 1000000
+#define        DURATION 5000
+
+static void *
+atomic_consumer(void * arg1 OVS_UNUSED)
+{
+    struct atomic_aux *old_aux = NULL;
+    uint64_t count;
+    long long int stop_time = time_msec() + DURATION;
+
+    do {
+        struct atomic_aux *aux;
+        uint32_t b;
+
+        /* Wait for a new item.  We may not be fast enough to process every
+         * item, but we are guaranteed to see the last one. */
+        do {
+            atomic_read_explicit(&paux, &aux, memory_order_consume);
+        } while (aux == old_aux);
+
+        b = aux->b;
+        atomic_read_explicit(&aux->count, &count, memory_order_relaxed);
+        ovs_assert(b == count + 42);
+
+        old_aux = aux;
+    } while (count < ATOMIC_ITEM_COUNT - 1 && time_msec() < stop_time);
+
+    if (time_msec() >= stop_time) {
+        if (count < 10) {
+            VLOG_WARN("atomic_consumer test stopped due to excessive runtime. "
+                      "Count = %"PRIu64, count);
+        }
+    }
+
+    return NULL;
+}
+
+static void *
+atomic_producer(void * arg1 OVS_UNUSED)
+{
+    size_t i;
+
+    for (i = 0; i < ATOMIC_ITEM_COUNT; i++) {
+        struct atomic_aux *aux = &auxes[i];
+
+        aux->count = ATOMIC_VAR_INIT(i);
+        aux->b = i + 42;
+
+        /* Publish the new item. */
+        atomic_store_explicit(&paux, aux, memory_order_release);
+    }
+
+    return NULL;
+}
 
 static void
-test_atomic_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+test_cons_rel(void)
+{
+    pthread_t reader, writer;
+
+    atomic_init(&paux, NULL);
+
+    auxes = xmalloc(sizeof *auxes * ATOMIC_ITEM_COUNT);
+
+    reader = ovs_thread_create("consumer", atomic_consumer, NULL);
+    writer = ovs_thread_create("producer", atomic_producer, NULL);
+
+    xpthread_join(reader, NULL);
+    xpthread_join(writer, NULL);
+
+    free(auxes);
+}
+
+static void *
+atomic_reader(void *aux_)
+{
+    struct atomic_aux *aux = aux_;
+    uint64_t count;
+    uint64_t data;
+    long long int now = time_msec();
+    long long int stop_time = now + DURATION;
+
+    do {
+        /* Non-synchronized add. */
+        atomic_add_explicit(&aux->count, 1, &count, memory_order_relaxed);
+
+        do {
+            atomic_read_explicit(&aux->data64, &data, memory_order_acquire);
+        } while (!data && (now = time_msec()) < stop_time);
+
+        if (now >= stop_time) {
+            if (count < 10) {
+                VLOG_WARN("atomic_reader test stopped due to excessive "
+                          "runtime. Count = %"PRIu64, count);
+            }
+            break;
+        }
+
+        ovs_assert(data == a && data == aux->b && a == aux->b);
+
+        atomic_read_explicit(&aux->count, &count, memory_order_relaxed);
+
+        ovs_assert(count == 2 * a && count == 2 * aux->b && count == 2 * data);
+
+        atomic_store_explicit(&aux->data64, UINT64_C(0), memory_order_release);
+    } while (count < 2 * ATOMIC_ITEM_COUNT);
+
+    return NULL;
+}
+
+static void *
+atomic_writer(void *aux_)
+{
+    struct atomic_aux *aux = aux_;
+    uint64_t old_count;
+    uint64_t data;
+    size_t i;
+    long long int now = time_msec();
+    long long int stop_time = now + DURATION;
+
+    for (i = 0; i < ATOMIC_ITEM_COUNT; i++) {
+        /* Wait for the reader to be done with the data. */
+        do {
+            atomic_read_explicit(&aux->data64, &data, memory_order_acquire);
+        } while (data && (now = time_msec()) < stop_time);
+
+        if (now >= stop_time) {
+            if (i < 10) {
+                VLOG_WARN("atomic_writer test stopped due to excessive "
+                          "runtime, Count = %"PRIuSIZE, i);
+            }
+            break;
+        }
+
+        a = i + 1;
+        atomic_add_explicit(&aux->count, 1, &old_count, memory_order_relaxed);
+        aux->b++;
+        atomic_store_explicit(&aux->data64,
+                              (i & 1) ? (uint64_t)aux->b : a, memory_order_release);
+    }
+
+    return NULL;
+}
+
+static void
+test_acq_rel(void)
+{
+    pthread_t reader, writer;
+    struct atomic_aux *aux = xmalloc(sizeof *aux);
+
+    a = 0;
+    aux->b = 0;
+
+    aux->count = ATOMIC_VAR_INIT(0);
+    atomic_init(&aux->data, NULL);
+    aux->data64 = ATOMIC_VAR_INIT(0);
+
+    reader = ovs_thread_create("reader", atomic_reader, aux);
+    writer = ovs_thread_create("writer", atomic_writer, aux);
+
+    xpthread_join(reader, NULL);
+    xpthread_join(writer, NULL);
+    free(aux);
+}
+
+static void
+test_atomic_plain(void)
 {
     TEST_ATOMIC_TYPE(atomic_char, char);
     TEST_ATOMIC_TYPE(atomic_uchar, unsigned char);
@@ -99,8 +375,58 @@ test_atomic_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     TEST_ATOMIC_TYPE(atomic_int16_t, int16_t);
     TEST_ATOMIC_TYPE(atomic_uint32_t, uint32_t);
     TEST_ATOMIC_TYPE(atomic_int32_t, int32_t);
+}
+
+static void
+test_atomic_relaxed(void)
+{
+    TEST_ATOMIC_ORDER(memory_order_relaxed, memory_order_relaxed,
+                      memory_order_relaxed);
+}
+
+static void
+test_atomic_consume(void)
+{
+    TEST_ATOMIC_ORDER(memory_order_consume, memory_order_release,
+                      memory_order_release);
+}
+
+static void
+test_atomic_acquire(void)
+{
+    TEST_ATOMIC_ORDER(memory_order_acquire, memory_order_release,
+                      memory_order_release);
+}
+
+static void
+test_atomic_acq_rel(void)
+{
+    TEST_ATOMIC_ORDER(memory_order_acquire, memory_order_release,
+                      memory_order_acq_rel);
+}
+
+static void
+test_atomic_seq_cst(void)
+{
+    TEST_ATOMIC_ORDER(memory_order_seq_cst, memory_order_seq_cst,
+                      memory_order_seq_cst);
+}
+
+static void
+test_atomic_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    fatal_signal_init();
+    test_atomic_plain();
+    test_atomic_relaxed();
+    test_atomic_consume();
+    test_atomic_acquire();
+    test_atomic_acq_rel();
+    test_atomic_seq_cst();
 
     test_atomic_flag();
+
+    test_acq_rel();
+    test_cons_rel();
 }
 
 OVSTEST_REGISTER("test-atomic", test_atomic_main);