mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 17:51:53 +00:00
Unit test for RCU
This commit is contained in:
parent
178ae9ba9e
commit
78a7d16c9d
@ -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)
|
obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(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_targets := $(tests_targets) $(tests-target-files)
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
204
lib/rcu_test.c
Normal file
204
lib/rcu_test.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* BIRD Library -- Auto storage attribute cleanup test
|
||||||
|
*
|
||||||
|
* (c) 2023 Maria Matejka <mq@jmq.cz>
|
||||||
|
* (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 <pthread.h>
|
||||||
|
|
||||||
|
#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; i<WRITER_ROUNDS; i++)
|
||||||
|
{
|
||||||
|
struct block *next = &ball[order][i];
|
||||||
|
ASSERT_DIE(next->value == 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<READERS; i++)
|
||||||
|
pthread_create(&readers[i], NULL, t_rcu_basic_reader, NULL);
|
||||||
|
|
||||||
|
for (uintptr_t i=0; i<WRITERS; i++)
|
||||||
|
pthread_create(&writers[i], NULL, t_rcu_basic_writer, (void *) i);
|
||||||
|
|
||||||
|
for (uintptr_t i=0; i<WRITERS; i++)
|
||||||
|
pthread_join(writers[i], NULL);
|
||||||
|
|
||||||
|
for (uintptr_t i=0; i<READERS; i++)
|
||||||
|
pthread_join(readers[i], NULL);
|
||||||
|
|
||||||
|
for (uint w = 0; w < WRITERS; w++)
|
||||||
|
for (uint r = 0; r < WRITER_ROUNDS; r++)
|
||||||
|
{
|
||||||
|
ASSERT_DIE(ball[w][r].value == 0xd4d4d4d4d4d4d4d4);
|
||||||
|
ASSERT_DIE(atomic_load_explicit(&ball[w][r].next, memory_order_relaxed) == (void *) 0xd8d8d8d8d8d8d8d8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
bt_init(argc, argv);
|
||||||
|
|
||||||
|
bt_test_suite(t_rcu_basic, "Basic RCU check");
|
||||||
|
|
||||||
|
return bt_exit_value();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user