From 1491fc3840762ae57661f247ae363be83e0132a2 Mon Sep 17 00:00:00 2001 From: weiyu Date: Tue, 4 Aug 2020 12:37:20 -0700 Subject: [PATCH] Add data structure benchmarks --- cdschecker_modified_benchmarks/Makefile | 7 + .../barrier/Makefile | 11 + .../barrier/barrier.cc | 43 +++ .../barrier/barrier.h | 36 +++ cdschecker_modified_benchmarks/benchmarks.mk | 10 + .../chase-lev-deque/Makefile | 17 ++ .../chase-lev-deque/deque.cc | 85 ++++++ .../chase-lev-deque/deque.h | 25 ++ .../chase-lev-deque/main.cc | 52 ++++ .../dekker-fences/Makefile | 11 + .../dekker-fences/dekker-fences.cc | 95 +++++++ .../include/cds_atomic.h | 52 ++++ .../include/cds_threads.h | 38 +++ .../include/librace.h | 29 ++ .../include/model-assert.h | 21 ++ .../include/unrelacy.h | 96 +++++++ .../linuxrwlocks/Makefile | 11 + .../linuxrwlocks/linuxrwlocks.cc | 118 ++++++++ .../mcs-lock/Makefile | 11 + .../mcs-lock/mcs-lock.cc | 43 +++ .../mcs-lock/mcs-lock.h | 93 +++++++ .../mpmc-queue/Makefile | 24 ++ .../mpmc-queue/mpmc-queue.cc | 143 ++++++++++ .../mpmc-queue/mpmc-queue.h | 97 +++++++ .../ms-queue-tsan11/Makefile | 17 ++ .../ms-queue-tsan11/main.cc | 87 ++++++ .../ms-queue-tsan11/main.o | Bin 0 -> 289240 bytes .../ms-queue-tsan11/my_queue.cc | 161 +++++++++++ .../ms-queue-tsan11/my_queue.h | 31 +++ .../ms-queue-tsan11/my_queue.o | Bin 0 -> 285376 bytes .../ms-queue/Makefile | 19 ++ .../ms-queue/main.cc | 88 ++++++ .../ms-queue/my_queue.c.correct | 260 ++++++++++++++++++ .../ms-queue/my_queue.cc | 167 +++++++++++ .../ms-queue/my_queue.h | 32 +++ .../spsc-queue/Makefile | 23 ++ .../spsc-queue/eventcount-relacy.h | 64 +++++ .../spsc-queue/eventcount.h | 72 +++++ .../spsc-queue/queue-relacy.h | 74 +++++ .../spsc-queue/queue.h | 77 ++++++ .../spsc-queue/spsc-queue.cc | 34 +++ .../spsc-queue/spsc-relacy.cc | 27 ++ cdschecker_modified_benchmarks/test.sh | 46 ++++ cdschecker_modified_benchmarks/test_all.sh | 13 + 44 files changed, 2460 insertions(+) create mode 100644 cdschecker_modified_benchmarks/Makefile create mode 100644 cdschecker_modified_benchmarks/barrier/Makefile create mode 100644 cdschecker_modified_benchmarks/barrier/barrier.cc create mode 100644 cdschecker_modified_benchmarks/barrier/barrier.h create mode 100644 cdschecker_modified_benchmarks/benchmarks.mk create mode 100644 cdschecker_modified_benchmarks/chase-lev-deque/Makefile create mode 100644 cdschecker_modified_benchmarks/chase-lev-deque/deque.cc create mode 100644 cdschecker_modified_benchmarks/chase-lev-deque/deque.h create mode 100644 cdschecker_modified_benchmarks/chase-lev-deque/main.cc create mode 100644 cdschecker_modified_benchmarks/dekker-fences/Makefile create mode 100644 cdschecker_modified_benchmarks/dekker-fences/dekker-fences.cc create mode 100755 cdschecker_modified_benchmarks/include/cds_atomic.h create mode 100755 cdschecker_modified_benchmarks/include/cds_threads.h create mode 100755 cdschecker_modified_benchmarks/include/librace.h create mode 100755 cdschecker_modified_benchmarks/include/model-assert.h create mode 100755 cdschecker_modified_benchmarks/include/unrelacy.h create mode 100644 cdschecker_modified_benchmarks/linuxrwlocks/Makefile create mode 100644 cdschecker_modified_benchmarks/linuxrwlocks/linuxrwlocks.cc create mode 100644 cdschecker_modified_benchmarks/mcs-lock/Makefile create mode 100644 cdschecker_modified_benchmarks/mcs-lock/mcs-lock.cc create mode 100644 cdschecker_modified_benchmarks/mcs-lock/mcs-lock.h create mode 100644 cdschecker_modified_benchmarks/mpmc-queue/Makefile create mode 100644 cdschecker_modified_benchmarks/mpmc-queue/mpmc-queue.cc create mode 100644 cdschecker_modified_benchmarks/mpmc-queue/mpmc-queue.h create mode 100755 cdschecker_modified_benchmarks/ms-queue-tsan11/Makefile create mode 100755 cdschecker_modified_benchmarks/ms-queue-tsan11/main.cc create mode 100644 cdschecker_modified_benchmarks/ms-queue-tsan11/main.o create mode 100755 cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.cc create mode 100755 cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.h create mode 100644 cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.o create mode 100644 cdschecker_modified_benchmarks/ms-queue/Makefile create mode 100644 cdschecker_modified_benchmarks/ms-queue/main.cc create mode 100644 cdschecker_modified_benchmarks/ms-queue/my_queue.c.correct create mode 100644 cdschecker_modified_benchmarks/ms-queue/my_queue.cc create mode 100644 cdschecker_modified_benchmarks/ms-queue/my_queue.h create mode 100644 cdschecker_modified_benchmarks/spsc-queue/Makefile create mode 100644 cdschecker_modified_benchmarks/spsc-queue/eventcount-relacy.h create mode 100644 cdschecker_modified_benchmarks/spsc-queue/eventcount.h create mode 100644 cdschecker_modified_benchmarks/spsc-queue/queue-relacy.h create mode 100644 cdschecker_modified_benchmarks/spsc-queue/queue.h create mode 100644 cdschecker_modified_benchmarks/spsc-queue/spsc-queue.cc create mode 100644 cdschecker_modified_benchmarks/spsc-queue/spsc-relacy.cc create mode 100755 cdschecker_modified_benchmarks/test.sh create mode 100755 cdschecker_modified_benchmarks/test_all.sh diff --git a/cdschecker_modified_benchmarks/Makefile b/cdschecker_modified_benchmarks/Makefile new file mode 100644 index 0000000..01dae3f --- /dev/null +++ b/cdschecker_modified_benchmarks/Makefile @@ -0,0 +1,7 @@ +DIRS = barrier chase-lev-deque dekker-fences linuxrwlocks mcs-lock mpmc-queue ms-queue spsc-queue + +all : + set -e; set -u; for d in $(DIRS); do (cd $$d; $(MAKE) ); done + +clean : + set -e; set -u; for d in $(DIRS); do (cd $$d; $(MAKE) clean ); done diff --git a/cdschecker_modified_benchmarks/barrier/Makefile b/cdschecker_modified_benchmarks/barrier/Makefile new file mode 100644 index 0000000..22df223 --- /dev/null +++ b/cdschecker_modified_benchmarks/barrier/Makefile @@ -0,0 +1,11 @@ +include ../benchmarks.mk + +TESTNAME = barrier + +all: $(TESTNAME) + +$(TESTNAME): $(TESTNAME).cc $(TESTNAME).h + $(CXX) -o $@ $< $(CXXFLAGS) $(LDFLAGS) + +clean: + rm -f $(TESTNAME) *.o diff --git a/cdschecker_modified_benchmarks/barrier/barrier.cc b/cdschecker_modified_benchmarks/barrier/barrier.cc new file mode 100644 index 0000000..4d3c8ba --- /dev/null +++ b/cdschecker_modified_benchmarks/barrier/barrier.cc @@ -0,0 +1,43 @@ +#include + +#include "cds_threads.h" + +#include "barrier.h" + +#include "librace.h" + +spinning_barrier *barr; +int var = 0; + +void threadA(void *arg) +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + store_32(&var, 1); + barr->wait(); +} + +void threadB(void *arg) +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + barr->wait(); + printf("var = %d\n", load_32(&var)); +} + +#define NUMREADERS 3 +int user_main(int argc, char **argv) +{ + std::thread A, B[NUMREADERS]; + int i; + + barr = new spinning_barrier(NUMREADERS + 1); + + A = std::thread(threadA, (void *)NULL); + for (i = 0; i < NUMREADERS; i++) + B[i] = std::thread(threadB, (void *)NULL); + + for (i = 0; i < NUMREADERS; i++) + B[i].join(); + A.join(); + + return 0; +} diff --git a/cdschecker_modified_benchmarks/barrier/barrier.h b/cdschecker_modified_benchmarks/barrier/barrier.h new file mode 100644 index 0000000..b52b849 --- /dev/null +++ b/cdschecker_modified_benchmarks/barrier/barrier.h @@ -0,0 +1,36 @@ +#include "cds_atomic.h" + +class spinning_barrier { + public: + spinning_barrier (unsigned int n) : n_ (n) { + nwait_ = 0; + step_ = 0; + } + + bool wait() { + unsigned int step = step_.load (); + + if (nwait_.fetch_add (1) == n_ - 1) { + /* OK, last thread to come. */ + nwait_.store (0); // XXX: maybe can use relaxed ordering here ?? + step_.fetch_add (1, std::memory_order_relaxed); + return true; + } else { + /* Run in circles and scream like a little girl. */ + while (step_.load () == step) + thrd_yield(); + return false; + } + } + + protected: + /* Number of synchronized threads. */ + const unsigned int n_; + + /* Number of threads currently spinning. */ + std::atomic nwait_; + + /* Number of barrier syncronizations completed so far, + * * it's OK to wrap. */ + std::atomic step_; +}; diff --git a/cdschecker_modified_benchmarks/benchmarks.mk b/cdschecker_modified_benchmarks/benchmarks.mk new file mode 100644 index 0000000..b103419 --- /dev/null +++ b/cdschecker_modified_benchmarks/benchmarks.mk @@ -0,0 +1,10 @@ +# A few common Makefile items + +CC=../../clang +CXX=../../clang++ + +CXXFLAGS=-std=c++0x -pthread -Wall $(SANITIZE) -g -I../include + +UNAME = $(shell uname) + + diff --git a/cdschecker_modified_benchmarks/chase-lev-deque/Makefile b/cdschecker_modified_benchmarks/chase-lev-deque/Makefile new file mode 100644 index 0000000..73266a3 --- /dev/null +++ b/cdschecker_modified_benchmarks/chase-lev-deque/Makefile @@ -0,0 +1,17 @@ +include ../benchmarks.mk + +TESTNAME = chase-lev-deque + +HEADERS = deque.h +OBJECTS = main.o deque.o + +all: $(TESTNAME) + +$(TESTNAME): $(HEADERS) $(OBJECTS) + $(CXX) -o $@ $(OBJECTS) $(CXXFLAGS) $(LDFLAGS) + +%.o: %.c + $(CXX) -c -o $@ $< $(CXXFLAGS) + +clean: + rm -f $(TESTNAME) *.o diff --git a/cdschecker_modified_benchmarks/chase-lev-deque/deque.cc b/cdschecker_modified_benchmarks/chase-lev-deque/deque.cc new file mode 100644 index 0000000..feb3fc9 --- /dev/null +++ b/cdschecker_modified_benchmarks/chase-lev-deque/deque.cc @@ -0,0 +1,85 @@ +#include "cds_atomic.h" +#include +#include "deque.h" +#include +#include + +Deque * create() { + Deque * q = (Deque *) calloc(1, sizeof(Deque)); + Array * a = (Array *) calloc(1, sizeof(Array)+2*sizeof(atomic_int)); + atomic_store_explicit(&q->array, (uintptr_t)a, memory_order_relaxed); + atomic_store_explicit(&q->top, 0, memory_order_relaxed); + atomic_store_explicit(&q->bottom, 0, memory_order_relaxed); + atomic_store_explicit(&a->size, 2, memory_order_relaxed); + return q; +} + +int take(Deque *q) { + size_t b = atomic_load_explicit(&q->bottom, memory_order_relaxed) - 1; + Array *a = (Array *) atomic_load_explicit(&q->array, memory_order_relaxed); + atomic_store_explicit(&q->bottom, b, memory_order_relaxed); + atomic_thread_fence(memory_order_seq_cst); + size_t t = atomic_load_explicit(&q->top, memory_order_relaxed); + int x; + if (t <= b) { + /* Non-empty queue. */ + x = atomic_load_explicit(&a->buffer[b % atomic_load_explicit(&a->size,memory_order_relaxed)], memory_order_relaxed); + if (t == b) { + /* Single last element in queue. */ + if (!atomic_compare_exchange_strong_explicit(&q->top, &t, t + 1, memory_order_seq_cst, memory_order_relaxed)) + /* Failed race. */ + x = EMPTY; + atomic_store_explicit(&q->bottom, b + 1, memory_order_relaxed); + } + } else { /* Empty queue. */ + x = EMPTY; + atomic_store_explicit(&q->bottom, b + 1, memory_order_relaxed); + } + return x; +} + +void resize(Deque *q) { + Array *a = (Array *) atomic_load_explicit(&q->array, memory_order_relaxed); + size_t size=atomic_load_explicit(&a->size, memory_order_relaxed); + size_t new_size=size << 1; + Array *new_a = (Array *) calloc(1, new_size * sizeof(atomic_int) + sizeof(Array)); + size_t top=atomic_load_explicit(&q->top, memory_order_relaxed); + size_t bottom=atomic_load_explicit(&q->bottom, memory_order_relaxed); + atomic_store_explicit(&new_a->size, new_size, memory_order_relaxed); + size_t i; + for(i=top; i < bottom; i++) { + atomic_store_explicit(&new_a->buffer[i % new_size], atomic_load_explicit(&a->buffer[i % size], memory_order_relaxed), memory_order_relaxed); + } + atomic_store_explicit(&q->array, (uintptr_t)new_a, memory_order_relaxed); + printf("resize\n"); +} + +void push(Deque *q, int x) { + size_t b = atomic_load_explicit(&q->bottom, memory_order_relaxed); + size_t t = atomic_load_explicit(&q->top, memory_order_acquire); + Array *a = (Array *) atomic_load_explicit(&q->array, memory_order_relaxed); + if (b - t > atomic_load_explicit(&a->size, memory_order_relaxed) - 1) /* Full queue. */ { + resize(q); + //Bug in paper...should have next line... + a = (Array *) atomic_load_explicit(&q->array, memory_order_relaxed); + } + atomic_store_explicit(&a->buffer[b % atomic_load_explicit(&a->size, memory_order_relaxed)], x, memory_order_relaxed); + atomic_thread_fence(memory_order_release); + atomic_store_explicit(&q->bottom, b + 1, memory_order_relaxed); +} + +int steal(Deque *q) { + size_t t = atomic_load_explicit(&q->top, memory_order_acquire); + atomic_thread_fence(memory_order_seq_cst); + size_t b = atomic_load_explicit(&q->bottom, memory_order_acquire); + int x = EMPTY; + if (t < b) { + /* Non-empty queue. */ + Array *a = (Array *) atomic_load_explicit(&q->array, memory_order_relaxed); + x = atomic_load_explicit(&a->buffer[t % atomic_load_explicit(&a->size, memory_order_relaxed)], memory_order_relaxed); + if (!atomic_compare_exchange_strong_explicit(&q->top, &t, t + 1, memory_order_seq_cst, memory_order_relaxed)) + /* Failed race. */ + return ABORT; + } + return x; +} diff --git a/cdschecker_modified_benchmarks/chase-lev-deque/deque.h b/cdschecker_modified_benchmarks/chase-lev-deque/deque.h new file mode 100644 index 0000000..c65da6f --- /dev/null +++ b/cdschecker_modified_benchmarks/chase-lev-deque/deque.h @@ -0,0 +1,25 @@ +#ifndef DEQUE_H +#define DEQUE_H + +#include "cds_atomic.h" + +typedef struct { + atomic_size_t size; + atomic_int buffer[]; +} Array; + +typedef struct { + atomic_size_t top, bottom; + atomic_uintptr_t array; /* Atomic(Array *) */ +} Deque; + +Deque * create(); +int take(Deque *q); +int steal(Deque *q); +void resize(Deque *q); +void push(Deque *q, int x); + +#define EMPTY 0xffffffff +#define ABORT 0xfffffffe + +#endif diff --git a/cdschecker_modified_benchmarks/chase-lev-deque/main.cc b/cdschecker_modified_benchmarks/chase-lev-deque/main.cc new file mode 100644 index 0000000..706d812 --- /dev/null +++ b/cdschecker_modified_benchmarks/chase-lev-deque/main.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include "cds_threads.h" +//#include + +#include "model-assert.h" + +#include "deque.h" + +#define ITERATION 1 + +Deque *q; +int a; +int b; +int c; + +static void task(void * param) { + for (int i = 0; i < ITERATION; i++) + a=steal(q); +} + +int user_main(int argc, char **argv) +{ + q=create(); + std::thread t(task, (void *)0); + + for (int i = 0; i < ITERATION; i++) { + push(q, 1); + push(q, 2); + push(q, 4); + b=take(q); + c=take(q); + } + + t.join(); + + bool correct=true; + if (a!=1 && a!=2 && a!=4 && a!= EMPTY) + correct=false; + if (b!=1 && b!=2 && b!=4 && b!= EMPTY) + correct=false; + if (c!=1 && c!=2 && c!=4 && a!= EMPTY) + correct=false; + if (a!=EMPTY && b!=EMPTY && c!=EMPTY && (a+b+c)!=7) + correct=false; + if (!correct) + printf("a=%d b=%d c=%d\n",a,b,c); + MODEL_ASSERT(correct); + + return 0; +} diff --git a/cdschecker_modified_benchmarks/dekker-fences/Makefile b/cdschecker_modified_benchmarks/dekker-fences/Makefile new file mode 100644 index 0000000..ffbb77a --- /dev/null +++ b/cdschecker_modified_benchmarks/dekker-fences/Makefile @@ -0,0 +1,11 @@ +include ../benchmarks.mk + +TESTNAME = dekker-fences + +all: $(TESTNAME) + +$(TESTNAME): $(TESTNAME).cc + $(CXX) -o $@ $< $(CXXFLAGS) $(LDFLAGS) + +clean: + rm -f $(TESTNAME) *.o diff --git a/cdschecker_modified_benchmarks/dekker-fences/dekker-fences.cc b/cdschecker_modified_benchmarks/dekker-fences/dekker-fences.cc new file mode 100644 index 0000000..30a3633 --- /dev/null +++ b/cdschecker_modified_benchmarks/dekker-fences/dekker-fences.cc @@ -0,0 +1,95 @@ +/* + * Dekker's critical section algorithm, implemented with fences. + * + * URL: + * http://www.justsoftwaresolutions.co.uk/threading/ + */ + + +#include "cds_threads.h" +//#include "cds_atomic.h" +#include + +#include "librace.h" + +std::atomic flag0, flag1; +std::atomic turn; + +uint32_t var = 0; + +void p0() +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + flag0.store(true,std::memory_order_relaxed); + //std::atomic_thread_fence(std::memory_order_seq_cst); + + while (flag1.load(std::memory_order_relaxed)) + { + if (turn.load(std::memory_order_relaxed) != 0) + { + flag0.store(false,std::memory_order_relaxed); + while (turn.load(std::memory_order_relaxed) != 0) + { + thrd_yield(); + } + flag0.store(true,std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); + } else + thrd_yield(); + } + std::atomic_thread_fence(std::memory_order_acquire); + + // critical section + store_32(&var, 1); + + turn.store(1,std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_release); + flag0.store(false,std::memory_order_relaxed); +} + +void p1() +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + flag1.store(true,std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); + + while (flag0.load(std::memory_order_relaxed)) + { + if (turn.load(std::memory_order_relaxed) != 1) + { + flag1.store(false,std::memory_order_relaxed); + while (turn.load(std::memory_order_relaxed) != 1) + { + thrd_yield(); + } + flag1.store(true,std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); + } else + thrd_yield(); + } + std::atomic_thread_fence(std::memory_order_acquire); + + // critical section + store_32(&var, 2); + + turn.store(0,std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_release); + flag1.store(false,std::memory_order_relaxed); +} + +int user_main(int argc, char **argv) +{ + flag0 = false; + flag1 = false; + turn = 0; + + std::thread a(p0); + std::thread b(p1); + + a.join(); + b.join(); + + return 0; +} diff --git a/cdschecker_modified_benchmarks/include/cds_atomic.h b/cdschecker_modified_benchmarks/include/cds_atomic.h new file mode 100755 index 0000000..aa71b59 --- /dev/null +++ b/cdschecker_modified_benchmarks/include/cds_atomic.h @@ -0,0 +1,52 @@ +#ifndef __STDATOMIC_H__ +#define __STDATOMIC_H__ + +#ifdef __cplusplus + +#include + +using std::atomic_flag; +using std::atomic_bool; +//using std::atomic_address; +using std::atomic_char; +using std::atomic_schar; +using std::atomic_uchar; +using std::atomic_short; +using std::atomic_ushort; +using std::atomic_int; +using std::atomic_uint; +using std::atomic_long; +using std::atomic_ulong; +using std::atomic_llong; +using std::atomic_ullong; +using std::atomic_wchar_t; + +using std::atomic_size_t; +using std::atomic_uintptr_t; + +using std::atomic; +using std::memory_order; +using std::memory_order_relaxed; +using std::memory_order_acquire; +using std::memory_order_release; +using std::memory_order_acq_rel; +using std::memory_order_seq_cst; + +using std::atomic_thread_fence; +using std::atomic_signal_fence; + +using std::atomic_init; + +#define atomic_init(A, V) *A=V + +#define atomic_load_explicit(A, MO) (*A).load(MO) +#define atomic_store_explicit(A, V, MO) (*A).store(V, MO) + +#define atomic_fetch_add_explicit(A, V, MO) (*A).fetch_add(V, MO); +#define atomic_fetch_sub_explicit(A, V, MO) (*A).fetch_sub(V, MO); +#define atomic_compare_exchange_strong_explicit(A, E, V, MO, FMO) \ + (*A).compare_exchange_strong(*E, V, MO, FMO) + +#endif + +#endif // __STDATOMIC_H__ diff --git a/cdschecker_modified_benchmarks/include/cds_threads.h b/cdschecker_modified_benchmarks/include/cds_threads.h new file mode 100755 index 0000000..5b13720 --- /dev/null +++ b/cdschecker_modified_benchmarks/include/cds_threads.h @@ -0,0 +1,38 @@ +// Header to handle CDSChecker bullshit. +// Handles most things, not just threads: +// - Catches C11 threads and forwards to C++11 threads. +// - Catches store_n and load_n and turns them into normal stores/loads. +// - Replaces user_main with just main. + +#ifndef __THREADS_H__ +#define __THREADS_H__ + +#ifdef __cplusplus +#include + +#include +#include +#include + +#define thrd_t std::thread +typedef int (*thrd_start_t)(void *); + +static std::map thrs; + +#define thrd_create(T, F, A) new_thread(#T, F, A) +static void new_thread(const char *t, thrd_start_t f, void *args) { + assert(thrs.emplace(std::string(++t), std::thread(*f, args)).second); +} + +#define thrd_join(T) end_thread(#T) +static void end_thread(const char *t) { + thrs[std::string(t)].join(); +} + +#define thrd_yield() std::this_thread::yield() + +#define user_main main + +#endif + +#endif // __THREADS_H__ diff --git a/cdschecker_modified_benchmarks/include/librace.h b/cdschecker_modified_benchmarks/include/librace.h new file mode 100755 index 0000000..2445139 --- /dev/null +++ b/cdschecker_modified_benchmarks/include/librace.h @@ -0,0 +1,29 @@ +#ifndef __LIBRACE_H__ +#define __LIBRACE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + void store_8(void *addr, uint8_t val) { *((uint8_t *)addr) = val; } + void store_16(void *addr, uint16_t val) { *((uint16_t *)addr) = val; } + void store_32(void *addr, uint32_t val) { *((uint32_t *)addr) = val; } + void store_64(void *addr, uint64_t val) { *((uint64_t *)addr) = val; } + + void store_8_(void *addr); + void store_16_(void *addr); + void store_32_(void *addr); + void store_64_(void *addr); + + uint8_t load_8(const void *addr) { return *((uint8_t *)addr); } + uint16_t load_16(const void *addr) { return *((uint16_t *)addr); } + uint32_t load_32(const void *addr) { return *((uint32_t *)addr); } + uint64_t load_64(const void *addr) { return *((uint64_t *)addr); } + +#ifdef __cplusplus +} +#endif + +#endif // __LIBRACE_H__ diff --git a/cdschecker_modified_benchmarks/include/model-assert.h b/cdschecker_modified_benchmarks/include/model-assert.h new file mode 100755 index 0000000..5cba524 --- /dev/null +++ b/cdschecker_modified_benchmarks/include/model-assert.h @@ -0,0 +1,21 @@ +#ifndef __MODEL_ASSERT_H__ +#define __MODEL_ASSERT_H__ + +#if __cplusplus +extern "C" { +#else +#include +#endif + +#include + +static void model_assert(bool expr, const char *file, int line) { + assert(expr && file && line); +} +#define MODEL_ASSERT(expr) model_assert((expr), __FILE__, __LINE__) + +#if __cplusplus +} +#endif + +#endif /* __MODEL_ASSERT_H__ */ diff --git a/cdschecker_modified_benchmarks/include/unrelacy.h b/cdschecker_modified_benchmarks/include/unrelacy.h new file mode 100755 index 0000000..555ec9b --- /dev/null +++ b/cdschecker_modified_benchmarks/include/unrelacy.h @@ -0,0 +1,96 @@ +#ifndef __UNRELACY_H__ +#define __UNRELACY_H__ + +#include +#include +#include +#include +#include + +#include +#include + +#define $ + +#define ASSERT(expr) MODEL_ASSERT(expr) +#define RL_ASSERT(expr) MODEL_ASSERT(expr) + +#define RL_NEW new +#define RL_DELETE(expr) delete expr + +#define mo_seqcst memory_order_relaxed +#define mo_release memory_order_release +#define mo_acquire memory_order_acquire +#define mo_acq_rel memory_order_acq_rel +#define mo_relaxed memory_order_relaxed + +namespace rl { + + /* This 'useless' struct is declared just so we can use partial template + * specialization in our store and load functions. */ + template + struct useless { + static void store(void *addr, T val); + static T load(const void *addr); + }; + + template + struct useless { + static void store(void *addr, T val) { store_8(addr, (uint8_t)val); } + static T load(const void *addr) { return (T)load_8(addr); } + }; + + template + struct useless { + static void store(void *addr, T val) { store_16(addr, (uint16_t)val); } + static T load(const void *addr) { return (T)load_16(addr); } + }; + + template + struct useless { + static void store(void *addr, T val) { store_32(addr, (uint32_t)val); } + static T load(const void *addr) { return (T)load_32(addr); } + }; + + template + struct useless { + static void store(void *addr, T val) { store_64(addr, (uint64_t)val); } + static T load(const void *addr) { return (T)load_64(addr); } + }; + + template + struct var { + var() { useless::store(&value, 0); } + var(T v) { useless::store(&value, v); } + var(var const& r) { + value = r.value; + } + ~var() { } + + void operator = (T v) { useless::store(&value, v); } + T operator () () { return useless::load(&value); } + void operator += (T v) { + useless::store(&value, + useless::load(&value) + v); + } + bool operator == (const struct var v) const { return useless::load(&value) == useless::load(&v.value); } + + T value; + }; + + class backoff_t + { + public: + typedef int debug_info_param; + void yield(debug_info_param info) { } + void yield() { } + }; + + + typedef backoff_t backoff; + typedef backoff_t linear_backoff; + typedef backoff_t exp_backoff; + +} + +#endif /* __UNRELACY_H__ */ diff --git a/cdschecker_modified_benchmarks/linuxrwlocks/Makefile b/cdschecker_modified_benchmarks/linuxrwlocks/Makefile new file mode 100644 index 0000000..b0c4612 --- /dev/null +++ b/cdschecker_modified_benchmarks/linuxrwlocks/Makefile @@ -0,0 +1,11 @@ +include ../benchmarks.mk + +TESTNAME = linuxrwlocks + +all: $(TESTNAME) + +$(TESTNAME): $(TESTNAME).cc + $(CXX) -o $@ $< $(CXXFLAGS) $(LDFLAGS) + +clean: + rm -f $(TESTNAME) *.o diff --git a/cdschecker_modified_benchmarks/linuxrwlocks/linuxrwlocks.cc b/cdschecker_modified_benchmarks/linuxrwlocks/linuxrwlocks.cc new file mode 100644 index 0000000..73e3a68 --- /dev/null +++ b/cdschecker_modified_benchmarks/linuxrwlocks/linuxrwlocks.cc @@ -0,0 +1,118 @@ +#include +#include "cds_threads.h" +#include "cds_atomic.h" + +#include "librace.h" + +#define RW_LOCK_BIAS 0x00100000 +#define WRITE_LOCK_CMP RW_LOCK_BIAS + +/** Example implementation of linux rw lock along with 2 thread test + * driver... */ + +typedef union { + atomic_int lock; +} rwlock_t; + +static inline int read_can_lock(rwlock_t *lock) +{ + return atomic_load_explicit(&lock->lock, memory_order_relaxed) > 0; +} + +static inline int write_can_lock(rwlock_t *lock) +{ + return atomic_load_explicit(&lock->lock, memory_order_relaxed) == RW_LOCK_BIAS; +} + +static inline void read_lock(rwlock_t *rw) +{ + int priorvalue = atomic_fetch_sub_explicit(&rw->lock, 1, memory_order_acquire); + while (priorvalue <= 0) { + atomic_fetch_add_explicit(&rw->lock, 1, memory_order_relaxed); + while (atomic_load_explicit(&rw->lock, memory_order_relaxed) <= 0) { + thrd_yield(); + } + priorvalue = atomic_fetch_sub_explicit(&rw->lock, 1, memory_order_relaxed); + } +} + +static inline void write_lock(rwlock_t *rw) +{ + int priorvalue = atomic_fetch_sub_explicit(&rw->lock, RW_LOCK_BIAS, memory_order_acquire); + while (priorvalue != RW_LOCK_BIAS) { + atomic_fetch_add_explicit(&rw->lock, RW_LOCK_BIAS, memory_order_relaxed); + while (atomic_load_explicit(&rw->lock, memory_order_relaxed) != RW_LOCK_BIAS) { + thrd_yield(); + } + priorvalue = atomic_fetch_sub_explicit(&rw->lock, RW_LOCK_BIAS, memory_order_relaxed); + } +} + +static inline int read_trylock(rwlock_t *rw) +{ + int priorvalue = atomic_fetch_sub_explicit(&rw->lock, 1, memory_order_acquire); + if (priorvalue > 0) + return 1; + + atomic_fetch_add_explicit(&rw->lock, 1, memory_order_relaxed); + return 0; +} + +static inline int write_trylock(rwlock_t *rw) +{ + int priorvalue = atomic_fetch_sub_explicit(&rw->lock, RW_LOCK_BIAS, memory_order_acquire); + if (priorvalue == RW_LOCK_BIAS) + return 1; + + atomic_fetch_add_explicit(&rw->lock, RW_LOCK_BIAS, memory_order_relaxed); + return 0; +} + +static inline void read_unlock(rwlock_t *rw) +{ + atomic_fetch_add_explicit(&rw->lock, 1, memory_order_release); +} + +static inline void write_unlock(rwlock_t *rw) +{ + atomic_fetch_add_explicit(&rw->lock, RW_LOCK_BIAS, memory_order_release); +} + +rwlock_t mylock; +int shareddata; + +void a() +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + int i, rs; + for(i = 0; i < 2; i++) { + if ((i % 2) == 0) { + read_lock(&mylock); + rs = load_32(&shareddata); + read_unlock(&mylock); + } else { + write_lock(&mylock); + std::this_thread::yield(); + store_32(&shareddata,(unsigned int)i); + write_unlock(&mylock); + } + } +} + +int user_main(int argc, char **argv) +{ + //thrd_t t1, t2; + atomic_init(&mylock.lock, RW_LOCK_BIAS); + + std::thread t1(a); + std::thread t2(a); + //thrd_create(&t1, (thrd_start_t)&a, NULL); + //thrd_create(&t2, (thrd_start_t)&a, NULL); + + t1.join(); + t2.join(); + //thrd_join(t1); + //thrd_join(t2); + + return 0; +} diff --git a/cdschecker_modified_benchmarks/mcs-lock/Makefile b/cdschecker_modified_benchmarks/mcs-lock/Makefile new file mode 100644 index 0000000..5a311b3 --- /dev/null +++ b/cdschecker_modified_benchmarks/mcs-lock/Makefile @@ -0,0 +1,11 @@ +include ../benchmarks.mk + +TESTNAME = mcs-lock + +all: $(TESTNAME) + +$(TESTNAME): $(TESTNAME).cc $(TESTNAME).h + $(CXX) -o $@ $< $(CXXFLAGS) $(LDFLAGS) + +clean: + rm -f $(TESTNAME) *.o diff --git a/cdschecker_modified_benchmarks/mcs-lock/mcs-lock.cc b/cdschecker_modified_benchmarks/mcs-lock/mcs-lock.cc new file mode 100644 index 0000000..4fc4f0f --- /dev/null +++ b/cdschecker_modified_benchmarks/mcs-lock/mcs-lock.cc @@ -0,0 +1,43 @@ +#include +#include "cds_threads.h" + +#include "mcs-lock.h" + +/* For data race instrumentation */ +#include "librace.h" + +struct mcs_mutex *mutex; +static uint32_t shared; + +void threadA(void *arg) +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + mcs_mutex::guard g(mutex); + printf("store: %d\n", 17); + store_32(&shared, 17); + mutex->unlock(&g); + mutex->lock(&g); + printf("load: %u\n", load_32(&shared)); +} + +void threadB(void *arg) +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + mcs_mutex::guard g(mutex); + printf("load: %u\n", load_32(&shared)); + mutex->unlock(&g); + mutex->lock(&g); + printf("store: %d\n", 17); + store_32(&shared, 17); +} + +int user_main(int argc, char **argv) +{ + mutex = new mcs_mutex(); + + std::thread A(threadA, (void *)NULL); + std::thread B(threadB, (void *)NULL); + A.join(); + B.join(); + return 0; +} diff --git a/cdschecker_modified_benchmarks/mcs-lock/mcs-lock.h b/cdschecker_modified_benchmarks/mcs-lock/mcs-lock.h new file mode 100644 index 0000000..d54e19b --- /dev/null +++ b/cdschecker_modified_benchmarks/mcs-lock/mcs-lock.h @@ -0,0 +1,93 @@ +// mcs on stack + +#include +#include + +struct mcs_node { + std::atomic next; + std::atomic gate; + + mcs_node() { + next.store(0); + gate.store(0); + } +}; + +struct mcs_mutex { +public: + // tail is null when lock is not held + std::atomic m_tail; + + mcs_mutex() { + m_tail.store( NULL ); + } + ~mcs_mutex() { + ASSERT( m_tail.load() == NULL ); + } + + class guard { + public: + mcs_mutex * m_t; + mcs_node m_node; // node held on the stack + + guard(mcs_mutex * t) : m_t(t) { t->lock(this); } + ~guard() { m_t->unlock(this); } + }; + + void lock(guard * I) { + mcs_node * me = &(I->m_node); + + // set up my node : + // not published yet so relaxed : + me->next.store(NULL, std::mo_relaxed ); + me->gate.store(1, std::mo_relaxed ); + + // publish my node as the new tail : + mcs_node * pred = m_tail.exchange(me, std::mo_acq_rel); + if ( pred != NULL ) { + // (*1) race here + // unlock of pred can see me in the tail before I fill next + + // publish me to previous lock-holder : + pred->next.store(me, std::mo_release ); + + // (*2) pred not touched any more + + // now this is the spin - + // wait on predecessor setting my flag - + rl::linear_backoff bo; + while ( me->gate.load(std::mo_relaxed) ) { + thrd_yield(); + } + } + } + + void unlock(guard * I) { + mcs_node * me = &(I->m_node); + + mcs_node * next = me->next.load(std::mo_acquire); + if ( next == NULL ) + { + mcs_node * tail_was_me = me; + if ( m_tail.compare_exchange_strong( tail_was_me,NULL,std::mo_acq_rel) ) { + // got null in tail, mutex is unlocked + return; + } + + // (*1) catch the race : + rl::linear_backoff bo; + for(;;) { + next = me->next.load(std::mo_relaxed); + if ( next != NULL ) + break; + thrd_yield(); + } + } + + // (*2) - store to next must be done, + // so no locker can be viewing my node any more + + // let next guy in : + next->gate.store( 0, std::mo_release ); + } +}; diff --git a/cdschecker_modified_benchmarks/mpmc-queue/Makefile b/cdschecker_modified_benchmarks/mpmc-queue/Makefile new file mode 100644 index 0000000..560074f --- /dev/null +++ b/cdschecker_modified_benchmarks/mpmc-queue/Makefile @@ -0,0 +1,24 @@ +include ../benchmarks.mk + +TESTNAME = mpmc-queue +#TESTS = mpmc-queue mpmc-1r2w mpmc-2r1w mpmc-queue-rdwr +#TESTS += mpmc-queue-noinit mpmc-1r2w-noinit mpmc-2r1w-noinit mpmc-rdwr-noinit +TESTS = mpmc-queue + +all: $(TESTS) + +#mpmc-queue: CPPFLAGS += -DCONFIG_MPMC_READERS=2 -DCONFIG_MPMC_WRITERS=2 +mpmc-queue: CXXFLAGS += -DCONFIG_MPMC_READERS=0 -DCONFIG_MPMC_WRITERS=0 -DCONFIG_MPMC_RDWR=2 +mpmc-queue-rdwr: CPPFLAGS += -DCONFIG_MPMC_READERS=0 -DCONFIG_MPMC_WRITERS=0 -DCONFIG_MPMC_RDWR=2 +mpmc-1r2w: CPPFLAGS += -DCONFIG_MPMC_READERS=1 -DCONFIG_MPMC_WRITERS=2 +mpmc-2r1w: CPPFLAGS += -DCONFIG_MPMC_READERS=2 -DCONFIG_MPMC_WRITERS=1 +mpmc-queue-noinit: CPPFLAGS += -DCONFIG_MPMC_READERS=2 -DCONFIG_MPMC_WRITERS=2 -DCONFIG_MPMC_NO_INITIAL_ELEMENT +mpmc-1r2w-noinit: CPPFLAGS += -DCONFIG_MPMC_READERS=1 -DCONFIG_MPMC_WRITERS=2 -DCONFIG_MPMC_NO_INITIAL_ELEMENT +mpmc-2r1w-noinit: CPPFLAGS += -DCONFIG_MPMC_READERS=2 -DCONFIG_MPMC_WRITERS=1 -DCONFIG_MPMC_NO_INITIAL_ELEMENT +mpmc-rdwr-noinit: CPPFLAGS += -DCONFIG_MPMC_READERS=0 -DCONFIG_MPMC_WRITERS=0 -DCONFIG_MPMC_RDWR=2 -DCONFIG_MPMC_NO_INITIAL_ELEMENT + +$(TESTS): $(TESTNAME).cc $(TESTNAME).h + $(CXX) -o $@ $< $(CXXFLAGS) $(LDFLAGS) + +clean: + rm -f $(TESTS) *.o diff --git a/cdschecker_modified_benchmarks/mpmc-queue/mpmc-queue.cc b/cdschecker_modified_benchmarks/mpmc-queue/mpmc-queue.cc new file mode 100644 index 0000000..038fac3 --- /dev/null +++ b/cdschecker_modified_benchmarks/mpmc-queue/mpmc-queue.cc @@ -0,0 +1,143 @@ +#include +#include "cds_threads.h" +#include +#include +#include + +#include + +#include "mpmc-queue.h" + +void threadA(struct mpmc_boundq_1_alt *queue) +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + int32_t *bin = queue->write_prepare(); + store_32(bin, 1); + queue->write_publish(); +} + +void threadB(struct mpmc_boundq_1_alt *queue) +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + int32_t *bin; + while ((bin = queue->read_fetch()) != NULL) { + printf("Read: %d\n", load_32(bin)); + queue->read_consume(); + } +} + +void threadC(struct mpmc_boundq_1_alt *queue) +{ +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); + int32_t *bin = queue->write_prepare(); + store_32(bin, 1); + queue->write_publish(); + + while ((bin = queue->read_fetch()) != NULL) { + printf("Read: %d\n", load_32(bin)); + queue->read_consume(); + } +} + +#define MAXREADERS 3 +#define MAXWRITERS 3 +#define MAXRDWR 3 + +#ifdef CONFIG_MPMC_READERS +#define DEFAULT_READERS (CONFIG_MPMC_READERS) +#else +#define DEFAULT_READERS 2 +#endif + +#ifdef CONFIG_MPMC_WRITERS +#define DEFAULT_WRITERS (CONFIG_MPMC_WRITERS) +#else +#define DEFAULT_WRITERS 2 +#endif + +#ifdef CONFIG_MPMC_RDWR +#define DEFAULT_RDWR (CONFIG_MPMC_RDWR) +#else +#define DEFAULT_RDWR 0 +#endif + +int readers = DEFAULT_READERS, writers = DEFAULT_WRITERS, rdwr = DEFAULT_RDWR; + +void print_usage() +{ + printf("Error: use the following options\n" + " -r Choose number of reader threads\n" + " -w Choose number of writer threads\n"); + exit(EXIT_FAILURE); +} + +void process_params(int argc, char **argv) +{ + const char *shortopts = "hr:w:"; + int opt; + bool error = false; + + while (!error && (opt = getopt(argc, argv, shortopts)) != -1) { + switch (opt) { + case 'h': + print_usage(); + break; + case 'r': + readers = atoi(optarg); + break; + case 'w': + writers = atoi(optarg); + break; + default: /* '?' */ + error = true; + break; + } + } + + if (writers < 1 || writers > MAXWRITERS) + error = true; + if (readers < 1 || readers > MAXREADERS) + error = true; + + if (error) + print_usage(); +} + +int user_main(int argc, char **argv) +{ + struct mpmc_boundq_1_alt queue; + std::thread A[MAXWRITERS], B[MAXREADERS], C[MAXRDWR]; + + /* Note: optarg() / optind is broken in model-checker - workaround is + * to just copy&paste this test a few times */ + //process_params(argc, argv); + printf("%d reader(s), %d writer(s)\n", readers, writers); + +#ifndef CONFIG_MPMC_NO_INITIAL_ELEMENT + printf("Adding initial element\n"); + int32_t *bin = queue.write_prepare(); + store_32(bin, 17); + queue.write_publish(); +#endif + + printf("Start threads\n"); + + for (int i = 0; i < writers; i++) + A[i] = std::thread(threadA, &queue); + for (int i = 0; i < readers; i++) + B[i] = std::thread(threadB, &queue); + + for (int i = 0; i < rdwr; i++) + C[i] = std::thread(threadC, &queue); + + for (int i = 0; i < writers; i++) + A[i].join(); + for (int i = 0; i < readers; i++) + B[i].join(); + for (int i = 0; i < rdwr; i++) + C[i].join(); + + printf("Threads complete\n"); + + return 0; +} diff --git a/cdschecker_modified_benchmarks/mpmc-queue/mpmc-queue.h b/cdschecker_modified_benchmarks/mpmc-queue/mpmc-queue.h new file mode 100644 index 0000000..f86bec4 --- /dev/null +++ b/cdschecker_modified_benchmarks/mpmc-queue/mpmc-queue.h @@ -0,0 +1,97 @@ +#include "cds_atomic.h" +#include + +template +struct mpmc_boundq_1_alt +{ +private: + + // elements should generally be cache-line-size padded : + t_element m_array[t_size]; + + // rdwr counts the reads & writes that have started + atomic m_rdwr; + // "read" and "written" count the number completed + atomic m_read; + atomic m_written; + +public: + + mpmc_boundq_1_alt() + { + m_rdwr = 0; + m_read = 0; + m_written = 0; + } + + //----------------------------------------------------- + + t_element * read_fetch() { + unsigned int rdwr = m_rdwr.load(mo_acquire); + unsigned int rd,wr; + for(;;) { + rd = (rdwr>>16) & 0xFFFF; + wr = rdwr & 0xFFFF; + + if ( wr == rd ) // empty + return NULL; + + if ( m_rdwr.compare_exchange_weak(rdwr,rdwr+(1<<16),mo_acquire) ) + break; + else + thrd_yield(); + } + + // (*1) + rl::backoff bo; + while ( (m_written.load(mo_acquire) & 0xFFFF) != wr ) { + thrd_yield(); + } + + t_element * p = & ( m_array[ rd % t_size ] ); + + return p; + } + + void read_consume() { + m_read.fetch_add(1,mo_release); + } + + //----------------------------------------------------- + + t_element * write_prepare() { + unsigned int rdwr = m_rdwr.load(mo_acquire); + unsigned int rd,wr; + for(;;) { + rd = (rdwr>>16) & 0xFFFF; + wr = rdwr & 0xFFFF; + + if ( wr == ((rd + t_size)&0xFFFF) ) // full + return NULL; + + if ( m_rdwr.compare_exchange_weak(rdwr,(rd<<16) | ((wr+1)&0xFFFF),mo_acq_rel) ) + break; + else + thrd_yield(); + } + + // (*1) + rl::backoff bo; + while ( (m_read.load(mo_acquire) & 0xFFFF) != rd ) { + thrd_yield(); + } + + t_element * p = & ( m_array[ wr % t_size ] ); + + return p; + } + + void write_publish() + { + m_written.fetch_add(1,mo_relaxed); + } + + //----------------------------------------------------- + + +}; diff --git a/cdschecker_modified_benchmarks/ms-queue-tsan11/Makefile b/cdschecker_modified_benchmarks/ms-queue-tsan11/Makefile new file mode 100755 index 0000000..151ad71 --- /dev/null +++ b/cdschecker_modified_benchmarks/ms-queue-tsan11/Makefile @@ -0,0 +1,17 @@ +include ../benchmarks.mk + +TESTNAME = ms-queue + +HEADERS = my_queue.h +OBJECTS = main.o my_queue.o + +all: $(TESTNAME) + +$(TESTNAME): $(HEADERS) $(OBJECTS) + $(CXX) -o $@ $(OBJECTS) $(CXXFLAGS) $(LDFLAGS) + +%.o: %.c + $(CXX) -c -o $@ $< $(CXXFLAGS) + +clean: + rm -f $(TESTNAME) *.o diff --git a/cdschecker_modified_benchmarks/ms-queue-tsan11/main.cc b/cdschecker_modified_benchmarks/ms-queue-tsan11/main.cc new file mode 100755 index 0000000..e4bb181 --- /dev/null +++ b/cdschecker_modified_benchmarks/ms-queue-tsan11/main.cc @@ -0,0 +1,87 @@ +#include +#include +#include "cds_threads.h" + +#include "my_queue.h" +#include "model-assert.h" + +static int procs = 4; +static queue_t *queue; +static thrd_t *threads; +static unsigned int *input; +static unsigned int *output; +static int num_threads; + +int get_thread_num() +{ + //thrd_t curr = thrd_current(); + int i; + printf("thread id: %d\n", std::this_thread::get_id()); + for (i = 0; i < num_threads; i++) + if (std::this_thread::get_id() == threads[i].get_id()) + return i; + MODEL_ASSERT(0); + return -1; +} + +bool succ1, succ2; + +static void main_task(void *param) +{ + unsigned int val; + int pid = *((int *)param); + if (!pid) { + input[0] = 17; + enqueue(queue, input[0]); + succ1 = dequeue(queue, &output[0]); + //printf("Dequeue: %d\n", output[0]); + } else { + input[1] = 37; + enqueue(queue, input[1]); + succ2 = dequeue(queue, &output[1]); + } +} + +int user_main(int argc, char **argv) +{ + int i; + int *param; + unsigned int in_sum = 0, out_sum = 0; + + queue = (queue_t *)calloc(1, sizeof(*queue)); + MODEL_ASSERT(queue); + + num_threads = procs; + threads = (std::thread *)malloc(num_threads * sizeof(std::thread)); + param = (int *)malloc(num_threads * sizeof(*param)); + input = (unsigned *)calloc(num_threads, sizeof(*input)); + output = (unsigned *)calloc(num_threads, sizeof(*output)); + + init_queue(queue, num_threads); + for (i = 0; i < num_threads; i++) { + param[i] = i; + //threads[i] = std::thread(main_task, ¶m[i]); + new (&threads[i])std::thread(main_task, ¶m[i]); + } + for (i = 0; i < num_threads; i++) + threads[i].join(); + + for (i = 0; i < num_threads; i++) { + in_sum += input[i]; + out_sum += output[i]; + } + for (i = 0; i < num_threads; i++) + printf("input[%d] = %u\n", i, input[i]); + for (i = 0; i < num_threads; i++) + printf("output[%d] = %u\n", i, output[i]); + if (succ1 && succ2) + MODEL_ASSERT(in_sum == out_sum); + else + MODEL_ASSERT (false); + + free(param); + free(threads); + free(queue); + + return 0; +} diff --git a/cdschecker_modified_benchmarks/ms-queue-tsan11/main.o b/cdschecker_modified_benchmarks/ms-queue-tsan11/main.o new file mode 100644 index 0000000000000000000000000000000000000000..e340deb1b402cb864dd2bef8be5a4329d4f69cdf GIT binary patch literal 289240 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 literal 0 HcmV?d00001 diff --git a/cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.cc b/cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.cc new file mode 100755 index 0000000..be84c7d --- /dev/null +++ b/cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.cc @@ -0,0 +1,161 @@ +#include "cds_threads.h" +#include +#include "librace.h" +#include "model-assert.h" + +#include "my_queue.h" + +#define relaxed memory_order_relaxed +#define release memory_order_release +#define acquire memory_order_acquire + +#define MAX_FREELIST 4 /* Each thread can own up to MAX_FREELIST free nodes */ +#define INITIAL_FREE 2 /* Each thread starts with INITIAL_FREE free nodes */ + +#define POISON_IDX 0x666 + +static unsigned int (*free_lists)[MAX_FREELIST]; + +/* Search this thread's free list for a "new" node */ +static unsigned int new_node() +{ + int i; + int t = get_thread_num(); + for (i = 0; i < MAX_FREELIST; i++) { + unsigned int node = load_32(&free_lists[t][i]); + if (node) { + store_32(&free_lists[t][i], 0); + return node; + } + } + /* free_list is empty? */ + MODEL_ASSERT(0); + return 0; +} + +/* Place this node index back on this thread's free list */ +static void reclaim(unsigned int node) +{ + int i; + int t = get_thread_num(); + + /* Don't reclaim NULL node */ + MODEL_ASSERT(node); + + for (i = 0; i < MAX_FREELIST; i++) { + /* Should never race with our own thread here */ + unsigned int idx = load_32(&free_lists[t][i]); + + /* Found empty spot in free list */ + if (idx == 0) { + store_32(&free_lists[t][i], node); + return; + } + } + /* free list is full? */ + MODEL_ASSERT(0); +} + +void init_queue(queue_t *q, int num_threads) +{ + int i, j; + + /* Initialize each thread's free list with INITIAL_FREE pointers */ + /* The actual nodes are initialized with poison indexes */ + free_lists = (unsigned (*)[MAX_FREELIST])malloc(num_threads * sizeof(*free_lists)); + for (i = 0; i < num_threads; i++) { + for (j = 0; j < INITIAL_FREE; j++) { + free_lists[i][j] = 2 + i * MAX_FREELIST + j; + atomic_init(&q->nodes[free_lists[i][j]].next, MAKE_POINTER(POISON_IDX, 0)); + } + } + + /* initialize queue */ + atomic_init(&q->head, MAKE_POINTER(1, 0)); + atomic_init(&q->tail, MAKE_POINTER(1, 0)); + atomic_init(&q->nodes[1].next, MAKE_POINTER(0, 0)); +} + +void enqueue(queue_t *q, unsigned int val) +{ + int success = 0; + unsigned int node; + pointer tail; + pointer next; + pointer tmp; + + node = new_node(); + store_32(&q->nodes[node].value, val); + tmp = atomic_load_explicit(&q->nodes[node].next, relaxed); + set_ptr(&tmp, 0); // NULL + atomic_store_explicit(&q->nodes[node].next, tmp, relaxed); + + while (!success) { + tail = atomic_load_explicit(&q->tail, acquire); + next = atomic_load_explicit(&q->nodes[get_ptr(tail)].next, acquire); + if (tail == atomic_load_explicit(&q->tail, relaxed)) { + + /* Check for uninitialized 'next' */ + MODEL_ASSERT(get_ptr(next) != POISON_IDX); + + if (get_ptr(next) == 0) { // == NULL + pointer value = MAKE_POINTER(node, get_count(next) + 1); + success = atomic_compare_exchange_strong_explicit(&q->nodes[get_ptr(tail)].next, + &next, value, release, release); + } + if (!success) { + unsigned int ptr = get_ptr(atomic_load_explicit(&q->nodes[get_ptr(tail)].next, acquire)); + pointer value = MAKE_POINTER(ptr, + get_count(tail) + 1); + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, value, + release, release); + thrd_yield(); + } + } + } + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, + MAKE_POINTER(node, get_count(tail) + 1), + release, release); +} + +bool dequeue(queue_t *q, unsigned int *retVal) +{ + int success = 0; + pointer head; + pointer tail; + pointer next; + + while (!success) { + head = atomic_load_explicit(&q->head, acquire); + tail = atomic_load_explicit(&q->tail, relaxed); + next = atomic_load_explicit(&q->nodes[get_ptr(head)].next, acquire); + if (atomic_load_explicit(&q->head, relaxed) == head) { + if (get_ptr(head) == get_ptr(tail)) { + + /* Check for uninitialized 'next' */ + MODEL_ASSERT(get_ptr(next) != POISON_IDX); + + if (get_ptr(next) == 0) { // NULL + return false; // NULL + } + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, + MAKE_POINTER(get_ptr(next), get_count(tail) + 1), + release, release); + thrd_yield(); + } else { + *retVal = load_32(&q->nodes[get_ptr(next)].value); + success = atomic_compare_exchange_strong_explicit(&q->head, + &head, + MAKE_POINTER(get_ptr(next), get_count(head) + 1), + release, release); + if (!success) + thrd_yield(); + } + } + } + reclaim(get_ptr(head)); + return true; +} diff --git a/cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.h b/cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.h new file mode 100755 index 0000000..45849a4 --- /dev/null +++ b/cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.h @@ -0,0 +1,31 @@ +#include "cds_atomic.h" + +#define MAX_NODES 0xf + +typedef unsigned long long pointer; +typedef atomic_ullong pointer_t; + +#define MAKE_POINTER(ptr, count) ((((pointer)count) << 32) | ptr) +#define PTR_MASK 0xffffffffLL +#define COUNT_MASK (0xffffffffLL << 32) + +static inline void set_count(pointer *p, unsigned int val) { *p = (*p & ~COUNT_MASK) | ((pointer)val << 32); } +static inline void set_ptr(pointer *p, unsigned int val) { *p = (*p & ~PTR_MASK) | val; } +static inline unsigned int get_count(pointer p) { return (p & COUNT_MASK) >> 32; } +static inline unsigned int get_ptr(pointer p) { return p & PTR_MASK; } + +typedef struct node { + unsigned int value; + pointer_t next; +} node_t; + +typedef struct { + pointer_t head; + pointer_t tail; + node_t nodes[MAX_NODES + 1]; +} queue_t; + +void init_queue(queue_t *q, int num_threads); +void enqueue(queue_t *q, unsigned int val); +bool dequeue(queue_t *q, unsigned int *retVal); +int get_thread_num(); diff --git a/cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.o b/cdschecker_modified_benchmarks/ms-queue-tsan11/my_queue.o new file mode 100644 index 0000000000000000000000000000000000000000..306682fad916e48ae0fdb4c072c55692f5589c2b GIT binary patch literal 285376 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 +#include +#include +//#include "cds_threads.h" + +#include "my_queue.h" +#include "model-assert.h" + +#define user_main main + +static int procs = 4; +static queue_t *queue; +static thrd_t *threads; +static unsigned int *input; +static unsigned int *output; +static int num_threads; + +int get_thread_num() +{ + thrd_t curr = thrd_current(); + int i; + for (i = 0; i < num_threads; i++) + if (curr.priv == threads[i].priv) + return i; + MODEL_ASSERT(0); + return -1; +} + +bool succ1, succ2; + +static void main_task(void *param) +{ + int pid = *((int *)param); + if (!pid) { + input[0] = 17; + enqueue(queue, input[0]); + succ1 = dequeue(queue, &output[0]); + //printf("Dequeue: %d\n", output[0]); + } else { + input[1] = 37; + enqueue(queue, input[1]); + succ2 = dequeue(queue, &output[1]); + } +} + +int user_main(int argc, char **argv) +{ + int i; + int *param; + unsigned int in_sum = 0, out_sum = 0; + + queue = (queue_t *)calloc(1, sizeof(*queue)); + MODEL_ASSERT(queue); + + num_threads = procs; + threads = (thrd_t *)malloc(num_threads * sizeof(thrd_t)); + param = (int *)malloc(num_threads * sizeof(*param)); + input = (unsigned *)calloc(num_threads, sizeof(*input)); + output = (unsigned *)calloc(num_threads, sizeof(*output)); + + init_queue(queue, num_threads); + for (i = 0; i < num_threads; i++) { + param[i] = i; + thrd_create(&threads[i], main_task, ¶m[i]); + } + for (i = 0; i < num_threads; i++) + thrd_join(threads[i]); + + for (i = 0; i < num_threads; i++) { + in_sum += input[i]; + out_sum += output[i]; + } + for (i = 0; i < num_threads; i++) + printf("input[%d] = %u\n", i, input[i]); + for (i = 0; i < num_threads; i++) + printf("output[%d] = %u\n", i, output[i]); +/* + if (succ1 && succ2) + MODEL_ASSERT(in_sum == out_sum); + else + MODEL_ASSERT(false); +*/ + free(param); + free(threads); + free(queue); + + return 0; +} diff --git a/cdschecker_modified_benchmarks/ms-queue/my_queue.c.correct b/cdschecker_modified_benchmarks/ms-queue/my_queue.c.correct new file mode 100644 index 0000000..b868d2e --- /dev/null +++ b/cdschecker_modified_benchmarks/ms-queue/my_queue.c.correct @@ -0,0 +1,260 @@ +#include +#include +#include "librace.h" +#include "model-assert.h" + +#include "queue.h" + +#define relaxed memory_order_relaxed +#define release memory_order_release +#define acquire memory_order_acquire + +#define MAX_FREELIST 4 /* Each thread can own up to MAX_FREELIST free nodes */ +#define INITIAL_FREE 2 /* Each thread starts with INITIAL_FREE free nodes */ + +#define POISON_IDX 0x666 + +static unsigned int (*free_lists)[MAX_FREELIST]; + +/* Search this thread's free list for a "new" node */ +static unsigned int new_node() +{ + int i; + int t = get_thread_num(); + for (i = 0; i < MAX_FREELIST; i++) { + //unsigned int node = load_32(&free_lists[t][i]); + unsigned int node = free_lists[t][i]; + if (node) { + //store_32(&free_lists[t][i], 0); + free_lists[t][i] = 0; + return node; + } + } + /* free_list is empty? */ + MODEL_ASSERT(0); + return 0; +} + +/* Simulate the fact that when a node got recycled, it will get assigned to the + * same queue or for other usage */ +void simulateRecycledNodeUpdate(queue_t *q, unsigned int node) { + atomic_store_explicit(&q->nodes[node].next, -1, memory_order_release); +} + + +/* Place this node index back on this thread's free list */ +static void reclaim(unsigned int node) +{ + int i; + int t = get_thread_num(); + + /* Don't reclaim NULL node */ + //MODEL_ASSERT(node); + + for (i = 0; i < MAX_FREELIST; i++) { + /* Should never race with our own thread here */ + //unsigned int idx = load_32(&free_lists[t][i]); + unsigned int idx = free_lists[t][i]; + + /* Found empty spot in free list */ + if (idx == 0) { + //store_32(&free_lists[t][i], node); + free_lists[t][i] = node; + return; + } + } + /* free list is full? */ + //MODEL_ASSERT(0); +} + +void init_queue(queue_t *q, int num_threads) +{ + int i, j; + + /* Initialize each thread's free list with INITIAL_FREE pointers */ + /* The actual nodes are initialized with poison indexes */ + free_lists = ( unsigned int (*)[MAX_FREELIST] ) malloc(num_threads * sizeof(*free_lists)); + for (i = 0; i < num_threads; i++) { + for (j = 0; j < INITIAL_FREE; j++) { + free_lists[i][j] = 2 + i * MAX_FREELIST + j; + atomic_init(&q->nodes[free_lists[i][j]].next, MAKE_POINTER(POISON_IDX, 0)); + } + } + + /* initialize queue */ + atomic_init(&q->head, MAKE_POINTER(1, 0)); + atomic_init(&q->tail, MAKE_POINTER(1, 0)); + atomic_init(&q->nodes[1].next, MAKE_POINTER(0, 0)); +} + +/** @DeclareState: IntList *q; +@Initial: q = new IntList; +@Print: + model_print("\tSTATE(q): "); + printContainer(q); + model_print("\n"); */ + +/** @Transition: STATE(q)->push_back(val); +@Print: model_print("\tENQ #%d: val=%d\n", ID, val); */ +void enqueue(queue_t *q, unsigned int val, int n) +{ + int success = 0; + unsigned int node; + pointer tail; + pointer next; + pointer tmp; + + node = new_node(); + //store_32(&q->nodes[node].value, val); + q->nodes[node].value = val; + tmp = atomic_load_explicit(&q->nodes[node].next, relaxed); + set_ptr(&tmp, 0); // NULL + // XXX-known-bug-#1: This is a found bug in AutoMO, and testcase4 can reveal + // this known bug. + // To reproduce, weaken the parameter "memory_order_release" to + // "memory_order_relaxed", run "make" to recompile, and then run: + // "./run.sh ./ms-queue/testcase4 -m2 -y -u3 -tSPEC" + /********** Detected KNOWN BUG (testcase4) **********/ + atomic_store_explicit(&q->nodes[node].next, tmp, release); + + while (!success) { + // XXX-injection-#1: To reproduce, weaken the parameter + // "memory_order_acquire" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase2 -m2 -y -u3 -tSPEC" + /********** Detected UL (testcase2) **********/ + tail = atomic_load_explicit(&q->tail, acquire); + // XXX-injection-#2: To reproduce, weaken the parameter + // "memory_order_acquire" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase4 -m2 -y -u3 -tSPEC" + /********** Detected Correctness (testcase4) **********/ + next = atomic_load_explicit(&q->nodes[get_ptr(tail)].next, acquire); + if (tail == atomic_load_explicit(&q->tail, relaxed)) { + + /* Check for uninitialized 'next' */ + //MODEL_ASSERT(get_ptr(next) != POISON_IDX); + + if (get_ptr(next) == 0) { // == NULL + pointer value = MAKE_POINTER(node, get_count(next) + 1); + // XXX-injection-#3: To reproduce, weaken the parameter + // "memory_order_release" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase1 -m2 -y -u3 -tSPEC" + /********** Detected Correctness (testcase1) **********/ + success = atomic_compare_exchange_strong_explicit(&q->nodes[get_ptr(tail)].next, + &next, value, release, release); + /** @OPClearDefine: success */ + } + if (!success) { + // XXX-injection-#4: To reproduce, weaken the parameter + // "memory_order_acquire" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase2 -m2 -y -u3 -tSPEC" + /********** Detected UL (testcase2) **********/ + unsigned int ptr = get_ptr(atomic_load_explicit(&q->nodes[get_ptr(tail)].next, acquire)); + pointer value = MAKE_POINTER(ptr, + get_count(tail) + 1); + // XXX-injection-#5: To reproduce, weaken the parameter + // "memory_order_release" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase2 -m2 -y -u3 -tSPEC" + /********** Detected Correctness (testcase2) **********/ + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, value, + release, release); + thrd_yield(); + } + } + } + + // XXX-injection-#6: To reproduce, weaken the parameter + // "memory_order_release" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase1 -m2 -y -u3 -tSPEC" + /********** Detected Correctness (testcase1) **********/ + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, + MAKE_POINTER(node, get_count(tail) + 1), + release, release); +} + +/** @Transition: S_RET = STATE(q)->empty() ? 0 : STATE(q)->front(); +if (S_RET && C_RET) STATE(q)->pop_front(); +@JustifyingPostcondition: if (!C_RET) + return S_RET == C_RET; +@PostCondition: return C_RET ? *retVal == S_RET : true; +@Print: model_print("\tDEQ #%d: C_RET=%d && *retVal=%d && S_RET=%d\n", ID, + C_RET, *retVal, S_RET); +*/ +int dequeue(queue_t *q, unsigned int *retVal, unsigned int *reclaimNode) +{ + int success = 0; + pointer head; + pointer tail; + pointer next; + + while (!success) { + // XXX-injection-#7: To reproduce, weaken the parameter + // "memory_order_acquire" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase3 -m2 -y -u3 -tSPEC" + /********** Detected Correctness (testcase3) **********/ + head = atomic_load_explicit(&q->head, acquire); + // To reproduce, weaken the parameter "memory_order_acquire" to + // "memory_order_relaxed", run "make" to recompile, and then run: + // "./run.sh ./ms-queue/testcase4 -m2 -y -u3 -tSPEC" + // XXX-known-bug-#2: This is another known bug, and testcase2 can reveal + // this known bug + /********** Detected KNOWN BUG (testcase2) **********/ + tail = atomic_load_explicit(&q->tail, acquire); + + // XXX-injection-#8: To reproduce, weaken the parameter + // "memory_order_acquire" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase1 -m2 -y -u3 -tSPEC" + /********** Detected Correctness (testcase1) **********/ + next = atomic_load_explicit(&q->nodes[get_ptr(head)].next, acquire); + /** @OPClearDefine: true */ + if (atomic_load_explicit(&q->head, relaxed) == head) { + if (get_ptr(head) == get_ptr(tail)) { + + /* Check for uninitialized 'next' */ + MODEL_ASSERT(get_ptr(next) != POISON_IDX); + + if (get_ptr(next) == 0) { // NULL + return false; // NULL + } + + // XXX-injection-#9: To reproduce, weaken the parameter + // "memory_order_release" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase2 -m2 -y -u3 -tSPEC" + /********** Detected UL (testcase2) **********/ + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, + MAKE_POINTER(get_ptr(next), get_count(tail) + 1), + release, release); + thrd_yield(); + } else { + //*retVal = load_32(&q->nodes[get_ptr(next)].value); + *retVal = q->nodes[get_ptr(next)].value; + + // XXX-injection-#10: To reproduce, weaken the parameter + // "memory_order_release" to "memory_order_relaxed", run "make" to + // recompile, and then run: + // "./run.sh ./ms-queue/testcase3 -m2 -y -u3 -tSPEC" + /********** Detected Correctness (testcase3) **********/ + success = atomic_compare_exchange_strong_explicit(&q->head, + &head, + MAKE_POINTER(get_ptr(next), get_count(head) + 1), + release, release); + if (!success) + thrd_yield(); + } + } + } + *reclaimNode = get_ptr(head); + reclaim(get_ptr(head)); + return true; +} diff --git a/cdschecker_modified_benchmarks/ms-queue/my_queue.cc b/cdschecker_modified_benchmarks/ms-queue/my_queue.cc new file mode 100644 index 0000000..d746bda --- /dev/null +++ b/cdschecker_modified_benchmarks/ms-queue/my_queue.cc @@ -0,0 +1,167 @@ +#include +#include +#include "librace.h" +#include "model-assert.h" + +#include "my_queue.h" + +#define relaxed memory_order_relaxed +#define release memory_order_release +#define acquire memory_order_acquire + +#define MAX_FREELIST 4 /* Each thread can own up to MAX_FREELIST free nodes */ +#define INITIAL_FREE 2 /* Each thread starts with INITIAL_FREE free nodes */ + +#define POISON_IDX 0x666 + +static unsigned int (*free_lists)[MAX_FREELIST]; + +/* Search this thread's free list for a "new" node */ +static unsigned int new_node() +{ + int i; + int t = get_thread_num(); + for (i = 0; i < MAX_FREELIST; i++) { + unsigned int node = load_32(&free_lists[t][i]); + if (node) { + store_32(&free_lists[t][i], 0); + return node; + } + } + /* free_list is empty? */ + MODEL_ASSERT(0); + return 0; +} + +/* Place this node index back on this thread's free list */ +static void reclaim(unsigned int node) +{ + int i; + int t = get_thread_num(); + + /* Don't reclaim NULL node */ + MODEL_ASSERT(node); + + for (i = 0; i < MAX_FREELIST; i++) { + /* Should never race with our own thread here */ + unsigned int idx = load_32(&free_lists[t][i]); + + /* Found empty spot in free list */ + if (idx == 0) { + store_32(&free_lists[t][i], node); + return; + } + } + /* free list is full? */ + MODEL_ASSERT(0); +} + +void init_queue(queue_t *q, int num_threads) +{ + int i, j; + + /* Initialize each thread's free list with INITIAL_FREE pointers */ + /* The actual nodes are initialized with poison indexes */ + free_lists = (unsigned (*)[MAX_FREELIST])malloc(num_threads * sizeof(*free_lists)); + for (i = 0; i < num_threads; i++) { + for (j = 0; j < INITIAL_FREE; j++) { + free_lists[i][j] = 2 + i * MAX_FREELIST + j; + atomic_init(&q->nodes[free_lists[i][j]].next, MAKE_POINTER(POISON_IDX, 0)); + } + } + + /* initialize queue */ + atomic_init(&q->head, MAKE_POINTER(1, 0)); + atomic_init(&q->tail, MAKE_POINTER(1, 0)); + atomic_init(&q->nodes[1].next, MAKE_POINTER(0, 0)); +} + +void enqueue(queue_t *q, unsigned int val) +{ + int success = 0; + unsigned int node; + pointer tail; + pointer next; + pointer tmp; + + node = new_node(); + store_32(&q->nodes[node].value, val); + tmp = atomic_load_explicit(&q->nodes[node].next, relaxed); + set_ptr(&tmp, 0); // NULL + + // Bug 1 + atomic_store_explicit(&q->nodes[node].next, tmp, relaxed); + //atomic_store_explicit(&q->nodes[node].next, tmp, release); + + while (!success) { + tail = atomic_load_explicit(&q->tail, acquire); + next = atomic_load_explicit(&q->nodes[get_ptr(tail)].next, acquire); + if (tail == atomic_load_explicit(&q->tail, relaxed)) { + /* Check for uninitialized 'next' */ + MODEL_ASSERT(get_ptr(next) != POISON_IDX); + + if (get_ptr(next) == 0) { // == NULL + pointer value = MAKE_POINTER(node, get_count(next) + 1); + success = atomic_compare_exchange_strong_explicit(&q->nodes[get_ptr(tail)].next, + &next, value, release, release); + } + if (!success) { + unsigned int ptr = get_ptr(atomic_load_explicit(&q->nodes[get_ptr(tail)].next, acquire)); + pointer value = MAKE_POINTER(ptr, + get_count(tail) + 1); + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, value, + release, release); + thrd_yield(); + } + } + } + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, + MAKE_POINTER(node, get_count(tail) + 1), + release, release); +} + +bool dequeue(queue_t *q, unsigned int *retVal) +{ + int success = 0; + pointer head; + pointer tail; + pointer next; + + while (!success) { + head = atomic_load_explicit(&q->head, acquire); + + // Bug 2 + tail = atomic_load_explicit(&q->tail, relaxed); + //tail = atomic_load_explicit(&q->tail, acquire); + + next = atomic_load_explicit(&q->nodes[get_ptr(head)].next, acquire); + if (atomic_load_explicit(&q->head, relaxed) == head) { + if (get_ptr(head) == get_ptr(tail)) { + + /* Check for uninitialized 'next' */ + MODEL_ASSERT(get_ptr(next) != POISON_IDX); + + if (get_ptr(next) == 0) { // NULL + return false; // NULL + } + atomic_compare_exchange_strong_explicit(&q->tail, + &tail, + MAKE_POINTER(get_ptr(next), get_count(tail) + 1), + release, release); + thrd_yield(); + } else { + *retVal = load_32(&q->nodes[get_ptr(next)].value); + success = atomic_compare_exchange_strong_explicit(&q->head, + &head, + MAKE_POINTER(get_ptr(next), get_count(head) + 1), + release, release); + if (!success) + thrd_yield(); + } + } + } + reclaim(get_ptr(head)); + return true; +} diff --git a/cdschecker_modified_benchmarks/ms-queue/my_queue.h b/cdschecker_modified_benchmarks/ms-queue/my_queue.h new file mode 100644 index 0000000..8bb8d58 --- /dev/null +++ b/cdschecker_modified_benchmarks/ms-queue/my_queue.h @@ -0,0 +1,32 @@ +#include +#include + +#define MAX_NODES 0xf + +typedef unsigned long long pointer; +typedef atomic_ullong pointer_t; + +#define MAKE_POINTER(ptr, count) ((((pointer)count) << 32) | ptr) +#define PTR_MASK 0xffffffffLL +#define COUNT_MASK (0xffffffffLL << 32) + +static inline void set_count(pointer *p, unsigned int val) { *p = (*p & ~COUNT_MASK) | ((pointer)val << 32); } +static inline void set_ptr(pointer *p, unsigned int val) { *p = (*p & ~PTR_MASK) | val; } +static inline unsigned int get_count(pointer p) { return (p & COUNT_MASK) >> 32; } +static inline unsigned int get_ptr(pointer p) { return p & PTR_MASK; } + +typedef struct node { + unsigned int value; + pointer_t next; +} node_t; + +typedef struct { + pointer_t head; + pointer_t tail; + node_t nodes[MAX_NODES + 1]; +} queue_t; + +void init_queue(queue_t *q, int num_threads); +void enqueue(queue_t *q, unsigned int val); +bool dequeue(queue_t *q, unsigned int *retVal); +int get_thread_num(); diff --git a/cdschecker_modified_benchmarks/spsc-queue/Makefile b/cdschecker_modified_benchmarks/spsc-queue/Makefile new file mode 100644 index 0000000..33b9d01 --- /dev/null +++ b/cdschecker_modified_benchmarks/spsc-queue/Makefile @@ -0,0 +1,23 @@ +include ../benchmarks.mk + +TESTNAME = spsc-queue +RELACYNAME = spsc-relacy + +all: $(TESTNAME) + +$(TESTNAME): $(TESTNAME).cc queue.h eventcount.h + $(CXX) -o $@ $< $(CXXFLAGS) $(LDFLAGS) + +relacy: $(RELACYNAME) + +$(RELACYNAME): spsc-relacy.cc queue-relacy.h eventcount-relacy.h +ifdef RELACYPATH + $(CXX) -o $(RELACYNAME) spsc-relacy.cc -I$(RELACYPATH) -Wno-deprecated +else + @echo "Please define RELACYPATH" + @echo " e.g., make RELACYPATH=/path-to-relacy" + @exit 1 +endif + +clean: + rm -f $(TESTNAME) $(RELACYNAME) *.o diff --git a/cdschecker_modified_benchmarks/spsc-queue/eventcount-relacy.h b/cdschecker_modified_benchmarks/spsc-queue/eventcount-relacy.h new file mode 100644 index 0000000..9eadcf3 --- /dev/null +++ b/cdschecker_modified_benchmarks/spsc-queue/eventcount-relacy.h @@ -0,0 +1,64 @@ +class eventcount +{ +public: + eventcount() : waiters(0) + { + count($) = 0; + } + + void signal_relaxed() + { + unsigned cmp = count.load(std::memory_order_relaxed); + signal_impl(cmp); + } + + void signal() + { + unsigned cmp = count.fetch_add(0, std::memory_order_seq_cst); + signal_impl(cmp); + } + + unsigned get() + { + unsigned cmp = count.fetch_or(0x80000000, +std::memory_order_seq_cst); + return cmp & 0x7FFFFFFF; + } + + void wait(unsigned cmp) + { + unsigned ec = count.load(std::memory_order_seq_cst); + if (cmp == (ec & 0x7FFFFFFF)) + { + guard.lock($); + ec = count.load(std::memory_order_seq_cst); + if (cmp == (ec & 0x7FFFFFFF)) + { + waiters($) += 1; + cv.wait(guard, $); + } + guard.unlock($); + } + } + +private: + std::atomic count; + rl::var waiters; + std::mutex guard; + std::condition_variable cv; + + void signal_impl(unsigned cmp) + { + if (cmp & 0x80000000) + { + guard.lock($); + while (false == count.compare_exchange_weak(cmp, + (cmp + 1) & 0x7FFFFFFF, std::memory_order_relaxed)); + unsigned w = waiters($); + waiters($) = 0; + guard.unlock($); + if (w) + cv.notify_all($); + } + } +}; diff --git a/cdschecker_modified_benchmarks/spsc-queue/eventcount.h b/cdschecker_modified_benchmarks/spsc-queue/eventcount.h new file mode 100644 index 0000000..f2b05f7 --- /dev/null +++ b/cdschecker_modified_benchmarks/spsc-queue/eventcount.h @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +class eventcount +{ +public: + eventcount() : waiters(0) + { + count = 0; + } + + void signal_relaxed() + { + unsigned cmp = count.load(std::memory_order_relaxed); + signal_impl(cmp); + } + + void signal() + { + unsigned cmp = count.fetch_add(0, std::memory_order_seq_cst); + signal_impl(cmp); + } + + unsigned get() + { + unsigned cmp = count.fetch_or(0x80000000, +std::memory_order_seq_cst); + return cmp & 0x7FFFFFFF; + } + + void wait(unsigned cmp) + { + unsigned ec = count.load(std::memory_order_seq_cst); + if (cmp == (ec & 0x7FFFFFFF)) + { + //guard.lock($); + std::unique_lock lck(guard); + ec = count.load(std::memory_order_seq_cst); + if (cmp == (ec & 0x7FFFFFFF)) + { + waiters += 1; + //cv.wait(guard); + cv.wait(lck); + } + guard.unlock($); + //lck.unlock(); + } + } + +private: + std::atomic count; + rl::var waiters; + std::mutex guard; + std::condition_variable cv; + + void signal_impl(unsigned cmp) + { + if (cmp & 0x80000000) + { + guard.lock($); + while (false == count.compare_exchange_weak(cmp, + (cmp + 1) & 0x7FFFFFFF, std::memory_order_relaxed)); + unsigned w = waiters($); + waiters = 0; + guard.unlock($); + if (w) + cv.notify_all($); + } + } +}; diff --git a/cdschecker_modified_benchmarks/spsc-queue/queue-relacy.h b/cdschecker_modified_benchmarks/spsc-queue/queue-relacy.h new file mode 100644 index 0000000..71aac2a --- /dev/null +++ b/cdschecker_modified_benchmarks/spsc-queue/queue-relacy.h @@ -0,0 +1,74 @@ +#include "eventcount-relacy.h" + +template +class spsc_queue +{ +public: + spsc_queue() + { + node* n = new node (); + head($) = n; + tail($) = n; + } + + ~spsc_queue() + { + RL_ASSERT(head($) == tail($)); + delete ((node*)head($)); + } + + void enqueue(T data) + { + node* n = new node (data); + head($)->next.store(n, std::memory_order_release); + head($) = n; + ec.signal_relaxed(); + } + + T dequeue() + { + T data = try_dequeue(); + while (0 == data) + { + int cmp = ec.get(); + data = try_dequeue(); + if (data) + break; + ec.wait(cmp); + data = try_dequeue(); + if (data) + break; + } + return data; + } + +private: + struct node + { + std::atomic next; + rl::var data; + + node(T data = T()) + : data(data) + { + next($) = 0; + } + }; + + rl::var head; + rl::var tail; + + eventcount ec; + + T try_dequeue() + { + node* t = tail($); + node* n = t->next.load(std::memory_order_acquire); + if (0 == n) + return 0; + T data = n->data($); + delete (t); + tail($) = n; + return data; + } +}; diff --git a/cdschecker_modified_benchmarks/spsc-queue/queue.h b/cdschecker_modified_benchmarks/spsc-queue/queue.h new file mode 100644 index 0000000..c77425f --- /dev/null +++ b/cdschecker_modified_benchmarks/spsc-queue/queue.h @@ -0,0 +1,77 @@ +#include +#include + +#include "eventcount.h" + +template +class spsc_queue +{ +public: + spsc_queue() + { + node* n = new node (); + head = n; + tail = n; + } + + ~spsc_queue() + { + RL_ASSERT(head == tail); + delete ((node*)head($)); + } + + void enqueue(T data) + { + node* n = new node (data); + head($)->next.store(n, std::memory_order_release); + head = n; + ec.signal_relaxed(); + } + + T dequeue() + { + T data = try_dequeue(); + while (0 == data) + { + int cmp = ec.get(); + data = try_dequeue(); + if (data) + break; + ec.wait(cmp); + data = try_dequeue(); + if (data) + break; + } + return data; + } + +private: + struct node + { + std::atomic next; + rl::var data; + + node(T data = T()) + : data(data) + { + next = 0; + } + }; + + rl::var head; + rl::var tail; + + eventcount ec; + + T try_dequeue() + { + node* t = tail($); + node* n = t->next.load(std::memory_order_acquire); + if (0 == n) + return 0; + T data = n->data($); + delete (t); + tail = n; + return data; + } +}; diff --git a/cdschecker_modified_benchmarks/spsc-queue/spsc-queue.cc b/cdschecker_modified_benchmarks/spsc-queue/spsc-queue.cc new file mode 100644 index 0000000..1347f4e --- /dev/null +++ b/cdschecker_modified_benchmarks/spsc-queue/spsc-queue.cc @@ -0,0 +1,34 @@ +#include "cds_threads.h" + +#include "queue.h" + +spsc_queue *q; + + void thread(unsigned thread_index) + { + if (0 == thread_index) + { + q->enqueue(11); + } + else + { + int d = q->dequeue(); + RL_ASSERT(11 == d); + } + } + +int user_main(int argc, char **argv) +{ + thrd_t A, B; + + q = new spsc_queue(); + + thrd_create(&A, (thrd_start_t)&thread, (void *)0); + thrd_create(&B, (thrd_start_t)&thread, (void *)1); + thrd_join(A); + thrd_join(B); + + delete q; + + return 0; +} diff --git a/cdschecker_modified_benchmarks/spsc-queue/spsc-relacy.cc b/cdschecker_modified_benchmarks/spsc-queue/spsc-relacy.cc new file mode 100644 index 0000000..37ed989 --- /dev/null +++ b/cdschecker_modified_benchmarks/spsc-queue/spsc-relacy.cc @@ -0,0 +1,27 @@ +#include + +#include "queue-relacy.h" + +struct spsc_queue_test : rl::test_suite +{ + spsc_queue q; + + void thread(unsigned thread_index) + { + if (0 == thread_index) + { + q.enqueue(11); + } + else + { + int d = q.dequeue(); + RL_ASSERT(11 == d); + } + } +}; + + +int main() +{ + rl::simulate(); +} diff --git a/cdschecker_modified_benchmarks/test.sh b/cdschecker_modified_benchmarks/test.sh new file mode 100755 index 0000000..ff2c3b5 --- /dev/null +++ b/cdschecker_modified_benchmarks/test.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +EXE=$1 +TOTAL_RUN=5 #00 +CDSLIB="/scratch/fuzzer/random-fuzzer" +export LD_LIBRARY_PATH=${CDSLIB} +export C11TESTER='-x1' + +#ERROR_FILE="data-structure.log" +TASKSET="" + +COUNT_DATA_RACE=0 +COUNT_TIME=0 + +for i in `seq 1 1 $TOTAL_RUN` ; do +# time ${TASKSET} $EXE &> $ERROR_FILE +# OUTPUT=$(< $ERROR_FILE) + + OUTPUT="$(/usr/bin/time -f "time: %U %S" $EXE -x1 2>&1)" + RACE="$(echo "$OUTPUT" | grep "race")" + if [ -n "$RACE" ] ; then + ((++COUNT_DATA_RACE)) + fi + + TIME="$(echo "$OUTPUT" | grep -o "time: .\... .\...")" + TIME_USER_S="$(echo "$TIME" | cut -d' ' -f2 | cut -d'.' -f1)" + TIME_USER_CS="$(echo "$TIME" | cut -d' ' -f2 | cut -d'.' -f2)" + TIME_SYSTEM_S="$(echo "$TIME" | cut -d' ' -f3 | cut -d'.' -f1)" + TIME_SYSTEM_CS="$(echo "$TIME" | cut -d' ' -f3 | cut -d'.' -f2)" + + TIME_EXE=$((10#$TIME_USER_S * 1000 + 10#$TIME_USER_CS * 10 + 10#$TIME_SYSTEM_S * 1000 + 10#$TIME_SYSTEM_CS * 10)) + COUNT_TIME=$((COUNT_TIME + TIME_EXE)) +done + +#rm $ERROR_FILE + +AVG_DATA_RACE=$(echo "${COUNT_DATA_RACE} * 100 / ${TOTAL_RUN}" | bc -l | xargs printf "%.1f") +AVG_TIME_INT=$(echo "${COUNT_TIME} / ${TOTAL_RUN} + 0.5" | bc -l | xargs printf "%.0f") + +# -3 / log(1 - p) < n +#NO_99=$(echo "-3 / (l(1 - (${AVG_DATA_RACE} / 100)) / l(10)) + 0.5" | bc -l | xargs printf "%.0f") +#TIME_99=$(echo "${NO_99} * ${AVG_TIME_INT}" | bc -l) + +echo "Runs: $TOTAL_RUN | Data races: $COUNT_DATA_RACE | Total time: ${COUNT_TIME}ms" +echo "Time: ${AVG_TIME_INT}ms | Race rate: ${AVG_DATA_RACE}%" +#echo "Time: ${AVG_TIME_INT}ms | Race rate: ${AVG_DATA_RACE}% | No. 99.9%: ${NO_99} | Time 99.9%: ${TIME_99}ms" diff --git a/cdschecker_modified_benchmarks/test_all.sh b/cdschecker_modified_benchmarks/test_all.sh new file mode 100755 index 0000000..a7200eb --- /dev/null +++ b/cdschecker_modified_benchmarks/test_all.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e +set -u + +# Paul: skip `spsc-queue` as it deadlocks. + +for t in barrier chase-lev-deque dekker-fences linuxrwlocks mcs-lock mpmc-queue ms-queue; do + cd $t + echo -n "$t " + ../test.sh ./$t + cd .. +done + -- 2.34.1