From 7c41d860af1eca960c7fb0de52929a820d393d6d Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Sun, 2 Jun 2024 22:49:35 +0200 Subject: [PATCH] Unit test for RCU --- lib/Makefile | 2 +- lib/rcu_test.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 lib/rcu_test.c diff --git a/lib/Makefile b/lib/Makefile index 5c106e2a..1a2cc928 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -2,6 +2,6 @@ src := a-path.c a-set.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c defer.c obj := $(src-o-files) $(all-daemon) -tests_src := a-set_test.c a-path_test.c attribute_cleanup_test.c bitmap_test.c heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c locking_test.c mac_test.c ip_test.c hash_test.c printf_test.c slab_test.c tlists_test.c type_test.c +tests_src := a-set_test.c a-path_test.c attribute_cleanup_test.c bitmap_test.c heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c locking_test.c mac_test.c ip_test.c hash_test.c printf_test.c rcu_test.c slab_test.c tlists_test.c type_test.c tests_targets := $(tests_targets) $(tests-target-files) tests_objs := $(tests_objs) $(src-o-files) diff --git a/lib/rcu_test.c b/lib/rcu_test.c new file mode 100644 index 00000000..b8372132 --- /dev/null +++ b/lib/rcu_test.c @@ -0,0 +1,204 @@ +/* + * BIRD Library -- Auto storage attribute cleanup test + * + * (c) 2023 Maria Matejka + * (c) 2023 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "test/birdtest.h" + +#include "lib/rcu.h" +#include "lib/io-loop.h" + +#include + +#define WRITERS 3 +#define READERS 28 + +#define WRITER_ROUNDS 20 + +static struct block { + struct block * _Atomic next; + u64 value; +} ball[WRITERS][WRITER_ROUNDS]; + +static struct block *_Atomic bin; +static _Atomic uint seen = 0; + +_Thread_local struct rcu_thread rtl; + +static void * +t_rcu_basic_reader(void *_ UNUSED) +{ + rcu_thread_start(&rtl); + + while (atomic_load_explicit(&bin, memory_order_acquire) == NULL) + birdloop_yield(); + + atomic_fetch_add_explicit(&seen, 1, memory_order_release); + + while (atomic_load_explicit(&bin, memory_order_acquire)) + { + rcu_read_lock(); + + uint mod = 0; + for (struct block * _Atomic *bp = &bin, *b; + b = atomic_load_explicit(bp, memory_order_acquire); + bp = &b->next) + { + uint val = b->value % WRITERS + 1; + ASSERT_DIE(val > mod); + mod = val; + } + + ASSERT_DIE(mod <= WRITERS); + + rcu_read_unlock(); + } + + rcu_thread_stop(&rtl); + return NULL; +} + +static _Atomic uint spinlock = 0; + +static inline void +spin_lock(void) +{ + while (atomic_exchange_explicit(&spinlock, 1, memory_order_acq_rel)) + birdloop_yield(); +} + +static inline void +spin_unlock(void) +{ + ASSERT_DIE(atomic_exchange_explicit(&spinlock, 0, memory_order_acq_rel)); +} + +static void * +t_rcu_basic_writer(void *order_ptr) +{ + rcu_thread_start(&rtl); + + uint order = (uintptr_t) order_ptr; + struct block *cur = &ball[order][0]; + + /* Insert the object */ + spin_lock(); + for (struct block * _Atomic *bp = &bin; bp; ) + { + struct block *b = atomic_load_explicit(bp, memory_order_acquire); + if (b && ((b->value % WRITERS) < order)) + bp = &b->next; + else + { + ASSERT_DIE(cur->value == 0xbabababababababa); + cur->value = order; + atomic_store_explicit(&cur->next, b, memory_order_relaxed); + atomic_store_explicit(bp, cur, memory_order_release); + break; + } + } + spin_unlock(); + + /* Wait for readers */ + while (atomic_load_explicit(&seen, memory_order_acquire) != READERS) + birdloop_yield(); + + /* Update the object */ + for (uint i=1; ivalue == 0xbabababababababa); + next->value = order + i*WRITERS; + + spin_lock(); + _Bool seen = 0; + for (struct block * _Atomic *bp = &bin, *b; + b = atomic_load_explicit(bp, memory_order_acquire); + bp = &b->next) + if (b == cur) + { + struct block *link = atomic_load_explicit(&b->next, memory_order_relaxed); + atomic_store_explicit(&next->next, link, memory_order_relaxed); + atomic_store_explicit(bp, next, memory_order_release); + seen = 1; + break; + } + ASSERT_DIE(seen); + spin_unlock(); + + synchronize_rcu(); + + ASSERT_DIE(cur->value + WRITERS == next->value); + cur->value = 0xd4d4d4d4d4d4d4d4; + atomic_store_explicit(&cur->next, ((void *) 0xd8d8d8d8d8d8d8d8), memory_order_relaxed); + + cur = next; + } + + /* Remove the object */ + spin_lock(); + _Bool seen = 0; + for (struct block * _Atomic *bp = &bin, *b; + b = atomic_load_explicit(bp, memory_order_acquire); + bp = &b->next) + if (b == cur) + { + struct block *link = atomic_load_explicit(&b->next, memory_order_relaxed); + atomic_store_explicit(bp, link, memory_order_relaxed); + seen = 1; + break; + } + ASSERT_DIE(seen); + spin_unlock(); + + synchronize_rcu(); + + cur->value = 0xd4d4d4d4d4d4d4d4; + atomic_store_explicit(&cur->next, ((void *) 0xd8d8d8d8d8d8d8d8), memory_order_relaxed); + + rcu_thread_stop(&rtl); + return NULL; +} + +static int +t_rcu_basic(void) +{ + memset(ball, 0xba, sizeof ball); + + pthread_t readers[READERS]; + pthread_t writers[WRITERS]; + + for (uint i=0; i