diff --git a/tests/TESTS b/tests/TESTS index 72f11589c8..1b03efa517 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -18,6 +18,7 @@ opr/time opr/uuid ptserver/pt_util ptserver/pts-man +rx/atomic rx/event rx/perf volser/vos-man diff --git a/tests/rx/.gitignore b/tests/rx/.gitignore index 6389919e21..230b5820df 100644 --- a/tests/rx/.gitignore +++ b/tests/rx/.gitignore @@ -1 +1,2 @@ +/atomic-t /event-t diff --git a/tests/rx/Makefile.in b/tests/rx/Makefile.in index 5af2d171b5..fe172e4693 100644 --- a/tests/rx/Makefile.in +++ b/tests/rx/Makefile.in @@ -10,10 +10,13 @@ MODULE_CFLAGS = -I$(TOP_OBJDIR) LIBS = $(abs_top_builddir)/tests/common/libafstest_common.la \ $(abs_top_builddir)/src/rx/liboafs_rx.la -BINS = event-t +BINS = atomic-t event-t all: $(BINS) +atomic-t: atomic-t.o $(LIBS) + $(LT_LDRULE_static) atomic-t.o $(LIBS) $(LIB_roken) $(XLIBS) + event-t: event-t.o $(LIBS) $(LT_LDRULE_static) event-t.o $(LIBS) $(LIB_roken) $(XLIBS) install: diff --git a/tests/rx/atomic-t.c b/tests/rx/atomic-t.c new file mode 100644 index 0000000000..b909e671c0 --- /dev/null +++ b/tests/rx/atomic-t.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2022 Sine Nomine Associates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include + +#include +#include + +struct thread_args { + rx_atomic_t atom; + int min_val; + int max_val; +}; + +/* This runs various atomic ops using the starting value of 'val', and results + * in setting various values in the range [val-20,val+20]. */ +static void +test_simple(int val) +{ + rx_atomic_t atom = RX_ATOMIC_INIT(val); + + diag("init val %d", val); + + is_int(val, rx_atomic_read(&atom), "rx_atomic_read -> %d", val); + + val += 5; + rx_atomic_set(&atom, val); + is_int(val, rx_atomic_read(&atom), "rx_atomic_set -> %d", val); + + val++; + rx_atomic_inc(&atom); + is_int(val, rx_atomic_read(&atom), "rx_atomic_inc -> %d", val); + + val++; + is_int(val, rx_atomic_inc_and_read(&atom), "rx_atomic_inc_and_read -> %d", val); + + val += 3; + rx_atomic_add(&atom, 3); + is_int(val, rx_atomic_read(&atom), "rx_atomic_add -> %d", val); + + val += 10; + is_int(val, rx_atomic_add_and_read(&atom, 10), "rx_atomic_add_and_read -> %d", val); + + val--; + rx_atomic_dec(&atom); + is_int(val, rx_atomic_read(&atom), "rx_atomic_dec -> %d", val); + + val--; + is_int(val, rx_atomic_dec_and_read(&atom), "rx_atomic_dec_and_read -> %d", val); + + val -= 3; + rx_atomic_sub(&atom, 3); + is_int(val, rx_atomic_read(&atom), "rx_atomic_sub -> %d", val); + + /* Get back to 20 below the initial value. */ + val -= 35; + rx_atomic_sub(&atom, 35); + is_int(val, rx_atomic_read(&atom), "rx_atomic_sub -> %d", val); +} + +static void * +atom_thread(void *rock) +{ +#define I_REPS 1000 +#define J_REPS 1000 + + struct thread_args *args = rock; + rx_atomic_t *atom = &args->atom; + int i, j; + int val; + const char *last_op; + + for (i = 0; i < I_REPS; i++) { + last_op = "rx_atomic_inc/dec"; + for (j = 0; j < J_REPS; j++) { + rx_atomic_inc(atom); + rx_atomic_dec(atom); + } + + val = rx_atomic_read(atom); + if (val < args->min_val || val > args->max_val) { + goto fail; + } + + for (j = 0; j < J_REPS; j++) { + val = rx_atomic_inc_and_read(atom); + if (val < args->min_val || val > args->max_val) { + last_op = "rx_atomic_inc_and_read"; + goto fail; + } + + val = rx_atomic_dec_and_read(atom); + if (val < args->min_val || val > args->max_val) { + last_op = "rx_atomic_dec_and_read"; + goto fail; + } + } + + last_op = "rx_atomic/add/sub/add_and_read"; + for (j = 0; j < J_REPS; j++) { + rx_atomic_add(atom, 2); + rx_atomic_sub(atom, 4); + val = rx_atomic_add_and_read(atom, 2); + if (val < args->min_val || val > args->max_val) { + goto fail; + } + } + } + + return rock; + + fail: + printf("# bad value after %s: %d (min %d, max %d)\n", + last_op, val, args->min_val, args->max_val); + return NULL; +} + +static void +test_threads(int val) +{ +#define N_THREADS 10 + pthread_t tid[N_THREADS]; + struct thread_args args; + int thread_i; + + memset(&args, 0, sizeof(args)); + args.min_val = val - N_THREADS * 2; + args.max_val = val + N_THREADS * 2; + + diag("threaded init val %d", val); + + rx_atomic_set(&args.atom, val); + + for (thread_i = 0; thread_i < N_THREADS; thread_i++) { + opr_Verify(pthread_create(&tid[thread_i], NULL, atom_thread, &args) == 0); + } + + for (thread_i = 0; thread_i < N_THREADS; thread_i++) { + void *ret; + opr_Verify(pthread_join(tid[thread_i], &ret) == 0); + + ok(ret != NULL, "atom thread %d/%d", thread_i, N_THREADS - 1); + } + + is_int(val, rx_atomic_read(&args.atom), "final atomic value"); +} + +int +main(void) +{ + plan(61); + + test_simple(0); + + /* Pick a couple of values that will cause us to cross from positive to + * negative, and negative to positive, for at least one operation. */ + test_simple(14); + test_simple(-14); + + /* Pick a couple of large values somewhat close to positive/negative 2^31, + * but not close enough that we overflow. */ + test_simple(2147483600); + test_simple(-2147483600); + + test_threads(0); + + return 0; +} +