Lesson 35 - Get Compute Auth Token Working

This commit is contained in:
Norman Lansing
2026-02-28 12:32:28 -05:00
parent 1d477ee42a
commit 4fde462bce
7743 changed files with 1397833 additions and 18 deletions

View File

@@ -0,0 +1,5 @@
These tests require CDSChecker to be checked out into a subdirectory
named 'model-checker'.
CDSChecker can be obtained from: git://demsky.eecs.uci.edu/model-checker.git
The version last used for testing was: da671f78d0aa057272bb82f580b36a188b6331bd

View File

@@ -0,0 +1,114 @@
// ©2013 Cameron Desrochers
// Provides the core enqueue/dequeue algorithm of moodycamel::ConcurrentQueue
// for testing with CDSChecker (a C++11 memory model checking tool).
// See http://demsky.eecs.uci.edu/c11modelchecker.html for more info.
#pragma once
#include "model-checker/include/atomic"
#include "model-checker/include/librace.h"
#include "model-checker/include/model-assert.h"
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
typedef unsigned int index_t;
static std::atomic<index_t> headIndex;
static std::atomic<index_t> tailIndex;
static std::atomic<index_t> dequeueOvercommit;
static std::atomic<index_t> dequeueOptimisticCount;
static const unsigned int BLOCK_SIZE = 256;
static int block[BLOCK_SIZE];
static void init()
{
headIndex.store(0, std::memory_order_relaxed);
tailIndex.store(0, std::memory_order_relaxed);
dequeueOvercommit.store(0, std::memory_order_relaxed);
dequeueOptimisticCount.store(0, std::memory_order_relaxed);
}
template<typename T>
static inline bool circular_less_than(T a, T b)
{
return static_cast<T>(a - b) > static_cast<T>(static_cast<T>(1) << static_cast<T>(sizeof(T) * CHAR_BIT - 1));
}
static void enqueue(int element)
{
index_t currentTailIndex = tailIndex.load(std::memory_order_relaxed);
index_t newTailIndex = 1 + currentTailIndex;
store_32(&block[currentTailIndex & (BLOCK_SIZE - 1)], element);
tailIndex.store(newTailIndex, std::memory_order_release);
}
static bool try_dequeue(int& element)
{
auto tail = tailIndex.load(std::memory_order_relaxed);
auto overcommit = dequeueOvercommit.load(std::memory_order_relaxed);
if (circular_less_than<index_t>(dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) {
// Might be something to dequeue, let's give it a try
// Note that this if is purely for performance purposes in the common case when the queue is
// empty and the values are eventually consistent -- we may enter here spuriously.
// Note that whatever the values of overcommit and tail are, they are not going to change (unless we
// change them) and must be the same value at this point (inside the if) as when the if condition was
// evaluated.
// We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below.
// This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in
// the fetch_add below will result in a value at least as recent as that (and therefore at least as large).
// Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all
// read-modify-write operations are guaranteed to work on the latest value in the modification order), but
// unfortunately that can't be shown to be correct using only the C++11 standard.
// See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case
std::atomic_thread_fence(std::memory_order_acquire);
// Increment optimistic counter, then check if it went over the boundary
auto myDequeueCount = dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed);
// Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever
// incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now
// have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon
// incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount.
MODEL_ASSERT(overcommit <= myDequeueCount);
// Note that we reload tail here in case it changed; it will be the same value as before or greater, since
// this load is sequenced after (happens after) the earlier load above. This is supported by read-read
// coherance (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order
auto newTail = tailIndex.load(std::memory_order_acquire);
MODEL_ASSERT(newTail >= tail);
tail = newTail;
if (circular_less_than<index_t>(myDequeueCount - overcommit, tail)) {
// Guaranteed to be at least one element to dequeue!
// Get the index. Note that since there's guaranteed to be at least one element, this
// will never exceed the true value of tail (but may exceed the value we read above).
auto index = headIndex.fetch_add(1, std::memory_order_acq_rel);
// Dequeue
element = load_32(&block[index & (BLOCK_SIZE - 1)]);
return true;
}
else {
// Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent
dequeueOvercommit.fetch_add(1, std::memory_order_release); // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write
}
}
return false;
}
static int size_approx()
{
auto tail = tailIndex.load(std::memory_order_relaxed);
auto head = headIndex.load(std::memory_order_relaxed);
return circular_less_than(head, tail) ? static_cast<int>(tail - head) : 0;
}

View File

@@ -0,0 +1,62 @@
// ©2013 Cameron Desrochers.
// Distributed under the simplified BSD license (see the LICENSE file that
// should have come with this file).
#include "model-checker/include/threads.h"
#include "corealgo.h"
void consumer(void* param)
{
int id = *(int*)param;
int& dequeueCount = *(int*)param;
dequeueCount = 0;
int last = 0;
int element;
bool success = try_dequeue(element);
if (success) {
MODEL_ASSERT(element > last);
last = element;
++dequeueCount;
}
success = try_dequeue(element);
if (success) {
MODEL_ASSERT(element > last);
last = element;
++dequeueCount;
}
}
void producer(void* param)
{
for (int i = 1; i <= 8; ++i)
enqueue(i);
consumer(param);
}
int user_main(int, char**)
{
init();
// Start out as thread IDs, but are re-used by the threads
// to indicate the number of elements each one dequeued
int w = 1, x = 2, y = 3, z = 4;
thrd_t a, b, c, d;
thrd_create(&a, &producer, &w);
thrd_create(&b, &consumer, &x);
thrd_create(&c, &consumer, &y);
thrd_create(&d, &consumer, &z);
thrd_join(a);
thrd_join(b);
thrd_join(c);
thrd_join(d);
MODEL_ASSERT(w + x + y + z + size_approx() == 8);
return 0;
}

View File

@@ -0,0 +1,37 @@
// ©2013 Cameron Desrochers.
// Distributed under the simplified BSD license (see the LICENSE file that
// should have come with this file).
#include "model-checker/include/threads.h"
#include "corealgo.h"
void producer_thread(void*)
{
enqueue(1234);
}
void consumer_thread(void*)
{
int element;
bool result = try_dequeue(element);
MODEL_ASSERT(!result || element == 1234);
if (result) {
MODEL_ASSERT(!try_dequeue(element));
}
}
int user_main(int, char**)
{
init();
thrd_t p, c;
thrd_create(&p, &producer_thread, nullptr);
thrd_create(&c, &consumer_thread, nullptr);
thrd_join(p);
thrd_join(c);
return 0;
}

View File

@@ -0,0 +1,14 @@
default: enqueue_dequeue_one.o enqueue_dequeue_many.o
enqueue_dequeue_one.o: CDSChecker makefile enqueue_dequeue_many.cpp enqueue_dequeue_one.cpp corealgo.h
g++ -std=c++11 -DNDEBUG -O3 enqueue_dequeue_one.cpp -o enqueue_dequeue_one.o -Lmodel-checker -lmodel
enqueue_dequeue_many.o: CDSChecker makefile enqueue_dequeue_many.cpp corealgo.h
g++ -std=c++11 -DNDEBUG -O3 enqueue_dequeue_many.cpp -o enqueue_dequeue_many.o -Lmodel-checker -lmodel
CDSChecker:
$(MAKE) -C model-checker
run: enqueue_dequeue_one.o enqueue_dequeue_many.o
cd model-checker && ./run.sh ../enqueue_dequeue_one.o > ../enqueue_dequeue_one.log
cd model-checker && ./run.sh ../enqueue_dequeue_many.o > ../enqueue_dequeue_many.log

View File

@@ -0,0 +1,84 @@
// ©2013 Cameron Desrochers
#include "simplethread.h"
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
struct SimpleThread::ThreadRef
{
HANDLE handle;
static DWORD WINAPI ThreadProc(LPVOID param)
{
auto threadRef = static_cast<ThreadRef*>(param);
threadRef->callbackFunc(threadRef->callbackObj);
return 0;
}
ThreadRef(void* callbackObj, CallbackFunc callbackFunc)
: callbackObj(callbackObj), callbackFunc(callbackFunc)
{
}
void* callbackObj;
CallbackFunc callbackFunc;
};
void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc)
{
thread = new ThreadRef(callbackObj, callbackFunc);
thread->handle = CreateThread(NULL, StackSize, &ThreadRef::ThreadProc, thread, 0, NULL);
}
void SimpleThread::join()
{
if (thread != nullptr && thread->handle != NULL) {
WaitForSingleObject(thread->handle, INFINITE);
CloseHandle(thread->handle);
thread->handle = NULL;
}
}
#else
#include <thread>
struct SimpleThread::ThreadRef
{
std::thread thread;
static void threadProc(ThreadRef* threadRef)
{
threadRef->callbackFunc(threadRef->callbackObj);
}
ThreadRef(void* callbackObj, CallbackFunc callbackFunc)
: callbackObj(callbackObj), callbackFunc(callbackFunc)
{
}
void* callbackObj;
CallbackFunc callbackFunc;
};
void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc)
{
thread = new ThreadRef(callbackObj, callbackFunc);
thread->thread = std::thread(&ThreadRef::threadProc, thread);
}
void SimpleThread::join()
{
if (thread != nullptr && thread->thread.joinable()) {
thread->thread.join();
}
}
#endif
SimpleThread::~SimpleThread()
{
if (thread != nullptr) {
join();
delete thread;
}
}

View File

@@ -0,0 +1,162 @@
// ©2013 Cameron Desrochers
#pragma once
// Like C++11's std::thread, but with a reduced API, and works on Windows with MSVC2010+.
// Wraps std::thread on other OSes. Perhaps the most significant departure between
// std::thread and this mini-library is that join() is called implicitly in the destructor,
// if the thread is joinable. The thread callback functions should not throw exceptions.
#include <utility>
#include <type_traits>
namespace details
{
template<typename TArg1 = void, typename TArg2 = void, typename TArg3 = void>
struct ArgWrapper
{
typename std::remove_reference<TArg1>::type arg1;
typename std::remove_reference<TArg2>::type arg2;
typename std::remove_reference<TArg3>::type arg3;
ArgWrapper(ArgWrapper const& o) : arg1(o.arg1), arg2(o.arg2), arg3(o.arg3) { }
ArgWrapper(ArgWrapper&& o) : arg1(std::move(o.arg1)), arg2(std::move(o.arg2)), arg3(std::move(o.arg3)) { }
template<typename T, typename U, typename V>
ArgWrapper(T&& a1, U&& a2, V&& a3) : arg1(std::forward<T>(a1)), arg2(std::forward<U>(a2)), arg3(std::forward<V>(a3)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1), std::move(arg2), std::move(arg3)); }
};
template<typename TArg1, typename TArg2>
struct ArgWrapper<TArg1, TArg2, void>
{
typename std::remove_reference<TArg1>::type arg1;
typename std::remove_reference<TArg2>::type arg2;
ArgWrapper(ArgWrapper const& o) : arg1(o.arg1), arg2(o.arg2) { }
ArgWrapper(ArgWrapper&& o) : arg1(std::move(o.arg1)), arg2(std::move(o.arg2)) { }
template<typename T, typename U>
ArgWrapper(T&& a1, U&& a2) : arg1(std::forward<T>(a1)), arg2(std::forward<U>(a2)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1), std::move(arg2)); }
};
template<typename TArg1>
struct ArgWrapper<TArg1, void, void>
{
typename std::remove_reference<TArg1>::type arg1;
ArgWrapper(ArgWrapper const& o) : arg1(o.arg1) { }
ArgWrapper(ArgWrapper&& o) : arg1(std::move(o.arg1)) { }
template<typename T>
ArgWrapper(T&& a1) : arg1(std::forward<T>(a1)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1)); }
};
template<> struct ArgWrapper<void, void, void>
{
template<typename TCallback> void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(); }
};
}
class SimpleThread
{
private:
struct ThreadRef;
template<typename TCallback, typename TArgs>
struct CallbackWrapper
{
template<typename U>
CallbackWrapper(TCallback&& callback, U&& args)
: callback(std::forward<TCallback>(callback)), args(std::forward<U>(args))
{
}
static void callAndDelete(void* wrapper)
{
auto typedWrapper = static_cast<CallbackWrapper*>(wrapper);
typedWrapper->args.callCallback(std::move(typedWrapper->callback));
delete typedWrapper;
}
typename std::decay<TCallback>::type callback;
TArgs args;
};
typedef void (*CallbackFunc)(void*);
void startThread(void* callbackObj, CallbackFunc callbackFunc);
public:
static const int StackSize = 4 * 1024; // bytes
SimpleThread() : thread(nullptr) { }
SimpleThread(SimpleThread&& other)
: thread(other.thread)
{
other.thread = nullptr;
}
SimpleThread& operator=(SimpleThread&& other)
{
thread = other.thread;
other.thread = nullptr;
return *this;
}
// Disable copying and copy-assignment
private:
SimpleThread(SimpleThread const&);
SimpleThread& operator=(SimpleThread const&);
public:
template<typename TCallback>
explicit SimpleThread(TCallback&& callback)
{
auto wrapper = new CallbackWrapper<TCallback, ::details::ArgWrapper<>>(
std::forward<TCallback>(callback),
::details::ArgWrapper<>()
);
startThread(wrapper, &CallbackWrapper<TCallback, ::details::ArgWrapper<>>::callAndDelete);
}
template<typename TCallback, typename TArg1>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1)
{
auto wrapper = new CallbackWrapper<TCallback, ::details::ArgWrapper<TArg1>>(
std::forward<TCallback>(callback),
::details::ArgWrapper<TArg1>(std::forward<TArg1>(arg1))
);
startThread(wrapper, &CallbackWrapper<TCallback, ::details::ArgWrapper<TArg1>>::callAndDelete);
}
template<typename TCallback, typename TArg1, typename TArg2>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2)
{
auto wrapper = new CallbackWrapper<TCallback, ::details::ArgWrapper<TArg1, TArg2>>(
std::forward<TCallback>(callback),
::details::ArgWrapper<TArg1, TArg2>(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2))
);
startThread(wrapper, &CallbackWrapper<TCallback, ::details::ArgWrapper<TArg1, TArg2>>::callAndDelete);
}
template<typename TCallback, typename TArg1, typename TArg2, typename TArg3>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2, TArg3&& arg3)
{
auto wrapper = new CallbackWrapper<TCallback, ::details::ArgWrapper<TArg1, TArg2, TArg3>>(
std::forward<TCallback>(callback),
::details::ArgWrapper<TArg1, TArg2, TArg3>(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), std::forward<TArg3>(arg3))
);
startThread(wrapper, &CallbackWrapper<TCallback, ::details::ArgWrapper<TArg1, TArg2, TArg3>>::callAndDelete);
}
~SimpleThread();
void join();
private:
ThreadRef* thread;
};

View File

@@ -0,0 +1,144 @@
// ©2013-2014 Cameron Desrochers
#include "systemtime.h"
#include <climits>
#if defined(_MSC_VER) && _MSC_VER < 1700
#include <intrin.h>
#define CompilerMemBar() _ReadWriteBarrier()
#else
#include <atomic>
#define CompilerMemBar() std::atomic_signal_fence(std::memory_order_seq_cst)
#endif
#if defined(ST_WINDOWS)
#include <windows.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::Sleep(milliseconds);
}
SystemTime getSystemTime()
{
LARGE_INTEGER t;
CompilerMemBar();
if (!QueryPerformanceCounter(&t)) {
return static_cast<SystemTime>(-1);
}
CompilerMemBar();
return static_cast<SystemTime>(t.QuadPart);
}
double getTimeDelta(SystemTime start)
{
LARGE_INTEGER t;
CompilerMemBar();
if (start == static_cast<SystemTime>(-1) || !QueryPerformanceCounter(&t)) {
return -1;
}
CompilerMemBar();
auto now = static_cast<SystemTime>(t.QuadPart);
LARGE_INTEGER f;
if (!QueryPerformanceFrequency(&f)) {
return -1;
}
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
return static_cast<double>(static_cast<__int64>(now - start)) / f.QuadPart * 1000;
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
}
} // end namespace moodycamel
#elif defined(ST_APPLE)
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <unistd.h>
#include <time.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::usleep(milliseconds * 1000);
}
SystemTime getSystemTime()
{
CompilerMemBar();
std::uint64_t result = mach_absolute_time();
CompilerMemBar();
return result;
}
double getTimeDelta(SystemTime start)
{
CompilerMemBar();
std::uint64_t end = mach_absolute_time();
CompilerMemBar();
mach_timebase_info_data_t tb = { 0 };
mach_timebase_info(&tb);
double toNano = static_cast<double>(tb.numer) / tb.denom;
return static_cast<double>(end - start) * toNano * 0.000001;
}
} // end namespace moodycamel
#elif defined(ST_NIX)
#include <unistd.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::usleep(milliseconds * 1000);
}
SystemTime getSystemTime()
{
timespec t;
CompilerMemBar();
if (clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) {
t.tv_sec = (time_t)-1;
t.tv_nsec = -1;
}
CompilerMemBar();
return t;
}
double getTimeDelta(SystemTime start)
{
timespec t;
CompilerMemBar();
if ((start.tv_sec == (time_t)-1 && start.tv_nsec == -1) || clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) {
return -1;
}
CompilerMemBar();
return static_cast<double>(static_cast<long>(t.tv_sec) - static_cast<long>(start.tv_sec)) * 1000 + double(t.tv_nsec - start.tv_nsec) / 1000000;
}
} // end namespace moodycamel
#endif

View File

@@ -0,0 +1,33 @@
// ©2013-2014 Cameron Desrochers
#pragma once
#if defined(_WIN32)
#define ST_WINDOWS
#elif defined(__APPLE__) && defined(__MACH__)
#define ST_APPLE
#elif defined(__linux__) || defined(__FreeBSD__) || defined(BSD)
#define ST_NIX
#else
#error "Unknown platform"
#endif
#if defined(ST_WINDOWS)
namespace moodycamel { typedef unsigned long long SystemTime; }
#elif defined(ST_APPLE)
#include <cstdint>
namespace moodycamel { typedef std::uint64_t SystemTime; }
#elif defined(ST_NIX)
#include <time.h>
namespace moodycamel { typedef timespec SystemTime; }
#endif
namespace moodycamel
{
void sleep(int milliseconds);
SystemTime getSystemTime();
// Returns the delta time, in milliseconds
double getTimeDelta(SystemTime start);
}

View File

@@ -0,0 +1,669 @@
// ©2014 Cameron Desrochers
// moodycamel::ConcurrentQueue contains many inner data structures which
// are difficult to test in isolation. So, this file contains copies of
// them, extracted and isolated so as to be independently testable.
#pragma once
#include <atomic>
#include <cstdint>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <limits>
#include <cassert>
// Define corealgos_allocator before including this header in order to override the
// default malloc/free functions
#ifndef corealgos_allocator
struct corealgos_allocator
{
static inline void* malloc(std::size_t size) { return std::malloc(size); }
static inline void free(void* ptr) { std::free(ptr); }
};
#endif
////////////////////////////////////////////////////////////////////////////////
// Lock-free add-only list (e.g. used to track producers)
////////////////////////////////////////////////////////////////////////////////
namespace moodycamel { namespace corealgos {
struct ListItem
{
ListItem()
: concurrentListPrev(nullptr)
{
}
public:
std::atomic<ListItem*> concurrentListPrev;
};
template<typename T> // T should inherit ListItem or implement the same interface
struct ConcurrentAddOnlyList
{
ConcurrentAddOnlyList()
: tail_(nullptr)
{
}
inline T* tail() { return tail_.load(std::memory_order_acquire); }
void add(T* element)
{
assert(element != nullptr);
// Add it to the lock-free list
auto prevTail = tail_.load(std::memory_order_relaxed);
do {
element->concurrentListPrev = prevTail;
} while (!tail_.compare_exchange_weak(prevTail, element, std::memory_order_release, std::memory_order_relaxed));
}
private:
std::atomic<T*> tail_;
};
} }
////////////////////////////////////////////////////////////////////////////////
// Thread local hash map
////////////////////////////////////////////////////////////////////////////////
#if defined(__APPLE__)
#include "TargetConditionals.h" // Needed for TARGET_OS_IPHONE
#endif
// Platform-specific definitions of a numeric thread ID type and an invalid value
#if defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__)
// No sense pulling in windows.h in a header, we'll manually declare the function
// we use and rely on backwards-compatibility for this not to break
extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
namespace moodycamel { namespace corealgos { namespace details {
static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows");
typedef std::uint32_t thread_id_t;
static const thread_id_t invalid_thread_id = 0; // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx
static inline thread_id_t thread_id() { return static_cast<thread_id_t>(::GetCurrentThreadId()); }
} } }
#elif defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || (defined(__APPLE__) && TARGET_OS_IPHONE)
namespace moodycamel { namespace corealgos { namespace details {
typedef std::uintptr_t thread_id_t;
static const thread_id_t invalid_thread_id = 0;
static inline thread_id_t thread_id() { return std::hash<std::thread::id>()(std::this_thread::get_id()); }
} } }
#else
// Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475
// In order to get a numeric thread ID in a platform-independent way, we use a thread-local
// static variable's address as a thread identifier :-)
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#define MOODYCAMEL_COREALGO_THREADLOCAL __thread
#elif defined(_MSC_VER)
#define MOODYCAMEL_COREALGO_THREADLOCAL __declspec(thread)
#else
// Assume C++11 compliant compiler
#define MOODYCAMEL_COREALGO_THREADLOCAL thread_local
#endif
namespace moodycamel { namespace corealgos { namespace details {
typedef std::uintptr_t thread_id_t;
static const thread_id_t invalid_thread_id = 0; // Address can't be nullptr
static inline thread_id_t thread_id() { static MOODYCAMEL_COREALGO_THREADLOCAL int x; return reinterpret_cast<thread_id_t>(&x); }
} } }
#endif
namespace moodycamel { namespace corealgos {
namespace details
{
template<bool use32> struct _hash_32_or_64 {
static inline std::size_t hash(std::uint32_t h)
{
// MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
// Since the thread ID is already unique, all we really want to do is propagate that
// uniqueness evenly across all the bits, so that we can use a subset of the bits while
// reducing collisions significantly
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
return static_cast<std::size_t>(h ^ (h >> 16));
}
};
template<> struct _hash_32_or_64<1> {
static inline std::size_t hash(std::uint64_t h)
{
h ^= h >> 33;
h *= 0xff51afd7ed558ccd;
h ^= h >> 33;
h *= 0xc4ceb9fe1a85ec53;
return static_cast<std::size_t>(h ^ (h >> 33));
}
};
template<std::size_t size> struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> { };
static inline std::size_t hash_thread_id(thread_id_t id)
{
static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values");
return hash_32_or_64<sizeof(thread_id_t)>::hash(id);
}
template<typename U>
static inline char* align_for(char* ptr)
{
const std::size_t alignment = std::alignment_of<U>::value;
return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
}
}
template<typename T> // T should inherit ListItem or implement the same interface
struct ThreadLocal
{
explicit ThreadLocal(std::size_t initialHashSize)
: initialHashEntries(initialHashSize)
{
assert(initialHashSize > 0 && (initialHashSize & (initialHashSize - 1)) == 0);
resizeInProgress.clear();
currentHashCount.store(0, std::memory_order_relaxed);
auto hash = &initialHash;
hash->capacity = initialHashSize;
hash->entries = &initialHashEntries[0];
for (std::size_t i = 0; i != initialHashSize; ++i) {
initialHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
}
hash->prev = nullptr;
currentHash.store(hash, std::memory_order_relaxed);
}
~ThreadLocal()
{
// Destroy items
auto ptr = items.tail();
while (ptr != nullptr) {
auto prev = static_cast<T*>(ptr->concurrentListPrev.load(std::memory_order_relaxed));
ptr->~T();
corealgos_allocator::free(ptr);
ptr = prev;
}
// Destroy hash tables
auto hash = currentHash.load(std::memory_order_relaxed);
while (hash != nullptr) {
auto prev = hash->prev;
if (prev != nullptr) { // The last hash is part of this object and was not allocated dynamically
for (std::size_t i = 0; i != hash->capacity; ++i) {
hash->entries[i].~KeyValuePair();
}
hash->~InnerHash();
corealgos_allocator::free(hash);
}
hash = prev;
}
}
// Only fails (returns nullptr) if memory allocation fails
T* get_or_create()
{
// Note that since the data is essentially thread-local (key is thread ID),
// there's a reduced need for fences (memory ordering is already consistent
// for any individual thread), except for the current table itself
// Start by looking for the thread ID in the current and all previous hash tables.
// If it's not found, it must not be in there yet, since this same thread would
// have added it previously to one of the tables that we traversed.
// Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table
auto id = details::thread_id();
auto hashedId = details::hash_thread_id(id);
auto mainHash = currentHash.load(std::memory_order_acquire);
for (auto hash = mainHash; hash != nullptr; hash = hash->prev) {
// Look for the id in this hash
auto index = hashedId;
while (true) { // Not an infinite loop because at least one slot is free in the hash table
index &= hash->capacity - 1;
auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed);
if (probedKey == id) {
// Found it! If we had to search several hashes deep, though, we should lazily add it
// to the current main hash table to avoid the extended search next time.
// Note there's guaranteed to be room in the current hash table since every subsequent
// table implicitly reserves space for all previous tables (there's only one
// currentHashCount).
auto value = hash->entries[index].value;
if (hash != mainHash) {
index = hashedId;
while (true) {
index &= mainHash->capacity - 1;
probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed);
auto expected = details::invalid_thread_id;
if (probedKey == expected && mainHash->entries[index].key.compare_exchange_strong(expected, id, std::memory_order_relaxed)) {
mainHash->entries[index].value = value;
break;
}
++index;
}
}
return value;
}
if (probedKey == details::invalid_thread_id) {
break; // Not in this hash table
}
++index;
}
}
// Insert!
auto newCount = 1 + currentHashCount.fetch_add(1, std::memory_order_relaxed);
while (true) {
if (newCount >= (mainHash->capacity >> 1) && !resizeInProgress.test_and_set(std::memory_order_acquire)) {
// We've acquired the resize lock, try to allocate a bigger hash table.
// Note the acquire fence synchronizes with the release fence at the end of this block, and hence when
// we reload currentHash it must be the most recent version (it only gets changed within this
// locked block).
mainHash = currentHash.load(std::memory_order_acquire);
auto newCapacity = mainHash->capacity << 1;
while (newCount >= (newCapacity >> 1)) {
newCapacity <<= 1;
}
auto raw = static_cast<char*>(corealgos_allocator::malloc(sizeof(InnerHash) + std::alignment_of<KeyValuePair>::value - 1 + sizeof(KeyValuePair) * newCapacity));
if (raw == nullptr) {
// Allocation failed
currentHashCount.fetch_add((uint32_t)-1, std::memory_order_relaxed);
resizeInProgress.clear(std::memory_order_relaxed);
return nullptr;
}
auto newHash = new (raw) InnerHash;
newHash->capacity = newCapacity;
newHash->entries = reinterpret_cast<KeyValuePair*>(details::align_for<KeyValuePair>(raw + sizeof(InnerHash)));
for (std::size_t i = 0; i != newCapacity; ++i) {
new (newHash->entries + i) KeyValuePair;
newHash->entries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
}
newHash->prev = mainHash;
currentHash.store(newHash, std::memory_order_release);
resizeInProgress.clear(std::memory_order_release);
mainHash = newHash;
}
// If it's < three-quarters full, add to the old one anyway so that we don't have to wait for the next table
// to finish being allocated by another thread (and if we just finished allocating above, the condition will
// always be true)
if (newCount < (mainHash->capacity >> 1) + (mainHash->capacity >> 2)) {
auto element = (T*)corealgos_allocator::malloc(sizeof(T));
if (element == nullptr) {
return nullptr;
}
new (element) T();
items.add(element); // Track items so they can be destructed later
auto index = hashedId;
while (true) {
index &= mainHash->capacity - 1;
auto probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed);
auto expected = details::invalid_thread_id;
if (probedKey == expected && mainHash->entries[index].key.compare_exchange_strong(expected, id, std::memory_order_relaxed)) {
mainHash->entries[index].value = element;
break;
}
++index;
}
return element;
}
// Hmm, the old hash is quite full and somebody else is busy allocating a new one.
// We need to wait for the allocating thread to finish (if it succeeds, we add, if not,
// we try to allocate ourselves).
mainHash = currentHash.load(std::memory_order_acquire);
}
}
private:
struct KeyValuePair
{
std::atomic<details::thread_id_t> key;
T* value; // No need for atomicity since it's only read by the thread that sets it in the first place
KeyValuePair()
{ }
KeyValuePair(KeyValuePair const& other)
: key(other.key.load()), value(other.value)
{ }
KeyValuePair& operator=(KeyValuePair const& other)
{
key.store(other.key.load());
value = other.value;
return *this;
}
};
struct InnerHash
{
std::size_t capacity;
KeyValuePair* entries;
InnerHash* prev;
};
std::atomic_flag resizeInProgress;
std::atomic<InnerHash*> currentHash;
std::atomic<std::size_t> currentHashCount; // Number of slots logically used
InnerHash initialHash;
std::vector<KeyValuePair> initialHashEntries;
ConcurrentAddOnlyList<T> items;
};
////////////////////////////////////////////////////////////////////////////////
// Lock-free free list
////////////////////////////////////////////////////////////////////////////////
template <typename N>
struct FreeListNode
{
FreeListNode() : freeListRefs(0), freeListNext(nullptr) { }
std::atomic<std::uint32_t> freeListRefs;
std::atomic<N*> freeListNext;
FreeListNode(FreeListNode const& other)
: freeListRefs(other.freeListRefs.load()), freeListNext(other.freeListNext.load())
{ }
FreeListNode& operator=(FreeListNode const& other)
{
freeListRefs.store(other.freeListRefs.load());
freeListNext.store(other.freeListNext.load());
return *this;
}
};
// A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention,
// but simple and correct (assuming nodes are never freed until after the free list is destroyed),
// and fairly speedy under low contention.
template<typename N> // N must inherit FreeListNode or have the same fields (and initialization)
struct FreeList
{
FreeList() : freeListHead(nullptr) { }
inline void add(N* node)
{
// We know that the should-be-on-freelist bit is 0 at this point, so it's safe to
// set it using a fetch_add
if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) {
// Oh look! We were the last ones referencing this node, and we know
// we want to add it to the free list, so let's do it!
add_knowing_refcount_is_zero(node);
}
}
inline N* try_get()
{
auto head = freeListHead.load(std::memory_order_acquire);
while (head != nullptr) {
auto prevHead = head;
auto refs = head->freeListRefs.load(std::memory_order_relaxed);
if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1,
std::memory_order_acquire, std::memory_order_relaxed)) {
head = freeListHead.load(std::memory_order_acquire);
continue;
}
// Good, reference count has been incremented (it wasn't at zero), which means
// we can read the next and not worry about it changing between now and the time
// we do the CAS
auto next = head->freeListNext.load(std::memory_order_relaxed);
if (freeListHead.compare_exchange_strong(head, next,
std::memory_order_acquire, std::memory_order_relaxed)) {
// Yay, got the node. This means it was on the list, which means
// shouldBeOnFreeList must be false no matter the refcount (because
// nobody else knows it's been taken off yet, it can't have been put back on).
assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0);
// Decrease refcount twice, once for our ref, and once for the list's ref
head->freeListRefs.fetch_add(-2u, std::memory_order_release);
return head;
}
// OK, the head must have changed on us, but we still need to decrease the refcount we
// increased.
// Note that we don't need to release any memory effects, but we do need to ensure that the reference
// count decrement happens-after the CAS on the head.
refs = prevHead->freeListRefs.fetch_add(-1u, std::memory_order_acq_rel);
if (refs == SHOULD_BE_ON_FREELIST + 1) {
add_knowing_refcount_is_zero(prevHead);
}
}
return nullptr;
}
// Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes)
N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); }
private:
inline void add_knowing_refcount_is_zero(N* node)
{
// Since the refcount is zero, and nobody can increase it once it's zero (except us, and we
// run only one copy of this method per node at a time, i.e. the single thread case), then we
// know we can safely change the next pointer of the node; however, once the refcount is back
// above zero, then other threads could increase it (happens under heavy contention, when the
// refcount goes to zero in between a load and a refcount increment of a node in try_get, then
// back up to something non-zero, then the refcount increment is done by the other thread) --
// so, if the CAS to add the node to the actual list fails, decrease the refcount and leave
// the add operation to the next thread who puts the refcount back at zero (which could be us,
// hence the loop).
auto head = freeListHead.load(std::memory_order_relaxed);
while (true) {
node->freeListNext.store(head, std::memory_order_relaxed);
node->freeListRefs.store(1, std::memory_order_release);
if (!freeListHead.compare_exchange_strong(head, node,
std::memory_order_release, std::memory_order_relaxed)) {
// Hmm, the add failed, but we can only try again when the refcount goes back to zero
if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) {
continue;
}
}
return;
}
}
private:
static const std::uint32_t REFS_MASK = 0x7FFFFFFF;
static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000;
// Implemented like a stack, but where node order doesn't matter (nodes are
// inserted out of order under contention)
std::atomic<N*> freeListHead;
};
////////////////////////////////////////////////////////////////////////////////
// Lock-free (single-producer, multi-consumer) numeric-key hash map of sorts;
// there are many conditions that must be met, i.e. items have to be inserted
// in increasing order by key (wrap-around is OK), and items cannot be searched
// for or removed unless they are known to be in the map in the first place.
////////////////////////////////////////////////////////////////////////////////
template<typename TValue>
struct SPMCSequentialHashMap
{
explicit SPMCSequentialHashMap(std::size_t initialSize)
: nextCapacity(initialSize), index(nullptr)
{
new_index();
}
~SPMCSequentialHashMap()
{
auto ptr = index.load(std::memory_order_relaxed);
if (ptr != nullptr) {
for (std::size_t i = 0; i != ptr->capacity; ++i) {
ptr->index[i]->~IndexEntry();
}
do {
auto prev = ptr->prev;
ptr->~IndexHeader();
corealgos_allocator::free(ptr);
ptr = prev;
} while (ptr != nullptr);
}
}
// Not thread safe. Only call from single producer thread.
// Note: key must *not* be in hash already, and must be exactly
// one larger than the previously inserted key value.
void insert(std::uint64_t key, TValue* value)
{
IndexEntry* idxEntry;
insert_index_entry(idxEntry, key);
idxEntry->value.store(value, std::memory_order_release);
}
// Thread-safe, but if somebody can remove the key while find() is
// in progress, then any returned value is not guaranteed to correspond
// to that key. This also applies if the key was not already present but
// once was. Elements can be found in any order.
TValue* find(std::uint64_t key)
{
auto idxEntry = get_entry_for_key(key);
if (idxEntry == nullptr)
return nullptr;
return idxEntry->value.load(std::memory_order_acquire);
}
// Thread-safe, but if somebody else can remove the same key while remove()
// is in progress, then any removed value is not guaranteed to correspond
// to that key This also applies if the key was not already present but
// once was. Elements can be removed in an order.
TValue* remove(std::uint64_t key)
{
auto idxEntry = get_entry_for_key(key);
if (idxEntry == nullptr)
return nullptr;
TValue* val = nullptr;
while (!idxEntry->value.compare_exchange_weak(val, nullptr, std::memory_order_acquire, std::memory_order_relaxed))
continue;
return val;
}
private:
struct IndexEntry
{
std::atomic<std::uint64_t> key;
std::atomic<TValue*> value;
};
struct IndexHeader
{
std::size_t capacity;
std::atomic<std::size_t> tail;
IndexEntry* entries;
IndexEntry** index;
IndexHeader* prev;
};
inline void insert_index_entry(IndexEntry*& idxEntry, std::uint64_t key)
{
auto localIndex = index.load(std::memory_order_relaxed); // We're the only writer thread, relaxed is OK
auto newTail = (localIndex->tail.load(std::memory_order_relaxed) + 1) & (localIndex->capacity - 1);
idxEntry = localIndex->index[newTail];
if (idxEntry->key.load(std::memory_order_relaxed) == INVALID_KEY ||
idxEntry->value.load(std::memory_order_relaxed) == nullptr) {
idxEntry->key.store(key, std::memory_order_relaxed);
localIndex->tail.store(newTail, std::memory_order_release);
return;
}
// No room in the old index, try to allocate another one!
new_index();
localIndex = index.load(std::memory_order_relaxed);
newTail = (localIndex->tail.load(std::memory_order_relaxed) + 1) & (localIndex->capacity - 1);
idxEntry = localIndex->index[newTail];
assert(idxEntry->key.load(std::memory_order_relaxed) == INVALID_KEY);
idxEntry->key.store(key, std::memory_order_relaxed);
localIndex->tail.store(newTail, std::memory_order_release);
}
inline IndexEntry* get_entry_for_key(std::uint64_t key) const
{
auto localIndex = index.load(std::memory_order_acquire);
auto tail = localIndex->tail.load(std::memory_order_acquire);
auto tailBase = localIndex->index[tail]->key.load(std::memory_order_relaxed);
if (tailBase == INVALID_KEY) {
return nullptr;
}
auto offset = static_cast<std::size_t>(key - tailBase);
std::size_t idx = (tail + offset) & (localIndex->capacity - 1);
auto entry = localIndex->index[idx];
return entry->key.load(std::memory_order_relaxed) == key ? entry : nullptr;
}
bool new_index()
{
auto prev = index.load(std::memory_order_relaxed);
std::size_t prevCapacity = prev == nullptr ? 0 : prev->capacity;
auto entryCount = prev == nullptr ? nextCapacity : prevCapacity;
auto raw = static_cast<char*>(corealgos_allocator::malloc(
sizeof(IndexHeader) +
std::alignment_of<IndexEntry>::value - 1 + sizeof(IndexEntry) * entryCount +
std::alignment_of<IndexEntry*>::value - 1 + sizeof(IndexEntry*) * nextCapacity));
if (raw == nullptr) {
return false;
}
auto header = new (raw) IndexHeader;
auto entries = reinterpret_cast<IndexEntry*>(details::align_for<IndexEntry>(raw + sizeof(IndexHeader)));
auto idx = reinterpret_cast<IndexEntry**>(details::align_for<IndexEntry*>(reinterpret_cast<char*>(entries) + sizeof(IndexEntry) * entryCount));
if (prev != nullptr) {
auto prevTail = prev->tail.load(std::memory_order_relaxed);
auto prevPos = prevTail;
std::size_t i = 0;
do {
prevPos = (prevPos + 1) & (prev->capacity - 1);
idx[i++] = prev->index[prevPos];
} while (prevPos != prevTail);
assert(i == prevCapacity);
}
for (std::size_t i = 0; i != entryCount; ++i) {
new (entries + i) IndexEntry;
entries[i].key.store(INVALID_KEY, std::memory_order_relaxed);
entries[i].value.store(nullptr, std::memory_order_relaxed);
idx[prevCapacity + i] = entries + i;
}
header->prev = prev;
header->entries = entries;
header->index = idx;
header->capacity = nextCapacity;
header->tail.store((prevCapacity - 1) & (nextCapacity - 1), std::memory_order_relaxed);
index.store(header, std::memory_order_release);
nextCapacity <<= 1;
return true;
}
private:
std::size_t nextCapacity;
std::atomic<IndexHeader*> index;
static const std::uint64_t INVALID_KEY = ~(std::uint64_t)0;
};
} }

View File

@@ -0,0 +1,867 @@
// ©2013-2014 Cameron Desrochers.
// Distributed under the simplified BSD license (see the LICENSE file that
// should have come with this file).
// Fuzz (random) tests for moodycamel::ConcurrentQueue
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <ctime>
#include <cassert>
#include <string>
#include <random>
#include <atomic>
#include <fstream>
#include <iomanip>
#include <vector>
#include <csignal>
#include <mutex>
#include <exception>
#include <cctype>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#include "../../concurrentqueue.h"
#include "../common/simplethread.h"
#include "../common/systemtime.h"
#include "../corealgos.h"
void failHook()
{
(void)1; // Attach debuggers here
}
#define _STR(x) #x
#define STR(x) _STR(x)
#define ASSERT_OR_FAIL_THREAD(cond) if (!(cond)) { const char* n = nullptr; failReason.compare_exchange_strong(n, "assertion failed on line " STR(__LINE__) ": " #cond, std::memory_order_relaxed, std::memory_order_relaxed); \
failed.store(true, std::memory_order_relaxed); failHook(); return; }
#define FAIL_IF_THREAD_TIMEOUT() if (getTimeDelta(startTime) > 60000) { const char* n = nullptr; failReason.compare_exchange_strong(n, "test timed out (detected on line " STR(__LINE__) ")", std::memory_order_relaxed, std::memory_order_relaxed); \
failed.store(true, std::memory_order_relaxed); failHook(); return; }
#define ASSERT_OR_FAIL(cond) if (!(cond)) { out_failReason = "assertion failed on line " STR(__LINE__) ": " #cond; result = false; failHook(); break; }
using namespace moodycamel;
typedef std::minstd_rand RNG_t;
enum test_type {
multithread_produce,
multithread_consume,
multithread_produce_and_consume,
completely_random,
// Core algo tests
core_add_only_list,
core_thread_local,
TEST_TYPE_COUNT
};
std::uint64_t test_count[TEST_TYPE_COUNT] = { 0 };
std::uint64_t fail_count[TEST_TYPE_COUNT] = { 0 };
const char* test_names[TEST_TYPE_COUNT] = {
"multithread_produce",
"multithread_consume",
"multithread_produce_and_consume",
"completely_random",
"core_add_only_list",
"core_thread_local",
};
const int SINGLE_SEED_ITERATIONS = 100;
const char* LOG_FILE = "fuzztests.log";
struct FuzzTraits : public ConcurrentQueueDefaultTraits
{
static const size_t BLOCK_SIZE = 8;
static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 4;
static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 4;
static const size_t INITIAL_IMPLCICIT_PRODUCER_HASH_SIZE = 1;
static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 24;
};
struct TestListItem : corealgos::ListItem
{
int value;
TestListItem() : value(0) { }
explicit TestListItem(int value) : value(value) { }
inline TestListItem* prev(std::memory_order order = std::memory_order_relaxed) const
{
return static_cast<TestListItem*>(concurrentListPrev.load(order));
}
};
bool run_test(uint64_t seed, int iterations, test_type& out_type, const char*& out_failReason)
{
bool result = true;
RNG_t baseRng((unsigned int)seed);
std::uniform_int_distribution<int> randTest(0, TEST_TYPE_COUNT - 1);
std::uniform_int_distribution<int> randInitialSize(0, 70);
auto type = static_cast<test_type>(randTest(baseRng));
out_type = type;
for (int iteration = 0; iteration != iterations; ++iteration) {
RNG_t rng(baseRng);
std::atomic<bool> failed(false);
std::atomic<const char*> failReason;
failReason = nullptr;
SystemTime startTime = getSystemTime();
switch (type) {
case multithread_produce:
{
const int countIncrement = std::uniform_int_distribution<int>(1, 1000)(rng);
int count = std::uniform_int_distribution<int>(0, 500)(rng) * countIncrement;
int prodCount = std::uniform_int_distribution<int>(0, 6)(rng);
bool useConsumerToken = static_cast<bool>(std::uniform_int_distribution<int>(0, 1)(rng));
ConcurrentQueue<int, FuzzTraits> q(randInitialSize(rng));
std::vector<SimpleThread> producers(prodCount);
std::vector<bool> useProducerToken(prodCount);
for (int i = 0; i != prodCount; ++i) {
useProducerToken[i] = static_cast<bool>(std::uniform_int_distribution<int>(0, 1)(rng));
producers[i] = SimpleThread([&](int i) {
ProducerToken t(q);
for (int j = 0; j != count && !failed.load(std::memory_order_relaxed); j += countIncrement) {
if (useProducerToken[i]) {
for (int k = 0; k != countIncrement; ++k) {
ASSERT_OR_FAIL_THREAD(q.enqueue(t, (i << 24) | (k + j)));
}
}
else {
for (int k = 0; k != countIncrement; ++k) {
ASSERT_OR_FAIL_THREAD(q.enqueue((i << 24) | (k + j)));
}
}
FAIL_IF_THREAD_TIMEOUT();
}
}, i);
}
SimpleThread consumer([&]() {
int item;
std::vector<int> lastItems(prodCount);
ConsumerToken t(q);
for (int i = 0; i != prodCount; ++i) {
lastItems[i] = -1;
}
for (int i = 0; i != count * prodCount && !failed.load(std::memory_order_relaxed);) {
if (useConsumerToken) {
for (int j = 0; j != 10000; ++j) {
if (q.try_dequeue(t, item)) {
++i;
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) < count);
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) == lastItems[item >> 24] + 1);
lastItems[item >> 24] = (item & 0xFFFFFF);
}
}
}
else {
for (int j = 0; j != 10000; ++j) {
if (q.try_dequeue(item)) {
++i;
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) < count);
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) == lastItems[item >> 24] + 1);
lastItems[item >> 24] = (item & 0xFFFFFF);
}
}
}
FAIL_IF_THREAD_TIMEOUT();
}
});
for (int i = 0; i != prodCount; ++i) {
producers[i].join();
}
consumer.join();
if (failed.load(std::memory_order_relaxed)) {
break;
}
int item;
ASSERT_OR_FAIL(!q.try_dequeue(item));
break;
}
case multithread_consume:
{
const int countIncrement = std::uniform_int_distribution<int>(1, 1000)(rng);
int count = std::uniform_int_distribution<int>(0, 500)(rng) * countIncrement;
int consCount = std::uniform_int_distribution<int>(0, 6)(rng);
bool useProducerToken = static_cast<bool>(std::uniform_int_distribution<int>(0, 1)(rng));
std::atomic<bool> producerDone(false);
ConcurrentQueue<int, FuzzTraits> q(randInitialSize(rng));
std::vector<SimpleThread> consumers(consCount);
std::vector<bool> useConsumerToken(consCount);
for (int i = 0; i != consCount; ++i) {
useConsumerToken[i] = static_cast<bool>(std::uniform_int_distribution<int>(0, 1)(rng));
consumers[i] = SimpleThread([&](int i) {
int item, lastItem = -1;
ConsumerToken t(q);
bool doneConsuming = false;
while (!doneConsuming && !failed.load(std::memory_order_relaxed)) {
auto producerDoneLocal = producerDone.load(std::memory_order_acquire);
if (useConsumerToken[i]) {
for (int j = 0; j != 10000; ++j) {
if (q.try_dequeue(t, item)) {
ASSERT_OR_FAIL_THREAD(item >= 0 && item < count * consCount && item > lastItem);
lastItem = item;
}
else if (producerDoneLocal) {
doneConsuming = true;
break;
}
}
}
else {
for (int j = 0; j != 10000; ++j) {
if (q.try_dequeue(item)) {
ASSERT_OR_FAIL_THREAD(item >= 0 && item < count * consCount && item > lastItem);
lastItem = item;
}
else if (producerDoneLocal) {
doneConsuming = true;
break;
}
}
}
FAIL_IF_THREAD_TIMEOUT();
}
}, i);
}
SimpleThread producer([&]() {
ProducerToken t(q);
for (int i = 0; i != count * consCount && !failed.load(std::memory_order_relaxed); i += countIncrement) {
if (useProducerToken) {
for (int j = 0; j != countIncrement; ++j) {
ASSERT_OR_FAIL_THREAD(q.enqueue(t, i + j));
}
}
else {
for (int j = 0; j != countIncrement; ++j) {
ASSERT_OR_FAIL_THREAD(q.enqueue(i + j));
}
}
FAIL_IF_THREAD_TIMEOUT();
}
producerDone.store(true, std::memory_order_release);
});
producer.join();
for (int i = 0; i != consCount; ++i) {
consumers[i].join();
}
if (failed.load(std::memory_order_relaxed)) {
break;
}
int item;
ASSERT_OR_FAIL(consCount == 0 || !q.try_dequeue(item));
break;
}
case multithread_produce_and_consume:
{
const int countIncrement = std::uniform_int_distribution<int>(1, 1000)(rng);
int count = std::uniform_int_distribution<int>(0, 500)(rng) * countIncrement;
int prodCount = std::uniform_int_distribution<int>(0, 6)(rng);
int consCount = std::uniform_int_distribution<int>(0, 6)(rng);
std::atomic<bool> producersDone(false);
ConcurrentQueue<int, FuzzTraits> q(randInitialSize(rng));
std::vector<SimpleThread> producers(prodCount);
std::vector<bool> useProducerToken(prodCount);
for (int i = 0; i != prodCount; ++i) {
useProducerToken[i] = static_cast<bool>(std::uniform_int_distribution<int>(0, 1)(rng));
producers[i] = SimpleThread([&](int i) {
ProducerToken t(q);
for (int j = 0; j != count && !failed.load(std::memory_order_relaxed); j += countIncrement) {
if (useProducerToken[i]) {
for (int k = 0; k != countIncrement; ++k) {
ASSERT_OR_FAIL_THREAD(q.enqueue(t, (i << 24) | (k + j)));
}
}
else {
for (int k = 0; k != countIncrement; ++k) {
ASSERT_OR_FAIL_THREAD(q.enqueue((i << 24) | (k + j)));
}
}
FAIL_IF_THREAD_TIMEOUT();
}
}, i);
}
std::vector<SimpleThread> consumers(consCount);
std::vector<bool> useConsumerToken(consCount);
for (int i = 0; i != consCount; ++i) {
useConsumerToken[i] = static_cast<bool>(std::uniform_int_distribution<int>(0, 1)(rng));
consumers[i] = SimpleThread([&](int i) {
int item;
std::vector<int> lastItems(prodCount);
ConsumerToken t(q);
for (int j = 0; j != prodCount; ++j) {
lastItems[j] = -1;
}
bool doneConsuming = false;
while (!doneConsuming && !failed.load(std::memory_order_relaxed)) {
auto producersDoneLocal = producersDone.load(std::memory_order_acquire);
if (useConsumerToken[i]) {
for (int j = 0; j != 10000; ++j) {
if (q.try_dequeue(t, item)) {
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) < count);
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) > lastItems[item >> 24]);
lastItems[item >> 24] = item & 0xFFFFFF;
}
else if (producersDoneLocal) {
doneConsuming = true;
break;
}
}
}
else {
for (int j = 0; j != 10000; ++j) {
if (q.try_dequeue(item)) {
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) < count);
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) > lastItems[item >> 24]);
lastItems[item >> 24] = item & 0xFFFFFF;
}
else if (producersDoneLocal) {
doneConsuming = true;
break;
}
}
}
FAIL_IF_THREAD_TIMEOUT();
}
}, i);
}
for (int i = 0; i != prodCount; ++i) {
producers[i].join();
}
producersDone.store(true, std::memory_order_release);
for (int i = 0; i != consCount; ++i) {
consumers[i].join();
}
if (failed.load(std::memory_order_relaxed)) {
break;
}
int item;
ASSERT_OR_FAIL(consCount == 0 || !q.try_dequeue(item));
break;
}
case completely_random:
{
int threadCount = std::uniform_int_distribution<int>(0, 32)(rng);
ConcurrentQueue<int, FuzzTraits> q(randInitialSize(rng));
std::vector<SimpleThread> threads(threadCount);
std::vector<unsigned int> seeds(threadCount);
std::vector<unsigned int> opCounts(threadCount);
unsigned int largestOpCount = 0;
for (int i = 0; i != threadCount; ++i) {
opCounts[i] = std::uniform_int_distribution<unsigned int>(0, 500000)(rng);
if (opCounts[i] > largestOpCount) {
largestOpCount = opCounts[i];
}
}
// Note: If you're wondering where all the memory goes, it's mostly here!
std::vector<unsigned int> itemStates(largestOpCount * threadCount * 2);
for (std::size_t j = 0; j != itemStates.size(); ++j) {
itemStates[j] = 0;
}
for (int i = 0; i != threadCount; ++i) {
seeds[i] = std::uniform_int_distribution<unsigned int>(0, 0xFFFFFFFF)(rng);
threads[i] = SimpleThread([&](int i) {
RNG_t rng((unsigned int)seeds[i]);
ConsumerToken ct(q);
ProducerToken pt(q);
int item;
int opCount = opCounts[i];
std::vector<int> lastItems(threadCount * 2); // * 2 because there's two producer queues per thread (one implicit, one explicit)
for (int j = 0; j != threadCount * 2; ++j) {
lastItems[j] = -1;
}
for (int j = 0; j < opCount && !failed.load(std::memory_order_relaxed); ++j) {
int op = std::uniform_int_distribution<int>(0, 7)(rng);
unsigned int* state;
switch (op) {
case 0:
state = &itemStates[(i * 2) * largestOpCount + j];
ASSERT_OR_FAIL_THREAD(*state == 0);
*state = 1;
ASSERT_OR_FAIL_THREAD(q.enqueue(pt, ((i * 2) << 24) | j));
break;
case 1:
state = &itemStates[(i * 2 + 1) * largestOpCount + j];
ASSERT_OR_FAIL_THREAD(*state == 0);
*state = 1;
ASSERT_OR_FAIL_THREAD(q.enqueue(((i * 2 + 1) << 24) | j));
break;
case 2:
if (q.try_dequeue(ct, item)) {
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) >= 0 && (item & 0xFFFFFF) < (int)largestOpCount);
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) > lastItems[item >> 24]);
lastItems[item >> 24] = item & 0xFFFFFF;
state = &itemStates[(item >> 24) * largestOpCount + (item & 0xFFFFFF)];
ASSERT_OR_FAIL_THREAD(*state == 1);
*state = 2;
}
break;
case 3:
if (q.try_dequeue(item)) {
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) >= 0 && (item & 0xFFFFFF) < (int)largestOpCount);
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) > lastItems[item >> 24]);
lastItems[item >> 24] = item & 0xFFFFFF;
state = &itemStates[(item >> 24) * largestOpCount + (item & 0xFFFFFF)];
ASSERT_OR_FAIL_THREAD(*state == 1);
*state = 2;
}
break;
case 4:
case 5: {
std::vector<int> bulkData(std::min(opCount - j, std::uniform_int_distribution<int>(0, 1024)(rng)));
for (std::size_t k = 0; k != bulkData.size(); ++k) {
state = &itemStates[(i * 2 + op - 4) * largestOpCount + j + k];
ASSERT_OR_FAIL_THREAD(*state == 0);
*state = 1;
bulkData[k] = ((i * 2 + op - 4) << 24) | (j + (int)k);
}
if (op == 4) {
ASSERT_OR_FAIL_THREAD(q.enqueue_bulk(pt, bulkData.begin(), bulkData.size()));
}
else {
ASSERT_OR_FAIL_THREAD(q.enqueue_bulk(bulkData.begin(), bulkData.size()));
}
j += (int)bulkData.size() - 1;
break;
}
case 6:
case 7: {
std::vector<int> bulkData(std::min(opCount - j, std::uniform_int_distribution<int>(0, 1024)(rng)));
std::size_t count = 0;
if (op == 6) {
count = q.try_dequeue_bulk(ct, bulkData.begin(), bulkData.size());
}
else {
count = q.try_dequeue_bulk(bulkData.begin(), bulkData.size());
}
for (std::size_t k = 0; k != count; ++k) {
item = bulkData[k];
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) >= 0 && (item & 0xFFFFFF) < (int)largestOpCount);
ASSERT_OR_FAIL_THREAD((item & 0xFFFFFF) > lastItems[item >> 24]);
lastItems[item >> 24] = item & 0xFFFFFF;
state = &itemStates[(item >> 24) * largestOpCount + (item & 0xFFFFFF)];
ASSERT_OR_FAIL_THREAD(*state == 1);
*state = 2;
}
if (count > 0) {
j += (int)count - 1;
}
break;
}
default:
assert(false);
}
FAIL_IF_THREAD_TIMEOUT();
}
}, i);
}
for (int i = 0; i != threadCount; ++i) {
threads[i].join();
}
#if MCDBGQ_TRACKMEM
auto stats = q.getMemStats(); // Make available under debugger
((void)stats);
#endif
int item;
while (q.try_dequeue(item)) {
unsigned int* state = &itemStates[(item >> 24) * largestOpCount + (item & 0xFFFFFF)];
ASSERT_OR_FAIL(*state == 1);
*state = 2;
}
for (std::size_t j = 0; j != itemStates.size(); ++j) {
ASSERT_OR_FAIL(itemStates[j] == 0 || itemStates[j] == 2);
}
if (failed.load(std::memory_order_relaxed)) {
break;
}
break;
}
case core_add_only_list:
{
int threadCount = std::uniform_int_distribution<int>(0, 48)(rng);
std::vector<SimpleThread> threads(threadCount);
std::vector<int> opCounts(threadCount);
for (int i = 0; i != threadCount; ++i) {
opCounts[i] = std::uniform_int_distribution<int>(0, 500000)(rng);
}
std::size_t expectedMemUsage = 0;
for (int i = 0; i != threadCount; ++i) {
expectedMemUsage += opCounts[i] * sizeof(TestListItem);
}
corealgos::ConcurrentAddOnlyList<TestListItem> list;
for (int i = 0; i != threadCount; ++i) {
threads[i] = SimpleThread([&](int tid) {
auto temp = expectedMemUsage;
((void)temp);
int opCount = opCounts[tid];
for (int j = 0; j != opCount; ++j) {
list.add(new TestListItem((tid << 24) | j));
}
}, i);
}
for (int i = 0; i != threadCount; ++i) {
threads[i].join();
}
std::vector<int> lastItems(threadCount);
for (int i = 0; i != threadCount; ++i) {
lastItems[i] = opCounts[i];
}
auto tail = list.tail();
while (tail != nullptr) {
auto tid = tail->value >> 24;
ASSERT_OR_FAIL(lastItems[tid] - 1 == (tail->value & 0xFFFFFF));
--lastItems[tid];
auto next = tail->prev();
delete tail;
tail = next;
}
break;
}
case core_thread_local:
{
int threadCount = std::uniform_int_distribution<int>(32, 256)(rng);
std::vector<SimpleThread> threads(threadCount);
std::vector<int> opCounts(threadCount);
std::vector<int*> localData(threadCount);
for (int i = 0; i != threadCount; ++i) {
opCounts[i] = std::uniform_int_distribution<int>(10000, 250000)(rng);
}
corealgos::ThreadLocal<TestListItem> tls(1);
for (int i = 0; i != threadCount; ++i) {
threads[i] = SimpleThread([&](int tid) {
auto p = tls.get_or_create();
ASSERT_OR_FAIL_THREAD(p->value == 0);
p->value = tid;
localData[tid] = &p->value;
int opCount = opCounts[tid];
for (int j = 0; j != opCount; ++j) {
auto q = tls.get_or_create();
ASSERT_OR_FAIL_THREAD(q == p);
ASSERT_OR_FAIL_THREAD(q->value == tid);
FAIL_IF_THREAD_TIMEOUT();
}
}, i);
}
for (int i = 0; i != threadCount; ++i) {
threads[i].join();
}
for (int i = 0; i != threadCount; ++i) {
ASSERT_OR_FAIL(localData[i] != nullptr);
ASSERT_OR_FAIL(*localData[i] == i);
}
break;
}
default:
assert(false);
}
++test_count[type];
if (failed.load(std::memory_order_relaxed)) {
out_failReason = failReason.load(std::memory_order_relaxed);
result = false;
}
if (!result) {
++fail_count[type];
break;
}
}
return result;
}
static const char* timestamp()
{
static char buf[32];
time_t time = std::time(NULL);
strcpy(buf, std::asctime(std::localtime(&time)));
buf[strlen(buf) - 1] = '\0'; // Remove trailing newline
return buf;
}
extern "C" { typedef void (*signal_handler_t)(int); }
static std::atomic<std::uint64_t> g_seed(0);
static std::atomic_flag reported_signal_error = ATOMIC_FLAG_INIT;
static std::atomic<signal_handler_t> g_prev_sigsegv(nullptr);
static std::atomic<signal_handler_t> g_prev_sigabrt(nullptr);
static std::mutex g_signal_handler_mutex;
void on_signal(int signal)
{
if (reported_signal_error.test_and_set()) {
return;
}
std::unique_lock<std::mutex> lock(g_signal_handler_mutex);
auto seed = g_seed.load(std::memory_order_acquire);
// Technically undefined behaviour to use stdlib functions,
// but oh well
const char* error = signal == SIGABRT ?
"Abort detected (assertion failed?)" :
"Segmentation fault detected!";
{
std::ofstream fout(LOG_FILE, std::ios::app);
fout << "*** " << error << "\n Seed: " << std::hex << seed << std::endl;
}
std::printf("*** %s\n Seed: %08x%08x\n", error, (uint32_t)(seed >> 32), (uint32_t)(seed));
std::fflush(stdout);
}
extern "C" void signal_handler(int signal)
{
on_signal(signal);
if (signal_handler_t handler_fn = g_prev_sigsegv.load(std::memory_order_relaxed)) {
handler_fn(signal);
}
else {
std::exit(signal);
}
}
#ifdef _WIN32
LONG CALLBACK se_handler(PEXCEPTION_POINTERS info)
{
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
on_signal(SIGSEGV);
}
return EXCEPTION_CONTINUE_SEARCH;
}
#endif
int main(int argc, char** argv)
{
bool singleSeed = false;
uint64_t seed = 0;
// Disable buffering (so that when run in, e.g., Sublime Text, the output appears as it is written)
std::setvbuf(stdout, nullptr, _IONBF, 0);
// Isolate the executable name
std::string progName = argv[0];
auto slash = progName.find_last_of("/\\");
if (slash != std::string::npos) {
progName = progName.substr(slash + 1);
}
// Parse command line options
if (argc > 1) {
bool printHelp = false;
bool error = false;
for (int i = 1; i < argc; ++i) {
if (std::strcmp(argv[i], "--help") == 0) {
printHelp = true;
}
else if (std::strcmp(argv[i], "--seed") == 0) {
if (i + 1 == argc || argv[i + 1][0] == '-') {
std::printf("Expected seed number argument for --seed option.\n");
error = true;
continue;
}
++i;
seed = 0;
// hex
for (int j = 0; argv[i][j] != '\0'; ++j) {
char ch = static_cast<char>(std::tolower(argv[i][j]));
if (j == 1 && seed == 0 && ch == 'x') {
continue; // Skip 0x, if any
}
else if (ch >= 'a' && ch <= 'f') {
seed = (seed << 4) | (10 + ch - 'a');
}
else if (ch >= '0' && ch <= '9') {
seed = (seed << 4) | (ch - '0');
}
else {
std::printf("Expected hex seed argument, found '%s' instead\n", argv[i]);
error = true;
}
}
singleSeed = true;
}
else {
std::printf("Unrecognized option '%s'.\n\n", argv[i]);
error = true;
}
}
if (error || printHelp) {
std::printf("%s\n Description: Runs fuzz tests (randomized stability tests) for moodycamel::ConcurrentQueue\n", progName.c_str());
std::printf(" An infinite series of random tests are run, each with a different seed.\nIf a test fails, the seed for that test is reported.\n");
std::printf(" --help Prints this help blurb\n");
std::printf(" --seed N Runs one test with the given seed\n");
return error ? -1 : 0;
}
}
{
bool logExists = true;
{
std::ifstream fin(LOG_FILE);
if (!fin) {
logExists = false;
}
}
std::ofstream fout(LOG_FILE, std::ios::app);
if (logExists) {
fout << "\n\n";
}
if (singleSeed) {
std::printf("Running %d iterations of single test with seed %08x%08x.\n\n", SINGLE_SEED_ITERATIONS, (uint32_t)(seed >> 32), (uint32_t)(seed));
fout << "--- New run (" << timestamp() << "): Executing " << SINGLE_SEED_ITERATIONS << " iterations of a single test with seed " << std::hex << seed << " ---" << std::endl;
}
else {
std::printf("Running random fuzz tests for moodycamel::ConcurrentQueue.\n");
std::printf("Press CTRL+C to exit.\n");
std::printf("(Run %s --help for options.)\n\n", progName.c_str());
fout << "--- New run (" << timestamp() << "): Executing random fuzz tests ---" << std::endl;
}
}
int exitCode = 0;
test_type test;
const char* failReason;
if (singleSeed) {
if (!run_test(seed, SINGLE_SEED_ITERATIONS, test, failReason)) {
exitCode = 1;
std::ofstream fout(LOG_FILE, std::ios::app);
fout << test_names[test] << " failed: " << failReason << std::endl;
std::printf(" %s failed: %s\n", test_names[test], failReason);
}
else {
std::ofstream fout(LOG_FILE, std::ios::app);
fout << test_names[test] << " succeeded!" << std::endl;
std::printf(" %s succeeded!\n", test_names[test]);
}
}
else {
#ifdef _WIN32
AddVectoredExceptionHandler(1 /* first? */, &se_handler);
#endif
uint32_t iteration = 0;
while (true) {
seed = (static_cast<uint64_t>(std::time(NULL)) << 32) | iteration++;
// MurmurHash3 64-bit finalizer
seed ^= seed >> 33;
seed *= 0xff51afd7ed558ccd;
seed ^= seed >> 33;
seed *= 0xc4ceb9fe1a85ec53;
g_seed.store(seed, std::memory_order_release);
std::signal(SIGSEGV, signal_handler);
std::signal(SIGABRT, signal_handler);
bool result;
try {
result = run_test(seed, 2, test, failReason);
}
catch (std::exception const& e) {
std::ofstream fout(LOG_FILE, std::ios::app);
fout << "*** Exception thrown: " << e.what() << "\n Seed: " << std::hex << seed << "\n Test: " << test_names[test] << std::endl;
std::printf("*** Exception thrown: %s\n Seed: %08x%08x\n Test: %s\n\n", e.what(), (uint32_t)(seed >> 32), (uint32_t)(seed), test_names[test]);
std::exit(2); // There shouldn't be any exceptions!
}
catch (...) {
std::ofstream fout(LOG_FILE, std::ios::app);
fout << "*** Unknown exception thrown!\n Seed: " << std::hex << seed << "\n Test: " << test_names[test] << std::endl;
std::printf("*** Unknown exception thrown!\n Seed: %08x%08x\n Test: %s\n\n", (uint32_t)(seed >> 32), (uint32_t)(seed), test_names[test]);
std::exit(2);
}
std::signal(SIGSEGV, SIG_DFL);
std::signal(SIGABRT, SIG_DFL);
if (!result) {
exitCode = 1;
std::ofstream fout(LOG_FILE, std::ios::app);
fout << "*** Failure detected!\n Seed: " << std::hex << seed << "\n Test: " << test_names[test] << "\n Reason: " << failReason << std::endl;
std::printf("*** Failure detected!\n Seed: %08x%08x\n Test: %s\n Reason: %s\n", (uint32_t)(seed >> 32), (uint32_t)(seed), test_names[test], failReason);
}
if ((iteration & 31) == 0) {
std::uint64_t total = 0;
char breakdown[128 * TEST_TYPE_COUNT];
char* ptr = breakdown;
for (int i = 0; i != TEST_TYPE_COUNT; ++i) {
std::sprintf(ptr, " %s: %llu successful, %llu failed\n", test_names[i], (unsigned long long)(test_count[i] - fail_count[i]), (unsigned long long)fail_count[i]);
ptr += std::strlen(ptr);
total += test_count[i];
}
std::ofstream fout(LOG_FILE, std::ios::app);
fout << "Executed " << total << " tests so far:\n" << breakdown;
std::printf("Executed %llu tests so far:\n%s", (unsigned long long)total, breakdown);
}
}
}
return exitCode;
}

View File

@@ -0,0 +1,9 @@
# ©2013-2014 Cameron Desrochers
include ../../build/makefile.inc
default:
$(MAKE) -C ../../build bin/fuzztests$(EXT)
run: default
../../build/bin/fuzztests$(EXT)

View File

@@ -0,0 +1,200 @@
// ©2014 Cameron Desrochers
#include "relacy/relacy/relacy_std.hpp"
template <typename N>
struct FreeListNode
{
FreeListNode() : freeListRefs(0), freeListNext(nullptr) { }
std::atomic<std::uint32_t> freeListRefs;
std::atomic<N*> freeListNext;
};
// A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention,
// but simple and correct (assuming nodes are never freed until after the free list is destroyed),
// and fairly speedy under low contention.
template<typename N> // N must inherit FreeListNode or have the same fields (and initialization)
struct FreeList
{
FreeList() : freeListHead(nullptr) { }
inline void add(N* node)
{
// We know that the should-be-on-freelist bit is 0 at this point, so it's safe to
// set it using a fetch_add
if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) {
// Oh look! We were the last ones referencing this node, and we know
// we want to add it to the free list, so let's do it!
add_knowing_refcount_is_zero(node);
}
}
inline N* try_get()
{
auto head = freeListHead.load(std::memory_order_acquire);
while (head != nullptr) {
auto prevHead = head;
auto refs = head->freeListRefs.load(std::memory_order_relaxed);
if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1,
std::memory_order_acquire, std::memory_order_relaxed)) {
head = freeListHead.load(std::memory_order_acquire);
continue;
}
// Good, reference count has been incremented (it wasn't at zero), which means
// we can read the next and not worry about it changing between now and the time
// we do the CAS
auto next = head->freeListNext.load(std::memory_order_relaxed);
if (freeListHead.compare_exchange_strong(head, next,
std::memory_order_acquire, std::memory_order_relaxed)) {
// Yay, got the node. This means it was on the list, which means
// shouldBeOnFreeList must be false no matter the refcount (because
// nobody else knows it's been taken off yet, it can't have been put back on).
RL_ASSERT((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0);
// Decrease refcount twice, once for our ref, and once for the list's ref
head->freeListRefs.fetch_add(-2, std::memory_order_release);
return head;
}
// OK, the head must have changed on us, but we still need to decrease the refcount we
// increased.
// Note that we don't need to release any memory effects, but we do need to ensure that the reference
// count decrement happens-after the CAS on the head.
refs = prevHead->freeListRefs.fetch_add(-1, std::memory_order_acq_rel);
if (refs == SHOULD_BE_ON_FREELIST + 1) {
add_knowing_refcount_is_zero(prevHead);
}
}
return nullptr;
}
// Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes)
N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); }
private:
inline void add_knowing_refcount_is_zero(N* node)
{
// Since the refcount is zero, and nobody can increase it once it's zero (except us, and we
// run only one copy of this method per node at a time, i.e. the single thread case), then we
// know we can safely change the next pointer of the node; however, once the refcount is back
// above zero, then other threads could increase it (happens under heavy contention, when the
// refcount goes to zero in between a load and a refcount increment of a node in try_get, then
// back up to something non-zero, then the refcount increment is done by the other thread) --
// so, if the CAS to add the node to the actual list fails, decrease the refcount and leave
// the add operation to the next thread who puts the refcount back at zero (which could be us,
// hence the loop).
auto head = freeListHead.load(std::memory_order_relaxed);
while (true) {
node->freeListNext.store(head, std::memory_order_relaxed);
node->freeListRefs.store(1, std::memory_order_release);
if (!freeListHead.compare_exchange_strong(head, node,
std::memory_order_release, std::memory_order_relaxed)) {
// Hmm, the add failed, but we can only try again when the refcount goes back to zero
if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) {
continue;
}
}
return;
}
}
private:
static const std::uint32_t REFS_MASK = 0x7FFFFFFF;
static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000;
// Implemented like a stack, but where node order doesn't matter (nodes are
// inserted out of order under contention)
std::atomic<N*> freeListHead;
};
struct TestNode : FreeListNode<TestNode>
{
int value;
TestNode() { }
explicit TestNode(int value) : value(value) { }
};
struct basic_test : rl::test_suite<basic_test, 2>
{
FreeList<TestNode> freeList;
TestNode initialNodes[2];
void before()
{
}
void thread(unsigned int tid)
{
TestNode* node = &initialNodes[tid];
node->value = tid;
freeList.add(node);
node = freeList.try_get();
if (node != nullptr) {
freeList.add(node);
}
}
void after()
{
}
void invariant()
{
}
};
struct full_test : rl::test_suite<full_test, 4>
{
FreeList<TestNode> freeList;
TestNode initialNodes[6];
void before()
{
}
void thread(unsigned int tid)
{
TestNode* node;
int myNodeCount = tid >= 4 ? 2 : 1;
for (int i = 0; i != myNodeCount; ++i) {
node = &initialNodes[tid + (tid >= 5 ? 1 : 0) + i];
node->value = tid;
freeList.add(node);
}
for (int i = 0; i != 3; ++i) {
node = freeList.try_get();
if (node != nullptr) {
freeList.add(node);
}
}
}
void after()
{
}
void invariant()
{
}
};
int main()
{
rl::test_params params;
//params.search_type = rl::sched_full;
//params.iteration_count = 100000000;
params.search_type = rl::sched_random;
params.iteration_count = 1000000;
rl::simulate<basic_test>(params);
rl::simulate<full_test>(params);
return 0;
}

View File

@@ -0,0 +1,722 @@
// ©2015 Cameron Desrochers
// Tests various parts of the queue using the actual
// full implementation itself, instead of isolated
// components. This is much slower, but provides much
// better coverage too.
#define MCDBGQ_USE_RELACY
#include "../../concurrentqueue.h"
#include <string>
using namespace moodycamel;
struct SmallConstantTraits : public ConcurrentQueueDefaultTraits
{
static const size_t BLOCK_SIZE = 2;
static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 2;
static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 2;
static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 1;
static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 2;
};
struct MediumConstantTraits : public ConcurrentQueueDefaultTraits
{
static const size_t BLOCK_SIZE = 4;
static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 2;
static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 4;
static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 2;
static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 4;
};
struct Foo {
static int& ctorCount() { static int c; return c; }
static int& dtorCount() { static int c; return c; }
static void reset() { ctorCount() = 0; dtorCount() = 0; }
Foo()
: id(-2)
{
++ctorCount();
}
Foo(int id)
: id(id)
{
++ctorCount();
}
Foo(Foo const& o)
: id(o.id)
{
++ctorCount();
}
~Foo()
{
RL_ASSERT(id != -1);
++dtorCount();
id = -1;
}
public:
int id;
};
struct enqueue_explicit_one : rl::test_suite<enqueue_explicit_one, 2>
{
ConcurrentQueue<int, SmallConstantTraits> q;
void before()
{
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
ProducerToken t(q);
q.enqueue(t, tid);
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int tid0, tid1;
RL_ASSERT(q.try_dequeue(tid0));
RL_ASSERT(tid0 == 0 || tid0 == 1);
RL_ASSERT(q.try_dequeue(tid1));
RL_ASSERT(tid1 == 0 || tid1 == 1);
RL_ASSERT(tid0 != tid1);
RL_ASSERT(!q.try_dequeue(tid0));
}
void invariant()
{
}
};
struct enqueue_explicit_many : rl::test_suite<enqueue_explicit_many, 3>
{
ConcurrentQueue<int, SmallConstantTraits> q;
void before()
{
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
ProducerToken t(q);
for (int i = 0; i != 5; ++i) {
q.enqueue(t, tid * 10 + i);
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int item;
for (int i = 0; i != 15; ++i) {
RL_ASSERT(q.try_dequeue(item));
}
RL_ASSERT(!q.try_dequeue(item));
}
void invariant()
{
}
};
// This one caught a bug with the memory ordering in the core dequeue algorithm
struct dequeue_some_explicit : rl::test_suite<dequeue_some_explicit, 3>
{
ConcurrentQueue<int, SmallConstantTraits> q;
void before()
{
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
if (tid <= 1) {
int item;
ConsumerToken t(q);
for (int i = 0; i != 5; ++i) {
q.try_dequeue(t, item);
}
}
else {
ProducerToken t(q);
for (int i = 0; i != 3; ++i) {
q.enqueue(t, tid * 10 + i);
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
}
void invariant()
{
}
};
// Causes blocks to be reused
struct recycle_blocks_explicit : rl::test_suite<recycle_blocks_explicit, 3>
{
ConcurrentQueue<int, SmallConstantTraits> q;
std::vector<bool> seen;
void before()
{
seen.resize(8, false);
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
if (tid == 0) {
ProducerToken t(q);
for (int i = 0; i != 8; ++i) {
q.enqueue(t, i);
}
}
else {
int item;
ConsumerToken t(q);
for (int i = 0; i != 6; ++i) {
if (q.try_dequeue(t, item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int item;
while (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
for (auto s : seen) {
RL_ASSERT(s);
}
}
void invariant()
{
}
};
// Causes the explicit producer's block index to expand
struct expand_block_index_explicit : rl::test_suite<expand_block_index_explicit, 4>
{
ConcurrentQueue<int, SmallConstantTraits> q;
std::vector<bool> seen;
void before()
{
seen.resize(12, false);
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
if (tid == 0) {
ProducerToken t(q);
for (int i = 0; i != 12; ++i) {
q.enqueue(t, i);
}
}
else {
int item;
ConsumerToken t(q);
for (int i = 0; i != 3; ++i) {
if (q.try_dequeue(t, item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int item;
while (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
for (auto s : seen) {
RL_ASSERT(s);
}
}
void invariant()
{
}
};
// Tests that implicit producers work at a very basic level
struct enqueue_implicit_one : rl::test_suite<enqueue_implicit_one, 2>
{
ConcurrentQueue<int, SmallConstantTraits> q;
void before()
{
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
q.enqueue(tid);
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int tid0, tid1;
RL_ASSERT(q.try_dequeue(tid0));
RL_ASSERT(tid0 == 0 || tid0 == 1);
RL_ASSERT(q.try_dequeue(tid1));
RL_ASSERT(tid1 == 0 || tid1 == 1);
RL_ASSERT(tid0 != tid1);
RL_ASSERT(!q.try_dequeue(tid0));
}
void invariant()
{
}
};
// Tests implicit producer at a simple level
struct implicit_simple : rl::test_suite<implicit_simple, 3>
{
ConcurrentQueue<int, SmallConstantTraits> q;
std::vector<bool> seen;
void before()
{
seen.resize(5, false);
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
if (tid == 0) {
for (int i = 0; i != 5; ++i) {
q.enqueue(i);
}
}
else {
int item;
for (int i = 0; i != 3; ++i) {
if (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int item;
while (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
for (auto s : seen) {
RL_ASSERT(s);
}
}
void invariant()
{
}
};
// Tests multiple implicit producers being created (stresses the implicit producer hash map)
struct many_implicit_producers : rl::test_suite<many_implicit_producers, 6>
{
ConcurrentQueue<int, SmallConstantTraits> q;
std::vector<bool> seen;
void before()
{
seen.resize(18, false);
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
q.enqueue(tid * 3 + 0);
q.enqueue(tid * 3 + 1);
q.enqueue(tid * 3 + 2);
int item;
for (int i = 0; i != 2; ++i) {
if (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int item;
while (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
for (auto s : seen) {
RL_ASSERT(s);
}
}
void invariant()
{
}
};
// Tests multiple implicit producers being created (stresses the implicit producer hash map)
struct implicit_producer_reuse : rl::test_suite<implicit_producer_reuse, 9>
{
ConcurrentQueue<int, SmallConstantTraits> q;
std::vector<bool> seen;
void before()
{
seen.resize(9, false);
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
q.enqueue(tid);
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int item;
while (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
for (auto s : seen) {
RL_ASSERT(s);
}
}
void invariant()
{
}
};
// Tests implicit producer block recycling
struct implicit_block_reuse : rl::test_suite<implicit_block_reuse, 4>
{
ConcurrentQueue<int, SmallConstantTraits> q;
std::vector<bool> seen;
void before()
{
seen.resize(28, false);
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
for (int i = 0; i != 7; ++i) {
q.enqueue(tid * 7 + i);
}
int item;
ConsumerToken t(q);
for (int i = 0; i != 7; ++i) {
if (q.try_dequeue(t, item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int item;
while (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
for (auto s : seen) {
RL_ASSERT(s);
}
}
void invariant()
{
}
};
// Tests consumption from mixed producers
struct mixed : rl::test_suite<mixed, 4>
{
ConcurrentQueue<int, SmallConstantTraits> q;
std::vector<bool> seen;
void before()
{
seen.resize(28, false);
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
if (tid <= 1) {
for (int i = 0; i != 7; ++i) {
q.enqueue(tid * 7 + i);
}
}
else {
ProducerToken t(q);
for (int i = 0; i != 7; ++i) {
q.enqueue(t, tid * 7 + i);
}
}
int item;
if (tid & 1) {
for (int i = 0; i != 4; ++i) {
if (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
}
}
else {
ConsumerToken t(q);
for (int i = 0; i != 4; ++i) {
if (q.try_dequeue(t, item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int item;
while (q.try_dequeue(item)) {
RL_ASSERT(!seen[item]);
seen[item] = true;
}
for (auto s : seen) {
RL_ASSERT(s);
}
}
void invariant()
{
}
};
// Test leftovers are being properly destroyed
struct leftovers_destroyed_explicit : rl::test_suite<leftovers_destroyed_explicit, 3>
{
ConcurrentQueue<Foo, MediumConstantTraits>* q;
std::vector<bool> seen;
void before()
{
seen.resize(rl::rand(32), false);
q = new ConcurrentQueue<Foo, MediumConstantTraits>();
Foo::reset();
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
if (tid == 0) {
ProducerToken t(*q);
for (int i = 0; i != (int)seen.size(); ++i) {
q->enqueue(t, Foo(i));
}
}
else {
Foo item;
ConsumerToken t(*q);
for (int i = rl::rand(17); i > 0; --i) {
if (q->try_dequeue(t, item)) {
RL_ASSERT(!seen[item.id]);
seen[item.id] = true;
}
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int seenCount = 0;
{
for (auto s : seen) {
if (s) {
++seenCount;
}
}
}
RL_ASSERT(Foo::ctorCount() == seen.size() * 2 + 2);
RL_ASSERT(Foo::dtorCount() == seen.size() + seenCount + 2);
delete q;
RL_ASSERT(Foo::ctorCount() == seen.size() * 2 + 2);
RL_ASSERT(Foo::ctorCount() == Foo::dtorCount());
}
void invariant()
{
}
};
// implicit
struct leftovers_destroyed_implicit : rl::test_suite<leftovers_destroyed_implicit, 3>
{
ConcurrentQueue<Foo, MediumConstantTraits>* q;
std::vector<bool> seen;
void before()
{
seen.resize(rl::rand(32), false);
q = new ConcurrentQueue<Foo, MediumConstantTraits>();
Foo::reset();
}
void thread(unsigned int tid)
{
RelacyThreadExitNotifier::notify_relacy_thread_start();
if (tid == 0) {
for (int i = 0; i != (int)seen.size(); ++i) {
q->enqueue(Foo(i));
}
}
else {
Foo item;
for (int i = rl::rand(17); i > 0; --i) {
if (q->try_dequeue(item)) {
RL_ASSERT(!seen[item.id]);
seen[item.id] = true;
}
}
}
RelacyThreadExitNotifier::notify_relacy_thread_exit();
}
void after()
{
int seenCount = 0;
{
for (auto s : seen) {
if (s) {
++seenCount;
}
}
}
RL_ASSERT(Foo::ctorCount() == seen.size() * 2 + 2);
RL_ASSERT(Foo::dtorCount() == seen.size() + seenCount + 2);
delete q;
RL_ASSERT(Foo::ctorCount() == seen.size() * 2 + 2);
RL_ASSERT(Foo::ctorCount() == Foo::dtorCount());
}
void invariant()
{
}
};
template<typename TTest>
void simulate(int iterations)
{
// Note: There's no point using the full search params
// Even with the simple enqueue_explicit_one test, it
// would take a few millenia to complete(!)
//rl::test_params fullParams;
//fullParams.search_type = rl::sched_full;
rl::test_params randomParams;
randomParams.search_type = rl::sched_random;
randomParams.iteration_count = iterations;
rl::simulate<TTest>(randomParams);
}
int main()
{
simulate<enqueue_explicit_one>(1000000);
simulate<enqueue_explicit_many>(1000000);
simulate<dequeue_some_explicit>(1000000);
simulate<recycle_blocks_explicit>(1000000);
simulate<expand_block_index_explicit>(1000000);
simulate<enqueue_implicit_one>(1000000);
simulate<implicit_simple>(1000000);
simulate<many_implicit_producers>(500000);
simulate<implicit_producer_reuse>(1000000);
simulate<implicit_block_reuse>(1000000);
simulate<mixed>(1000000);
simulate<leftovers_destroyed_explicit>(1000000);
simulate<leftovers_destroyed_implicit>(1000000);
return 0;
}

View File

@@ -0,0 +1,29 @@
# ©2014-2015 Cameron Desrochers
include ../../build/makefile.inc
ifeq ($(OS),Windows_NT)
PLATFORM_OPTS = -static
else
PLATFORM_OPTS = -lrt
endif
OPTS = -Wno-int-to-pointer-cast -pthread $(PLATFORM_OPTS) -O3 -g
default: freelist$(EXT) spmchash$(EXT) integrated$(EXT)
freelist$(EXT): makefile freelist.cpp
g++ -std=c++11 -fpermissive $(OPTS) freelist.cpp -o freelist$(EXT)
spmchash$(EXT): makefile spmchash.cpp
g++ -std=c++11 -fpermissive $(OPTS) spmchash.cpp -o spmchash$(EXT)
integrated$(EXT): makefile integrated.cpp ../../concurrentqueue.h relacy_shims.h
g++ -std=c++11 -fpermissive $(OPTS) -Irelacy -I. integrated.cpp -o integrated$(EXT)
#run: freelist$(EXT)
# ./freelist$(EXT)
#run: spmchash$(EXT)
# ./spmchash$(EXT)
run: integrated$(EXT)
./integrated$(EXT)

View File

@@ -0,0 +1,25 @@
Version 2.4
Features:
+ Support for futex(FUTEX_WAIT/FUTEX_WAKE)
+ Linux/Darwin performance improved (2.5x for Linux, 7x for Darwin)
Fixes:
+ Fixed a bunch of issues with WaitForMultipleObjects()/SignalObjectAndWait()
+ Fixed rare spurious memory leak reports related to test progress reporting
Version 2.3
Features:
+ Support for FlushProcessWriteBuffers()
Version 2.2
Features:
+ Support for pthread_mutex_timedlock()
+ Support for ETIMEDOUT, EINTR in pthread_cond_timedwait()/pthread_cond_wait()
+ rl::hash_ptr(p, sz) function which provides deterministic hashing of pointers
Fixes:
+ Win32 mutex is now recursive
+ Compilation issue on MSVC x64 when RL_DEBUGBREAK_ON_ASSERT/RL_DEBUGBREAK_ON_FAILURE defined
+ Fixed OOM crash when execution history is very large
+ Fixed rare crash during iteration count estimation in context bound scheduler
+ Fixed bug in pthread_rwlock/SRWLOCK that at most 2 readers may acquire it simultaneously
+ Fixed bug regarding false race detection when simulation runs for very long time (int overflow)

View File

@@ -0,0 +1,19 @@
Relacy Race Detector
Copyright (c) 2008-2013, Dmitry S. Vyukov
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- 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.
- The name of the owner may not be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE OWNER "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 OWNER 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.

View File

@@ -0,0 +1 @@
acc09bbe65a1837a08912774c7fed178547514e6

View File

@@ -0,0 +1,195 @@
#include "stdafx.h"
#include "../../relacy/relacy_cli.hpp"
using rl::nvar;
using rl::nvolatile;
using rl::mutex;
template<typename T>
class ws_deque
{
public:
ws_deque()
{
m_mask($) = initial_size - 1;
m_headIndex($) = 0;
m_tailIndex($) = 0;
m_array($) = new nvar<T> [initial_size];
m_arraySize($) = initial_size;
}
bool IsEmpty()
{
return m_headIndex($) >= m_tailIndex($);
}
size_t Count()
{
return m_tailIndex($) - m_headIndex($);
}
void push(T item)
{
size_t tail = m_tailIndex($);
if (tail <= m_headIndex($) + m_mask($))
{
m_array($)[tail & m_mask($)]($) = item;
m_tailIndex($) = tail + 1;
}
else
{
m_foreignLock.lock($);
size_t head = m_headIndex($);
size_t count = Count();
if (count >= m_mask($))
{
size_t arraySize = m_arraySize($);
size_t mask = m_mask($);
nvar<T>* newArray = new nvar<T> [arraySize * 2];
nvar<T>* arr = m_array($);
for (size_t i = 0; i != count; ++i)
newArray[i]($) = arr[(i + head) & mask]($);
m_array($) = newArray;
m_arraySize($) = arraySize * 2;
m_headIndex($) = 0;
m_tailIndex($) = count;
tail = count;
m_mask($) = (mask * 2) | 1;
}
m_array($)[tail & m_mask($)]($) = item;
m_tailIndex($) = tail + 1;
m_foreignLock.unlock($);
}
}
bool pop(T& item)
{
size_t tail = m_tailIndex($);
if (tail == 0)
return false;
tail -= 1;
rl::Interlocked::Exchange(m_tailIndex, tail, $);
if (m_headIndex($) <= tail)
{
item = m_array($)[tail & m_mask($)]($);
return true;
}
else
{
m_foreignLock.lock($);
if (m_headIndex($) <= tail)
{
item = m_array($)[tail & m_mask($)]($);
m_foreignLock.unlock($);
return true;
}
else
{
m_tailIndex($) = tail + 1;
m_foreignLock.unlock($);
return false;
}
}
}
bool steal(T& item)
{
if (false == m_foreignLock.try_lock($))
return false;
size_t head = m_headIndex($);
rl::Interlocked::Exchange(m_headIndex, head + 1, $);
if (head < m_tailIndex($))
{
item = m_array($)[head & m_mask($)]($);
m_foreignLock.unlock($);
return true;
}
else
{
m_headIndex($) = head;
m_foreignLock.unlock($);
return false;
}
}
private:
static size_t const initial_size = 2;
nvar<nvar<T>*> m_array;
nvar<size_t> m_mask;
nvar<size_t> m_arraySize;
nvolatile<size_t> m_headIndex;
nvolatile<size_t> m_tailIndex;
mutex m_foreignLock;
};
struct ws_deque_test : rl::test_suite<ws_deque_test, 2>
{
ws_deque<int> q;
bool state [2];
void before()
{
state[0] = true;
state[1] = true;
}
void after()
{
RL_ASSERT(state[0] == false);
RL_ASSERT(state[1] == false);
}
void thread(unsigned index)
{
if (0 == index)
{
q.push(1);
q.push(2);
int item = 0;
bool res = q.pop(item);
RL_ASSERT(res && item == 2);
RL_ASSERT(state[1]);
state[1] = false;
item = 0;
res = q.pop(item);
if (res)
{
RL_ASSERT(state[0]);
state[0] = false;
}
item = 0;
res = q.pop(item);
RL_ASSERT(res == false);
}
else
{
int item = 0;
bool res = q.steal(item);
if (res)
{
RL_ASSERT(item == 1);
RL_ASSERT(state[0]);
state[0] = false;
}
}
}
};
int main()
{
rl::simulate<ws_deque_test>();
}

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="cli_ws_deque"
ProjectGUID="{967F376B-BDBF-4AC8-9325-371CC8ABD8FD}"
RootNamespace="cli_ws_deque"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\cli_ws_deque.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,7 @@
#pragma once
#include "../../relacy/pch.hpp"

View File

@@ -0,0 +1,181 @@
#include "stdafx.h"
#include "../../relacy/relacy_std.hpp"
// THE TEST IS EXPECTED TO FAIL WITH "DEADLOCK"
class CondVar
{
public:
CondVar();
~CondVar();
void Enter();
void Wait();
void Release();
void ReleaseAll();
void Leave();
private:
std::atomic<int> m_lMutex;
std::atomic<unsigned> m_dwWaitingForSignal;
HANDLE m_xhEvtEnter;
HANDLE m_xhSemRelease;
};
CondVar::CondVar()
: m_xhEvtEnter(CreateEvent(0, 0, 0, 0))
, m_xhSemRelease(CreateSemaphore(0, 0, 0x7FFFFFFF, 0))
{
m_lMutex.store(0, std::memory_order_relaxed);
m_dwWaitingForSignal.store(0, std::memory_order_relaxed);
}
CondVar::~CondVar()
{
CloseHandle(m_xhEvtEnter);
CloseHandle(m_xhSemRelease);
}
void CondVar::Enter()
{
int lMutex = m_lMutex.load(std::memory_order_seq_cst);
for (;;)
{
if( lMutex >= 0 )
{
if (m_lMutex.compare_exchange_weak(lMutex, lMutex | 0x80000000u, std::memory_order_seq_cst))
break;
}
else
{
if (false == m_lMutex.compare_exchange_weak(lMutex, lMutex + 1, std::memory_order_seq_cst))
continue;
WaitForSingleObject(m_xhEvtEnter, INFINITE);
RL_ASSERT(m_lMutex.load(std::memory_order_seq_cst) < 0);
break;
}
}
}
void CondVar::Wait()
{
unsigned dwWaitingForSignal = m_dwWaitingForSignal.load(std::memory_order_seq_cst);
m_dwWaitingForSignal.store(dwWaitingForSignal + 1, std::memory_order_seq_cst);
RL_ASSERT(m_lMutex.load(std::memory_order_seq_cst) < 0);
int lMutex = m_lMutex.load(std::memory_order_seq_cst);
for (;;)
{
unsigned dwWaitingToOwn = lMutex & 0x7FFFFFFFu;
RL_ASSERT(dwWaitingToOwn >= dwWaitingForSignal);
if (dwWaitingToOwn == dwWaitingForSignal)
{
if (m_lMutex.compare_exchange_weak(lMutex, dwWaitingToOwn + 1, std::memory_order_seq_cst))
break;
}
else
{
SetEvent(m_xhEvtEnter);
break;
}
}
WaitForSingleObject(m_xhSemRelease, INFINITE);
WaitForSingleObject(m_xhEvtEnter, INFINITE);
RL_ASSERT(m_lMutex.load(std::memory_order_seq_cst) < 0);
}
void CondVar::Release()
{
RL_ASSERT(m_lMutex.load(std::memory_order_seq_cst) < 0);
unsigned dwWaitingForSignal = m_dwWaitingForSignal.load(std::memory_order_seq_cst);
if (dwWaitingForSignal != 0)
{
m_dwWaitingForSignal.store(dwWaitingForSignal - 1, std::memory_order_seq_cst);
ReleaseSemaphore(m_xhSemRelease, 1, 0);
}
}
void CondVar::ReleaseAll()
{
RL_ASSERT(m_lMutex.load(std::memory_order_seq_cst) < 0);
unsigned dwWaitingForSignal = m_dwWaitingForSignal.load(std::memory_order_seq_cst);
if (dwWaitingForSignal != 0)
{
m_dwWaitingForSignal.store(0, std::memory_order_seq_cst);
ReleaseSemaphore(m_xhSemRelease, dwWaitingForSignal, 0);
}
}
void CondVar::Leave()
{
int lMutex = m_lMutex.load(std::memory_order_seq_cst);
RL_ASSERT(lMutex < 0);
for (;;)
{
unsigned dwWaitingToOwn = lMutex & 0x7FFFFFFFu;
unsigned dwWaitingForSignal = m_dwWaitingForSignal.load(std::memory_order_seq_cst);
RL_ASSERT(dwWaitingToOwn >= dwWaitingForSignal);
if (dwWaitingToOwn == dwWaitingForSignal)
{
if (m_lMutex.compare_exchange_weak(lMutex, lMutex & 0x7FFFFFFF, std::memory_order_seq_cst))
break;
}
else
{
if (false == m_lMutex.compare_exchange_weak(lMutex, lMutex - 1, std::memory_order_seq_cst))
continue;
SetEvent(m_xhEvtEnter);
break;
}
}
}
struct CondVarTest : rl::test_suite<CondVarTest, 3>
{
VAR_T(int) stage;
CondVar cv;
void before()
{
VAR(stage) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
cv.Enter();
VAR(stage) += 1;
cv.ReleaseAll();
while (VAR(stage) != 2)
cv.Wait();
cv.Leave();
}
else if (1 == index)
{
cv.Enter();
while (VAR(stage) != 1)
cv.Wait();
VAR(stage) += 1;
cv.ReleaseAll();
cv.Leave();
}
else if (2 == index)
{
cv.Enter();
while (VAR(stage) != 2)
cv.Wait();
cv.Leave();
}
}
};
int main()
{
rl::simulate<CondVarTest>();
}

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="condvar"
ProjectGUID="{6CC59CF8-408B-441B-8F65-15651210CB82}"
RootNamespace="condvar"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\condvar.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="condvar"
ProjectGUID="{6CC59CF8-408B-441B-8F65-15651210CB82}"
RootNamespace="condvar"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\condvar.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,14 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#define RL_MSVC_OUTPUT
//#define RL_DEBUGBREAK_ON_FAILURE
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,178 @@
/*
#define BOOST_ALL_NO_LIB
#pragma warning (push, 3)
#include <boost/thread/mutex.hpp>
#include <boost/thread/shared_mutex.hpp>
#include "C:\boost_1_35_0\libs\thread\src\win32\exceptions.cpp"
#pragma warning (pop)
class business_logic
{
public:
typedef unsigned account_id_t;
typedef double balance_t;
bool add_account(account_id_t acc_id, balance_t balance)
{
accounts_guard.lock();
if (accounts.find(acc_id) != accounts.end())
{
accounts_guard.unlock();
return false;
}
accounts[acc_id].balance = balance;
accounts_guard.unlock();
return true;
}
bool transfer_balance(account_id_t acc_id1, account_id_t acc_id2, balance_t amount)
{
accounts_guard.lock_shared();
if (accounts.find(acc_id1) != accounts.end()
|| accounts.find(acc_id2) != accounts.end())
{
accounts_guard.unlock_shared();
return false;
}
account_info& acc1 = accounts[acc_id1];
account_info& acc2 = accounts[acc_id2];
acc1.mtx.lock();
acc2.mtx.lock();
accounts_guard.unlock_shared();
acc1.balance -= amount;
acc2.balance += amount;
acc1.mtx.unlock();
acc2.mtx.unlock();
return true;
}
private:
struct account_info
{
balance_t balance;
boost::mutex mtx;
account_info()
: balance()
{}
account_info(account_info const& acc)
: balance(acc.balance)
{}
};
typedef std::map<account_id_t, account_info> account_map_t;
account_map_t accounts;
boost::shared_mutex accounts_guard;
};
*/
/*
#undef RL_TEST
#ifndef RL_TEST
//# define ASSERT assert
typedef boost::mutex mutex_t;
# define $$
#else
//# define ASSERT RL_ASSERT
typedef rl::recursive_mutex mutex_t;
# define $$ $
#endif
class business_logic
{
public:
typedef unsigned account_id_t;
typedef double balance_t;
bool add_account(account_id_t acc_id, balance_t balance)
{
accounts_guard.lock($$);
if (accounts.find(acc_id) != accounts.end())
{
accounts_guard.unlock($$);
return false;
}
accounts[acc_id].balance = balance;
accounts_guard.unlock($$);
return true;
}
bool transfer_balance(account_id_t acc_id1, account_id_t acc_id2, balance_t amount)
{
accounts_guard.lock($$);
if (accounts.find(acc_id1) == accounts.end()
|| accounts.find(acc_id2) == accounts.end())
{
accounts_guard.unlock($$);
return false;
}
account_info& acc1 = accounts[acc_id1];
account_info& acc2 = accounts[acc_id2];
acc1.mtx.lock($$);
acc2.mtx.lock($$);
accounts_guard.unlock($$);
acc1.balance -= amount;
acc2.balance += amount;
acc1.mtx.unlock($$);
acc2.mtx.unlock($$);
return true;
}
private:
struct account_info
{
balance_t balance;
mutex_t mtx;
account_info()
: balance()
{}
account_info(account_info const& acc)
: balance(acc.balance)
{}
};
typedef std::map<account_id_t, account_info> account_map_t;
account_map_t accounts;
mutex_t accounts_guard;
};
*/
/*
struct business_logic_test : rl::test_suite<business_logic_test, 2>
{
business_logic bl;
static size_t const account_count = 10;
void before()
{
for (size_t i = 0; i != account_count; ++i)
{
bool rv = bl.add_account(i, i * 10.0);
RL_ASSERT(rv);
}
}
void thread(unsigned)
{
business_logic::account_id_t acc1 = rl::rand(account_count);
business_logic::account_id_t acc2 = rl::rand(account_count);
bool rv = bl.transfer_balance(acc1, acc2, 1.0);
RL_ASSERT(rv);
}
};
*/

View File

@@ -0,0 +1,707 @@
#include "stdafx.h"
#include "../../relacy/windows.h"
#define HANDLE rl::HANDLE
#define CreateSemaphoreA rl::RL_CreateSemaphore($)
#define CreateSemaphoreW rl::RL_CreateSemaphore($)
#ifndef CreateSemaphore
# define CreateSemaphore CreateSemaphoreW
#endif
#define CloseHandle rl::RL_CloseHandle($)
#include <stddef.h>
#if defined(WIN32) && defined(_MSC_VER)
#include <windows.h>
#include <intrin.h>
class semaphore
{
public:
semaphore()
{
h_ = CreateSemaphore(0, 0, LONG_MAX, 0);
}
~semaphore()
{
CloseHandle(h_);
}
void wait()
{
WaitForSingleObject(h_, INFINITE);
}
void post()
{
ReleaseSemaphore(h_, 1, 0);
}
private:
HANDLE h_;
semaphore(semaphore const&);
semaphore& operator = (semaphore const&);
};
class mutex
{
public:
mutex()
{
InitializeCriticalSection(&cs_);
}
~mutex()
{
DeleteCriticalSection(&cs_);
}
void lock()
{
EnterCriticalSection(&cs_);
}
void unlock()
{
LeaveCriticalSection(&cs_);
}
private:
CRITICAL_SECTION cs_;
mutex(mutex const&);
mutex& operator = (mutex const&);
};
void full_memory_fence()
{
_mm_mfence();
}
#define THREAD_LOCAL __declspec(thread)
#elif defined(POSIX) && defined(GCC)
#include <pthread.h>
#include <semaphore.h>
class semaphore
{
public:
semaphore()
{
sem_init(&sem_, 0, 0);
}
~semaphore()
{
sem_destroy(&sem_);
}
void wait()
{
sem_wait(&sem_);
}
void post()
{
sem_post(&sem_);
}
private:
sem_t sem_;
semaphore(semaphore const&);
semaphore& operator = (semaphore const&);
};
class mutex
{
public:
mutex()
{
pthread_mutex_init(&mutex_, 0);
}
~mutex()
{
pthread_mutex_destroy(&mutex_);
}
void lock()
{
pthread_mutex_lock(&mutex_);
}
void unlock()
{
pthread_mutex_unlock(&mutex_);
}
private:
pthread_mutex_t mutex_;
mutex(mutex const&);
mutex& operator = (mutex const&);
};
void full_memory_fence()
{
__sync_synchronize();
}
#define THREAD_LOCAL __thread
#endif
class lock
{
public:
lock(mutex& m)
: m_(m)
{
m.lock();
}
~lock()
{
m_.unlock();
}
private:
mutex& m_;
lock(lock const&);
lock& operator = (lock const&);
};
/** simple single-threaded double-linked list
* nothing interesting
*/
class dlist
{
public:
struct node
{
node* prev_;
node* next_;
node()
{
prev_ = 0;
next_ = 0;
}
};
dlist()
{
reset();
}
void push(node* n)
{
size_ += 1;
n->next_ = head_.next_;
n->prev_ = &head_;
head_.next_->prev_ = n;
head_.next_ = n;
}
node* pop()
{
if (size_ == 0)
return 0;
node* n = head_.next_;
remove(n);
return n;
}
void remove(node* n)
{
size_ -= 1;
n->prev_->next_ = n->next_;
n->next_->prev_ = n->prev_;
}
size_t size() const
{
return size_;
}
node* begin()
{
return head_.next_;
}
void flush_to(dlist& target)
{
if (size_)
{
target.size_ = size_;
target.head_.next_ = head_.next_;
target.head_.next_->prev_ = &target.head_;
target.tail_.prev_ = tail_.prev_;
target.tail_.prev_->next_ = &target.tail_;
}
else
{
target.reset();
}
reset();
}
static bool not_last(node* n)
{
return n->next_ != 0;
}
static node* get_next(node* n)
{
return n->next_;
}
private:
size_t volatile size_;
node head_;
node tail_;
void reset()
{
size_ = 0;
head_.next_ = &tail_;
head_.prev_ = 0;
tail_.next_ = 0;
tail_.prev_ = &head_;
}
dlist(dlist const&);
dlist& operator = (dlist const&);
};
/** pre-thread descriptor for eventcount
*/
struct ec_thread
{
dlist::node node_;
semaphore sema_;
unsigned epoch_;
bool volatile in_waitset_;
bool spurious_;
void* ctx_;
ec_thread()
{
epoch_ = 0;
in_waitset_ = false;
spurious_ = false;
ctx_ = 0;
}
~ec_thread()
{
if (spurious_)
sema_.wait();
}
static ec_thread* current()
{
static THREAD_LOCAL ec_thread* ec_thread_instance = 0;
ec_thread* instance = ec_thread_instance;
if (instance == 0)
{
instance = new ec_thread;
ec_thread_instance = instance;
}
return instance;
// instance must be destroyed in DllMain() callback
// or in pthread_key_create() callback
}
private:
ec_thread(ec_thread const&);
ec_thread& operator = (ec_thread const&);
};
/** fine-grained eventcount implementation
*/
class eventcount
{
public:
eventcount()
{
epoch_ = 0;
}
void prepare_wait(void* ctx = 0)
{
ec_thread* th = ec_thread::current();
// this is good place to pump previous spurious wakeup
if (th->spurious_)
{
th->spurious_ = false;
th->sema_.wait();
}
th->in_waitset_ = true;
th->ctx_ = ctx;
{
lock l (mtx_);
th->epoch_ = epoch_;
waitset_.push(&th->node_);
}
full_memory_fence();
}
void wait()
{
ec_thread* th = ec_thread::current();
// this check is just an optimization
if (th->epoch_ == epoch_)
th->sema_.wait();
else
retire_wait();
}
void retire_wait()
{
ec_thread* th = ec_thread::current();
// spurious wakeup will be pumped in following prepare_wait()
th->spurious_ = true;
// try to remove node from waitset
if (th->in_waitset_)
{
lock l (mtx_);
if (th->in_waitset_)
{
// successfully removed from waitset,
// so there will be no spurious wakeup
th->in_waitset_ = false;
th->spurious_ = false;
waitset_.remove(&th->node_);
}
}
}
void notify_one()
{
full_memory_fence();
notify_one_relaxed();
}
template<typename predicate_t>
void notify(predicate_t pred)
{
full_memory_fence();
notify_relaxed(pred);
}
void notify_all()
{
full_memory_fence();
notify_all_relaxed();
}
void notify_one_relaxed()
{
if (waitset_.size() == 0)
return;
dlist::node* n;
{
lock l (mtx_);
epoch_ += 1;
n = waitset_.pop();
if (n)
to_ec_thread(n)->in_waitset_ = false;
}
if (n)
{
to_ec_thread(n)->sema_.post();
}
}
template<typename predicate_t>
void notify_relaxed(predicate_t pred)
{
if (waitset_.size() == 0)
return;
dlist temp;
{
lock l (mtx_);
epoch_ += 1;
size_t size = waitset_.size();
size_t idx = 0;
dlist::node* n = waitset_.begin();
while (dlist::not_last(n))
{
dlist::node* next = dlist::get_next(n);
ec_thread* th = to_ec_thread(n);
if (pred(th->ctx_, size, idx))
{
waitset_.remove(n);
temp.push(n);
th->in_waitset_ = false;
}
n = next;
idx += 1;
}
}
dlist::node* n = temp.begin();
while (dlist::not_last(n))
{
dlist::node* next = dlist::get_next(n);
to_ec_thread(n)->sema_.post();
n = next;
}
}
void notify_all_relaxed()
{
if (waitset_.size() == 0)
return;
dlist temp;
{
lock l (mtx_);
epoch_ += 1;
waitset_.flush_to(temp);
dlist::node* n = temp.begin();
while (dlist::not_last(n))
{
to_ec_thread(n)->in_waitset_ = false;
n = dlist::get_next(n);
}
}
dlist::node* n = temp.begin();
while (dlist::not_last(n))
{
dlist::node* next = dlist::get_next(n);
to_ec_thread(n)->sema_.post();
n = next;
}
}
private:
mutex mtx_;
dlist waitset_;
volatile unsigned epoch_;
ec_thread* to_ec_thread(dlist::node* n)
{
return (ec_thread*)((char*)n - offsetof(ec_thread, node_));
}
eventcount(eventcount const&);
eventcount& operator = (eventcount const&);
};
struct scheduler
{
struct tbb_thread {};
eventcount ec_;
tbb_thread* threads_;
bool volatile is_permanently_open_;
void wait_while_pool_is_empty(tbb_thread* th)
{
if (is_permanently_open_)
return;
ec_.prepare_wait(th);
if (pool_is_empty())
ec_.wait();
else
ec_.retire_wait();
}
void notify_about_new_task_available()
{
ec_.notify_one_relaxed();
}
void notify_about_new_task_available_with_preference(tbb_thread* preference)
{
struct local
{
tbb_thread* preference_;
bool fired_;
bool operator () (void* ctx, size_t count, size_t idx)
{
tbb_thread* th = (tbb_thread*)ctx;
if (th == preference_)
{
fired_ = true;
return true;
}
else if (idx == count - 1 && fired_ == false)
{
return true;
}
else
{
return false;
}
}
}
pred = {preference};
ec_.notify_relaxed(pred);
}
void notify_about_list_of_tasks_available(size_t total_count, size_t preference_count, tbb_thread** preferences)
{
struct local
{
size_t remain_to_signal_;
size_t preference_count_;
tbb_thread** preferences_;
bool operator () (void* ctx, size_t count, size_t idx)
{
tbb_thread* th = (tbb_thread*)ctx;
size_t remain_in_waitset = count - idx;
if (remain_in_waitset <= remain_to_signal_)
{
return true;
}
else
{
for (size_t i = 0; i != preference_count_; ++i)
{
if (preferences_[i] == th)
{
remain_to_signal_ -= 1;
return true;
}
}
}
return false;
}
}
pred = {total_count, preference_count, preferences};
ec_.notify_relaxed(pred);
}
bool pool_is_empty()
{
return true;
}
};
struct queue
{
int producer_idx_;
int consumer_idx_;
void** buffer_;
eventcount ec_;
void enqueue(void* data)
{
int idx = ++producer_idx_; // atomic
buffer_[idx] = data;
struct local
{
int idx_;
bool operator () (void* ctx, size_t /*count*/, size_t /*idx*/)
{
return idx_ == *(int*)ctx;
}
}
pred = {idx};
ec_.notify(pred); // not relaxed!!!
}
void* dequeue()
{
int idx = ++consumer_idx_; // atomic
void* data = buffer_[idx];
if (data)
return data;
for (;;)
{
ec_.prepare_wait(&idx);
data = buffer_[idx];
if (data)
{
ec_.retire_wait();
return data;
}
ec_.wait();
data = buffer_[idx];
if (data)
{
return data;
}
}
}
};
class condition_variable
{
eventcount ec_;
public:
void wait(mutex& mtx)
{
ec_.prepare_wait();
mtx.unlock();
ec_.wait();
mtx.lock();
}
void signal()
{
ec_.notify_one();
}
void broadcast()
{
ec_.notify_all();
}
};
struct eventcount_test : rl::test_suite<eventcount_test, 2>
{
void thread(unsigned index)
{
delete ec_thread::current();
(void)index;
}
};
int main()
{
rl::test_params p;
//p.iteration_count = 1000000;
rl::simulate<eventcount_test>(p);
}

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="eventcount"
ProjectGUID="{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E54}"
RootNamespace="eventcount"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
<File
RelativePath="..\eventcount.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="eventcount"
ProjectGUID="{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E54}"
RootNamespace="eventcount"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
<File
RelativePath="..\eventcount.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// ws_deque.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@@ -0,0 +1,10 @@
#pragma once
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <intrin.h>
#include <stddef.h>
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,316 @@
#pragma once
struct amp_raw_condition_variable_s
{
CRITICAL_SECTION access_waiting_threads_count_critsec;
HANDLE wake_waiting_threads_mutex;
HANDLE waking_waiting_threads_count_control_sem;
HANDLE finished_waking_waiting_threads_event;
VAR_T(LONG) waiting_thread_count;
VAR_T(BOOL) broadcast_in_progress;
};
struct amp_raw_mutex_s
{
CRITICAL_SECTION critical_section;
BOOL is_locked;
};
typedef amp_raw_condition_variable_s* amp_raw_condition_variable_t;
typedef amp_raw_mutex_s* amp_raw_mutex_t;
int const AMP_SUCCESS = 0;
int amp_raw_mutex_init(amp_raw_mutex_t mutex)
{
InitializeCriticalSectionAndSpinCount(&mutex->critical_section, 1);
mutex->is_locked = FALSE;
return AMP_SUCCESS;
}
int amp_raw_mutex_finalize(amp_raw_mutex_t mutex)
{
assert(NULL != mutex);
int retval = AMP_SUCCESS;
DeleteCriticalSection(&mutex->critical_section);
return retval;
}
int amp_raw_mutex_lock(amp_raw_mutex_t mutex)
{
assert(NULL != mutex);
EnterCriticalSection(&mutex->critical_section);
mutex->is_locked = TRUE;
return AMP_SUCCESS;
}
int amp_raw_mutex_unlock(amp_raw_mutex_t mutex)
{
assert(NULL != mutex);
mutex->is_locked = FALSE;
LeaveCriticalSection(&mutex->critical_section);
return AMP_SUCCESS;
}
int amp_raw_condition_variable_init(amp_raw_condition_variable_t cond)
{
InitializeCriticalSectionAndSpinCount(&cond->access_waiting_threads_count_critsec, 1);
cond->wake_waiting_threads_mutex = CreateMutex(0, 0, 0);
cond->waking_waiting_threads_count_control_sem = CreateSemaphore(NULL, /* No inheritance to child processes */
0, /* Initially no threads can pass */
LONG_MAX, /* Max semaphore count */
NULL); /* Only intra-process semaphore */
cond->finished_waking_waiting_threads_event = CreateEvent(NULL, /* Default security and no inheritance to child processes */
FALSE, /* No manual reset */
0, /* Initially not signaled */
NULL /* Not inter-process available */
);
cond->VAR(waiting_thread_count) = 0l;
cond->VAR(broadcast_in_progress) = FALSE;
return AMP_SUCCESS;
}
int amp_raw_condition_variable_finalize(amp_raw_condition_variable_t cond)
{
DeleteCriticalSection(&cond->access_waiting_threads_count_critsec);
CloseHandle(cond->wake_waiting_threads_mutex);
CloseHandle(cond->waking_waiting_threads_count_control_sem);
CloseHandle(cond->finished_waking_waiting_threads_event);
int ret_error_code = AMP_SUCCESS;
return ret_error_code;
}
int amp_raw_condition_variable_signal(amp_raw_condition_variable_t cond)
{
WaitForSingleObject(cond->wake_waiting_threads_mutex,
INFINITE);
BOOL at_least_one_waiting_thread = (0l != cond->VAR(waiting_thread_count));
if (at_least_one_waiting_thread) {
LONG prev_sem_count = 0;
ReleaseSemaphore(cond->waking_waiting_threads_count_control_sem,
1,
&prev_sem_count /* No interest in the previous sem count. */
);
WaitForSingleObject(cond->finished_waking_waiting_threads_event,
INFINITE);
}
ReleaseMutex(cond->wake_waiting_threads_mutex);
return AMP_SUCCESS;
}
int amp_raw_condition_variable_broadcast(amp_raw_condition_variable_t cond)
{
WaitForSingleObject(cond->wake_waiting_threads_mutex,
INFINITE);
LONG const waiting_thread_count = cond->VAR(waiting_thread_count);
if (0 < waiting_thread_count) {
cond->VAR(broadcast_in_progress) = TRUE;
/* Releasing the sem here and waiting on it should update the memory of
* the waiting threads to see that a broadcast is in progress.
*/
LONG prev_sem_count = 0;
/* Assuming that less threads exist than max possible semaphore count.
* TODO: @todo Decide if to spin here if the assumption doesn't hold
* true in the future?
*/
ReleaseSemaphore(cond->waking_waiting_threads_count_control_sem,
waiting_thread_count,
&prev_sem_count /* No interest in the previous sem count. */
);
WaitForSingleObject(cond->finished_waking_waiting_threads_event,
INFINITE);
cond->VAR(broadcast_in_progress) = FALSE;
}
ReleaseMutex(cond->wake_waiting_threads_mutex);
return AMP_SUCCESS;
}
int amp_raw_condition_variable_wait(amp_raw_condition_variable_t cond,
struct amp_raw_mutex_s *mutex)
{
WaitForSingleObject(cond->wake_waiting_threads_mutex,
INFINITE);
{
++(cond->VAR(waiting_thread_count));
}
amp_raw_mutex_unlock(mutex);
SignalObjectAndWait(cond->wake_waiting_threads_mutex, cond->waking_waiting_threads_count_control_sem, INFINITE, FALSE);
BOOL broadcast_in_progress = FALSE;
LONG count = 0;
EnterCriticalSection(&cond->access_waiting_threads_count_critsec);
{
count = --(cond->VAR(waiting_thread_count));
broadcast_in_progress = cond->VAR(broadcast_in_progress);
}
LeaveCriticalSection(&cond->access_waiting_threads_count_critsec);
BOOL all_waiting_threads_awake = TRUE;
if (TRUE == broadcast_in_progress && count > 0) {
all_waiting_threads_awake = FALSE;
}
if (TRUE == all_waiting_threads_awake) {
SetEvent(cond->finished_waking_waiting_threads_event);
}
amp_raw_mutex_lock(mutex);
return AMP_SUCCESS;
}
struct amp_condvar_test : rl::test_suite<amp_condvar_test, 2>
{
VAR_T(int) data;
amp_raw_mutex_s mtx;
amp_raw_condition_variable_s cv;
void before()
{
VAR(data) = 0;
amp_raw_mutex_init(&mtx);
amp_raw_condition_variable_init(&cv);
}
void after()
{
amp_raw_mutex_finalize(&mtx);
amp_raw_condition_variable_finalize(&cv);
}
void thread(unsigned index)
{
if (0 == index)
{
amp_raw_mutex_lock(&mtx);
data($) += 1;
amp_raw_condition_variable_signal(&cv);
amp_raw_mutex_unlock(&mtx);
}
else
{
amp_raw_mutex_lock(&mtx);
while (0 == data($))
{
amp_raw_condition_variable_wait(&cv, &mtx);
}
amp_raw_mutex_unlock(&mtx);
}
}
};
struct amp_condvar_test2 : rl::test_suite<amp_condvar_test2, 4>
{
VAR_T(int) stage;
amp_raw_mutex_s mtx;
amp_raw_condition_variable_s cv;
void before()
{
VAR(stage) = 0;
amp_raw_mutex_init(&mtx);
amp_raw_condition_variable_init(&cv);
}
void after()
{
amp_raw_mutex_finalize(&mtx);
amp_raw_condition_variable_finalize(&cv);
}
void thread(unsigned index)
{
if (0 == index)
{
amp_raw_mutex_lock(&mtx);
stage($) += 1;
amp_raw_condition_variable_broadcast(&cv);
while (stage($) < 2)
amp_raw_condition_variable_wait(&cv, &mtx);
amp_raw_mutex_unlock(&mtx);
}
else if (1 == index)
{
amp_raw_mutex_lock(&mtx);
while (stage($) != 1)
amp_raw_condition_variable_wait(&cv, &mtx);
stage($) += 1;
amp_raw_condition_variable_broadcast(&cv);
amp_raw_mutex_unlock(&mtx);
}
else if (2 == index)
{
amp_raw_mutex_lock(&mtx);
while (stage($) != 2)
amp_raw_condition_variable_wait(&cv, &mtx);
stage($) += 1;
//amp_raw_condition_variable_broadcast(&cv);
amp_raw_mutex_unlock(&mtx);
amp_raw_condition_variable_signal(&cv);
}
else if (3 == index)
{
amp_raw_mutex_lock(&mtx);
while (stage($) != 3)
amp_raw_condition_variable_wait(&cv, &mtx);
amp_raw_mutex_unlock(&mtx);
}
}
};

View File

@@ -0,0 +1,17 @@
#include "stdafx.h"
#include "spsc_overwrite_queue.hpp"
#include "amp_condvar.hpp"
int main()
{
rl::test_params p;
p.iteration_count = 10000;
//p.search_type = rl::sched_bound;
//p.context_bound = 3;
rl::execute<spsc_overwrite_queue_test, 2>(p);
rl::simulate<amp_condvar_test>(p);
rl::simulate<amp_condvar_test2>(p);
}

View File

@@ -0,0 +1,208 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="examples"
ProjectGUID="{1EB73A6F-7F94-4ED4-8EB3-C245E773207A}"
RootNamespace="examples"
Keyword="Win32Proj"
TargetFrameworkVersion="0"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\amp_condvar.hpp"
>
</File>
<File
RelativePath="..\examples.cpp"
>
</File>
<File
RelativePath="..\spsc_overwrite_queue.hpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,202 @@
#pragma once
template<typename T>
class queue
{
public:
queue(size_t count)
{
assert(count >= 6);
sema = CreateSemaphore(0, 0, 1, 0);
waiting.store(false, std::memory_order_relaxed);
deq_node = 0;
block = new node [count];
block->next.store(0, std::memory_order_relaxed);
full_tail = block;
full_head.store(block, std::memory_order_relaxed);
free_head = block + 1;
free_tail.store(block + count - 1, std::memory_order_relaxed);
free_tail.load(std::memory_order_relaxed)->next.store(0, std::memory_order_relaxed);
for (size_t i = 1; i != count - 1; i += 1)
block[i].next.store(block + i + 1, std::memory_order_relaxed);
}
~queue()
{
CloseHandle(sema);
delete [] block;
}
VAR_T(T)& enqueue_prepare()
{
return full_tail->data;
}
void enqueue_commit()
{
node* n = get_free_node();
n->next.store(0, std::memory_order_release);
full_tail->next.store(n, std::memory_order_seq_cst);
bool signal = waiting.load(std::memory_order_seq_cst);
full_tail = n;
if (signal)
{
waiting.store(false, std::memory_order_relaxed);
ReleaseSemaphore(sema, 1, 0);
}
}
VAR_T(T)& dequeue_prepare()
{
deq_node = get_full_node();
return deq_node->data;
}
void dequeue_commit()
{
deq_node->next.store(0, std::memory_order_release);
node* prev = free_tail.exchange(deq_node, std::memory_order_acq_rel);
prev->next.store(deq_node, std::memory_order_release);
}
private:
struct node
{
std::atomic<node*> next;
VAR_T(T) data;
};
node* block;
node* full_tail;
node* free_head;
node* deq_node;
char pad [64];
std::atomic<node*> full_head;
std::atomic<node*> free_tail;
std::atomic<bool> waiting;
HANDLE sema;
node* get_free_node()
{
for (;;)
{
node* n = free_head;
node* next = n->next.load(std::memory_order_acquire);
if (next)
{
free_head = next;
return n;
}
n = full_head.load(std::memory_order_acquire);
next = n->next.load(std::memory_order_acquire);
if (next)
{
if (full_head.compare_exchange_strong(n, next, std::memory_order_seq_cst))
{
//node* n2 = free_head;
//node* next2 = n2->next.load(std::memory_order_acquire);
//if (next2)
//{
// n->next.store(0, std::memory_order_release);
// node* prev = free_tail.exchange(n, std::memory_order_acq_rel);
// prev->next.store(n, std::memory_order_release);
// free_head = next2;
// return n2;
//}
//else
{
return n;
}
}
}
}
}
node* get_full_node()
{
node* n = full_head.load(std::memory_order_acquire);
for (;;)
{
node* next = n->next.load(std::memory_order_acquire);
if (next == 0)
{
waiting.store(true, std::memory_order_seq_cst);
n = full_head.load(std::memory_order_seq_cst);
next = n->next.load(std::memory_order_acquire);
if (next)
{
waiting.store(false, std::memory_order_relaxed);
}
else
{
WaitForSingleObject(sema, INFINITE);
n = full_head.load(std::memory_order_acquire);
continue;
}
}
if (full_head.compare_exchange_strong(n, next, std::memory_order_acq_rel))
return n;
}
}
};
unsigned RL_STDCALL consumer_thread(void* ctx)
{
queue<int>* q = (queue<int>*)ctx;
int prev_data = -1;
for (;;)
{
VAR_T(int)& data0 = q->dequeue_prepare();
int data = VAR(data0);
assert(data > prev_data);
prev_data = data;
q->dequeue_commit();
//printf("%d\n", prev_data);
if (prev_data == 11)
break;
//Sleep(5);
}
return 0;
}
unsigned RL_STDCALL producer_thread(void* ctx)
{
queue<int>* q = (queue<int>*)ctx;
for (int i = 0; i != 12; i += 1)
{
VAR_T(int)& data = q->enqueue_prepare();
VAR(data) = i;
q->enqueue_commit();
//Sleep(1);
}
return 0;
}
void spsc_overwrite_queue_test()
{
queue<int> q (6);
HANDLE th [2];
th[0] = (HANDLE)_beginthreadex(0, 0, consumer_thread, &q, 0, 0);
th[1] = (HANDLE)_beginthreadex(0, 0, producer_thread, &q, 0, 0);
WaitForMultipleObjects(2, th, 1, INFINITE);
for (int i = 100; i != 104; i += 1)
{
VAR_T(int)& data = q.enqueue_prepare();
VAR(data) = i;
q.enqueue_commit();
}
for (int i = 100; i != 104; i += 1)
{
VAR_T(int)& data0 = q.dequeue_prepare();
int data = VAR(data0);
assert(data == i);
q.dequeue_commit();
}
}

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#define RL_FORCE_SEQ_CST
#define RL_MSVC_OUTPUT
//#define RL_DEBUGBREAK_ON_FAILURE
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"
#include "../../relacy/stdlib/windows.hpp"

View File

@@ -0,0 +1,195 @@
#include "stdafx.h"
#include "../../relacy/relacy_java.hpp"
using rl::jvar;
using rl::jvolatile;
using rl::mutex;
template<typename T>
class ws_deque
{
public:
ws_deque()
{
m_mask($) = initial_size - 1;
m_headIndex($) = 0;
m_tailIndex($) = 0;
m_array($) = new jvar<T> [initial_size];
m_arraySize($) = initial_size;
}
bool IsEmpty()
{
return m_headIndex($) >= m_tailIndex($);
}
size_t Count()
{
return m_tailIndex($) - m_headIndex($);
}
void push(T item)
{
size_t tail = m_tailIndex($);
if (tail <= m_headIndex($) + m_mask($))
{
m_array($)[tail & m_mask($)]($) = item;
m_tailIndex($) = tail + 1;
}
else
{
m_foreignLock.lock($);
size_t head = m_headIndex($);
size_t count = Count();
if (count >= m_mask($))
{
size_t arraySize = m_arraySize($);
size_t mask = m_mask($);
jvar<T>* newArray = new jvar<T> [arraySize * 2];
jvar<T>* arr = m_array($);
for (size_t i = 0; i != count; ++i)
newArray[i]($) = arr[(i + head) & mask]($);
m_array($) = newArray;
m_arraySize($) = arraySize * 2;
m_headIndex($) = 0;
m_tailIndex($) = count;
tail = count;
m_mask($) = (mask * 2) | 1;
}
m_array($)[tail & m_mask($)]($) = item;
m_tailIndex($) = tail + 1;
m_foreignLock.unlock($);
}
}
bool pop(T& item)
{
size_t tail = m_tailIndex($);
if (tail == 0)
return false;
tail -= 1;
m_tailIndex($) = tail;
if (m_headIndex($) <= tail)
{
item = m_array($)[tail & m_mask($)]($);
return true;
}
else
{
m_foreignLock.lock($);
if (m_headIndex($) <= tail)
{
item = m_array($)[tail & m_mask($)]($);
m_foreignLock.unlock($);
return true;
}
else
{
m_tailIndex($) = tail + 1;
m_foreignLock.unlock($);
return false;
}
}
}
bool steal(T& item)
{
if (false == m_foreignLock.try_lock($))
return false;
size_t head = m_headIndex($);
m_headIndex($) = head + 1;
if (head < m_tailIndex($))
{
item = m_array($)[head & m_mask($)]($);
m_foreignLock.unlock($);
return true;
}
else
{
m_headIndex($) = head;
m_foreignLock.unlock($);
return false;
}
}
private:
static size_t const initial_size = 2;
jvar<jvar<T>*> m_array;
jvar<size_t> m_mask;
jvar<size_t> m_arraySize;
jvolatile<size_t> m_headIndex;
jvolatile<size_t> m_tailIndex;
mutex m_foreignLock;
};
struct ws_deque_test : rl::test_suite<ws_deque_test, 2>
{
ws_deque<int> q;
bool state [2];
void before()
{
state[0] = true;
state[1] = true;
}
void after()
{
RL_ASSERT(state[0] == false);
RL_ASSERT(state[1] == false);
}
void thread(unsigned index)
{
if (0 == index)
{
q.push(1);
q.push(2);
int item = 0;
bool res = q.pop(item);
RL_ASSERT(res && item == 2);
RL_ASSERT(state[1]);
state[1] = false;
item = 0;
res = q.pop(item);
if (res)
{
RL_ASSERT(state[0]);
state[0] = false;
}
item = 0;
res = q.pop(item);
RL_ASSERT(res == false);
}
else
{
int item = 0;
bool res = q.steal(item);
if (res)
{
RL_ASSERT(item == 1);
RL_ASSERT(state[0]);
state[0] = false;
}
}
}
};
int main()
{
rl::simulate<ws_deque_test>();
}

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="java_ws_deque"
ProjectGUID="{9E88433F-779E-4461-9963-35E3338873AC}"
RootNamespace="java_ws_deque"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\java_ws_deque.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,7 @@
#pragma once
#include "../../relacy/pch.hpp"

View File

@@ -0,0 +1,511 @@
#include "stdafx.h"
#ifdef RL_TEST
#define ATOMIC(x) rl::atomic<x>
#define VAR(x) rl::var<x>
#define ATOMIC_FETCH_ADD(x, v) x($).fetch_add(v)
#define ATOMIC_COMPARE_EXCHANGE(x, c, v) x($).compare_exchange(c, v)
#define LOAD_ACQ(x) x($).load(rl::memory_order_acquire)
#define STORE_REL(x, v) x($).store(v, rl::memory_order_release)
#else
#define ATOMIC(x) x volatile
#define VAR(x) x
#define ATOMIC_FETCH_ADD(x, v) _InterlockedExchangeAdd((long*)&x, v)
#define ATOMIC_COMPARE_EXCHANGE(x, c, v) interlocked_compare_exchange(x, c, v)
#define LOAD_ACQ(x) x
#define STORE_REL(x, v) x = v
template<typename T>
bool interlocked_compare_exchange(T& x, T& c, T v)
{
T c0 = _InterlockedCompareExchange((long*)&x), v, c);
if (c0 == c)
{
return true;
}
else
{
c = c0;
return false;
}
}
#endif
//#include "pcx.h"
/*
template<typename T>
class mpmcq
{
public:
mpmcq()
{
STORE_REL(head_, alloc_block());
STORE_REL(tail_, LOAD_ACQ(head_));
}
void enqueue(T v)
{
for (;;)
{
block* b = LOAD_ACQ(head_);
unsigned raw = ATOMIC_FETCH_ADD(b->state_, state_head_inc);
unsigned idx = raw >> state_head_pos;
if (idx < item_count)
{
STORE_REL(b->data_[idx], v);
return;
}
unsigned last = raw & state_last_msk;
if (0 == last)
{
ATOMIC_COMPARE_EXCHANGE(head_, b, b+1);
}
else
{
block* b2 = LOAD_ACQ(b->next_);
if (b2)
{
ATOMIC_COMPARE_EXCHANGE(head_, b, b2);
}
else
{
b2 = alloc_block();
block* b3 = 0;
if (ATOMIC_COMPARE_EXCHANGE(b->next_, b3, b2))
{
ATOMIC_COMPARE_EXCHANGE(head_, b, b2);
}
else
{
for (;;)
{
b = LOAD_ACQ(head_);
while (0 == (LOAD_ACQ(b->state_) & state_last_msk))
b = b + 1;
while (LOAD_ACQ(b->next_))
b = LOAD_ACQ(b->next_) + block_count - 1;
b3 = 0;
if (ATOMIC_COMPARE_EXCHANGE(b->next_, b3, b2))
break;
}
}
}
}
}
}
T dequeue()
{
for (;;)
{
block* b = LOAD_ACQ(tail_);
unsigned cmp = LOAD_ACQ(b->state_);
unsigned tail = cmp & (state_last_msk - 1);
if (tail < item_count)
{
unsigned head = cmp >> state_head_pos;
if (tail < head)
{
unsigned xchg = cmp + state_tail_inc;
if (ATOMIC_COMPARE_EXCHANGE(b->state_, cmp, xchg))
{
for (;;)
{
T v = LOAD_ACQ(b->data_[tail]);
if (v != T())
return v;
rl::yield($);
}
}
}
else
{
return T();
}
}
else
{
unsigned last = cmp & state_last_msk;
if (0 == last)
{
ATOMIC_COMPARE_EXCHANGE(tail_, b, b+1);
}
else
{
block* b2 = LOAD_ACQ(b->next_);
if (0 == b2)
return T();
ATOMIC_COMPARE_EXCHANGE(tail_, b, b2);
}
}
}
}
private:
static unsigned const state_head_pos = 7;
static unsigned const state_head_inc = 1 << state_head_pos;
static unsigned const state_last_msk = 1 << 6;
static unsigned const state_tail_inc = 1 << 0;
static unsigned const item_count = 2;
static unsigned const block_count = 16;
struct block
{
//unsigned head_ : 24;
//unsigned last_ : 1;
//unsigned tail_ : 7;
ATOMIC(unsigned) state_;
ATOMIC(block*) next_;
ATOMIC(T) data_ [item_count];
};
struct superblock
{
block blocks_ [block_count];
};
char pad0_ [64];
ATOMIC(block*) head_;
char pad1_ [64];
ATOMIC(block*) tail_;
char pad2_ [64];
block* alloc_block()
{
superblock* sb = RL_NEW(superblock);
for (int x = 0; x != block_count; ++x)
{
block* b = &sb->blocks_[x];
STORE_REL(b->state_, 0);
STORE_REL(b->next_, 0);
for (int y = 0; y != item_count; ++y)
{
STORE_REL(b->data_[y], 0);
}
}
STORE_REL(sb->blocks_[block_count - 1].state_, 1 * state_head_inc + 1 * state_tail_inc + state_last_msk);
return &sb->blocks_[0];
}
};
struct test_mpmc : rl::test_suite<test_mpmc, 6>
{
mpmcq<int> q;
void thread(unsigned idx)
{
if (idx < thread_count / 2)
{
for (int i = 0; i != 2; ++i)
q.enqueue(1);
}
else
{
for (int i = 0; i != 2; ++i)
q.dequeue();
}
}
};
*/
struct thread_node
{
rl::var<thread_node*> next;
rl::var<size_t> count;
rl::var<size_t> unconsumed;
rl::HANDLE sema;
rl::CRITICAL_SECTION mtx;
};
void on_thread_exit(thread_node*& t_thread_node)
{
thread_node* head = t_thread_node;
thread_node* my = 0;
if (head)
{
rl::EnterCriticalSection(&head->mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
if (head->next($))
{
my = head->next($);
head->next($) = (thread_node*)my->next($);
}
else
{
my = head;
}
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&head->mtx, $);
while (my->unconsumed($))
{
rl::WaitForSingleObject(my->sema, rl::RL_INFINITE, $);
my->unconsumed($) -= 1;
}
rl::DeleteCriticalSection(&my->mtx, $);
rl::CloseHandle(my->sema, $);
RL_DELETE(my);
}
}
struct eventcount
{
eventcount()
{
root($) = 0;
rl::InitializeCriticalSection(&mtx, $);
}
~eventcount()
{
rl::DeleteCriticalSection(&mtx, $);
}
void prepare_wait(thread_node*& t_thread_node)
{
thread_node* my = 0;
thread_node* head = t_thread_node;
if (head)
{
rl::EnterCriticalSection(&head->mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
//RL_ASSERT(head->status == stat_root);
RL_ASSERT (root($) != head);
if (head->next($))
{
my = head->next($);
head->next($) = (thread_node*)my->next($);
my->next($) = 0;
//node_status st;
//if (stat_bucket != (st = (node_status)_InterlockedExchange(&my->status, stat_private)))
// __asm int 3;
RL_ASSERT (0 == my->count($));
}
else
{
my = head;
//node_status st;
//if (stat_root != (st = (node_status)_InterlockedExchange(&my->status, stat_private)))
// __asm int 3;
RL_ASSERT(0 == my->count($));
}
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&head->mtx, $);
}
else
{
my = RL_NEW thread_node;
my->next($) = 0;
my->count($) = 0;
my->unconsumed($) = 0;
my->sema = rl::CreateSemaphore(0, 0, LONG_MAX, 0, $);
//my->status = stat_private;
rl::InitializeCriticalSection(&my->mtx, $);
}
while (my->unconsumed($))
{
rl::WaitForSingleObject(my->sema, rl::RL_INFINITE, $);
my->unconsumed($) -= 1;
}
RL_ASSERT(0 == my->next($));
RL_ASSERT(0 == my->count($));
//if (my->status != stat_private) __asm int 3;
rl::EnterCriticalSection(&mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
RL_ASSERT(root($) != my);
if (root($))
{
my->next($) = (thread_node*)((thread_node*)root($))->next($);
((thread_node*)root($))->next($) = my;
//node_status st;
//if (stat_private != (st = (node_status)_InterlockedExchange(&my->status, stat_bucket)))
// __asm int 3;
my = root($);
}
else
{
root($) = my;
//node_status st;
//if (stat_private != (st = (node_status)_InterlockedExchange(&my->status, stat_root)))
// __asm int 3;
}
((thread_node*)root($))->count($) += 1;
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
t_thread_node = my;
}
void wait(thread_node*& t_thread_node)
{
thread_node* head = t_thread_node;
if (head == root($))
{
rl::WaitForSingleObject(head->sema, rl::RL_INFINITE, $);
}
else
{
rl::EnterCriticalSection(&head->mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
head->unconsumed($) += 1;
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&head->mtx, $);
}
}
void retire_wait(thread_node*& t_thread_node)
{
thread_node* head = t_thread_node;
if (head == root($))
{
rl::EnterCriticalSection(&mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
if (head == root($))
{
thread_node* my = 0;
head->count($) -= 1;
if (head->next($))
{
my = head->next($);
head->next($) = (thread_node*)my->next($);
my->next($) = 0;
}
else
{
my = head;
root($) = 0;
}
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
//my->status = stat_root;
t_thread_node = my;
return;
}
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
}
rl::EnterCriticalSection(&head->mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
head->unconsumed($) += 1;
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&head->mtx, $);
}
void signal_all()
{
//std::
//_mm_mfence();
thread_node* head = root($);
if (0 == head)
return;
rl::EnterCriticalSection(&mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
if (head != root($))
{
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
return;
}
size_t count = head->count($);
head->count($) = 0;
root($) = 0;
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
rl::ReleaseSemaphore(head->sema, count, 0, $);
}
std::atomic<thread_node*> root;
rl::CRITICAL_SECTION mtx;
};
struct test_ec : rl::test_suite<test_ec, 8>
{
std::atomic<int> x [2];
eventcount ec;
void before()
{
x[0]($) = 0;
x[1]($) = 0;
}
void thread(unsigned idx)
{
if (idx < 4)
{
for (int i = 0; i != 3; ++i)
{
x[idx % 2]($).fetch_add(1);
ec.signal_all();
}
}
else
{
thread_node* my = 0;
for (int i = 0; i != 3; ++i)
{
for (;;)
{
int cmp = x[idx % 2]($);
if (cmp > 0)
{
if (x[idx % 2]($).compare_exchange(cmp, cmp - 1))
break;
}
else
{
for (;;)
{
ec.prepare_wait(my);
cmp = x[idx % 2]($);
if (cmp > 0)
{
ec.retire_wait(my);
break;
}
ec.wait(my);
cmp = x[idx % 2]($);
if (cmp > 0)
{
break;
}
}
}
}
}
on_thread_exit(my);
}
}
};
int main()
{
rl::test_params p;
p.iteration_count = 20000000;
p.initial_state = "10000000";
rl::simulate<test_ec>(p);
}

View File

@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="mpmc"
ProjectGUID="{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}"
RootNamespace="mpmc"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\mpmc.cpp"
>
</File>
<File
RelativePath="..\pcx.h"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,481 @@
#pragma once
#include <intrin.h>
#pragma intrinsic (_InterlockedExchangeAdd)
#pragma intrinsic (_InterlockedCompareExchange)
//#define PCX_DEBUG
#ifdef PCX_DEBUG
#include <sstream>
#include <windows.h>
#endif
namespace rl
{
size_t const cacheline_size = 64;
struct pcx_node
{
typedef void (*pcx_dtor_t)(pcx_node*);
ATOMIC(pcx_node*) pcx_next_;
ATOMIC(pcx_dtor_t) pcx_dtor_;
};
namespace pcx_int
{
unsigned const word_bits = 32;
unsigned const collector_bits = 4;
unsigned const collector_count = 1 << collector_bits;
unsigned const counter_inc = 1 << (collector_bits * 2);
unsigned const is_current_inc = 1;
unsigned const back_link_inc = 2;
struct master;
struct collector;
struct local_collector
{
pcx_node* defer_head_;
pcx_node defer_tail_;
unsigned defer_size_;
};
struct thread_int
{
pcx_int::master* master_;
pcx_int::collector* collectors_;
unsigned recursion_count_;
unsigned is_acquired_;
unsigned collector_index_;
unsigned last_seen_collector_index_;
unsigned flush_tail_;
pcx_node* defer_head_;
pcx_node defer_tail_;
unsigned defer_size_;
unsigned promote_;
local_collector local_collectors_ [collector_count];
};
}
class pcx_thread : private pcx_int::thread_int
{
public:
static pcx_thread& get();
void acquire();
void release();
void defer(pcx_node* node, pcx_node::pcx_dtor_t dtor);
void flush();
void promote();
void quiescent();
void init();
void deinit();
private:
unsigned acquire_impl();
void release_impl(unsigned, unsigned);
void flush_impl();
void local_flush();
void quiescent_impl();
friend void init();
friend void deinit();
friend void thread_callback(bool);
};
namespace pcx_int
{
struct master
{
char pad0_ [64];
unsigned garbage_threshold_;
char pad1_ [64];
struct state_part
{
unsigned current_collector_ : collector_bits;
unsigned collector_tail_ : collector_bits;
unsigned outer_counter_ : word_bits - 2 * collector_bits;
};
union state
{
long whole_;
state_part part_;
};
state state_;
char pad2_ [64];
state state_copy_;
char pad3_ [64];
};
struct collector
{
char pad0_ [64];
pcx_node* defer_list_head_;
unsigned defer_list_size_;
char pad1_ [64];
struct state_part
{
unsigned is_current_ : 1;
unsigned back_link_ : 1;
unsigned pad_ : collector_bits * 2 - 2;
unsigned inner_counter_ : word_bits - 2 * collector_bits;
};
union state
{
long whole_;
state_part part_;
};
state state_;
char pad2_ [64];
};
__declspec(selectany)
master g_master;
__declspec(selectany)
collector g_collectors [collector_count];
__declspec(selectany, thread)
thread_int* g_thread_instance;
typedef void (__stdcall nt_tls_cb_t)(void*, unsigned long, void*);
nt_tls_cb_t on_tls_callback;
#pragma data_seg(push, old_seg)
#pragma data_seg(".CRT$XLB")
__declspec(selectany, dllexport)
nt_tls_cb_t* volatile p_thread_callback = on_tls_callback;
#pragma data_seg(pop, old_seg)
inline void __stdcall on_tls_callback(void*, unsigned long reason, void*)
{
if (1 == reason)
{
init();
thread_callback(true);
}
else if (0 == reason)
{
thread_callback(false);
deinit();
}
if (2 == reason)
{
thread_callback(true);
}
else if (3 == reason)
{
thread_callback(false);
}
}
}
inline void init()
{
using namespace pcx_int;
master& m = g_master;
m.garbage_threshold_ = 128;
m.state_.part_.current_collector_ = 0;
m.state_.part_.collector_tail_ = 0;
m.state_.part_.outer_counter_ = 0;
m.state_copy_.part_.current_collector_ = 0;
m.state_copy_.part_.collector_tail_ = 0;
m.state_copy_.part_.outer_counter_ = 0;
for (unsigned i = 0; i != collector_count; ++i)
{
collector& c = g_collectors[i];
c.defer_list_head_ = 0;
c.defer_list_size_ = 0;
c.state_.part_.is_current_ = 1;
c.state_.part_.back_link_ = 1;
c.state_.part_.inner_counter_ = 0;
}
g_collectors[0].state_.part_.back_link_ = 0;
}
inline void deinit()
{
using namespace pcx_int;
pcx_thread::get().release_impl(g_master.state_.part_.current_collector_, is_current_inc);
}
inline void thread_callback(bool init)
{
if (init)
{
g_thread_instance = RL_NEW pcx_thread ();
pcx_thread::get().init();
}
else
{
pcx_thread::get().deinit();
RL_DELETE(g_thread_instance);
g_thread_instance = 0;
}
}
inline pcx_thread& pcx_thread::get()
{
return static_cast<pcx_thread&>(*pcx_int::g_thread_instance);
}
inline unsigned pcx_thread::acquire_impl()
{
using namespace pcx_int;
long const prev =
_InterlockedExchangeAdd(
&master_->state_.whole_, counter_inc);
master::state_part u = {prev};
#ifdef PCX_DEBUG
std::ostringstream ss;
ss << "[PCX] thread " << this << " acquire " << u.current_collector_ << "\n";
OutputDebugStringA(ss.str().c_str());
#endif
if (u.current_collector_ == flush_tail_
&& local_collectors_[flush_tail_].defer_size_)
{
local_flush();
}
return u.current_collector_;
}
inline void pcx_thread::release_impl(unsigned index, unsigned count)
{
using namespace pcx_int;
collector& c = collectors_[index];
unsigned const prev =
_InterlockedExchangeAdd(
&c.state_.whole_, (unsigned)-(int)count);
#ifdef PCX_DEBUG
std::ostringstream ss;
ss << "[PCX] thread " << this << " release " << index << "\n";
OutputDebugStringA(ss.str().c_str());
#endif
if (0 == prev - count)
{
pcx_node* curr = c.defer_list_head_;
while (curr)
{
pcx_node* next = curr->pcx_next_;
curr->pcx_dtor_(curr);
curr = next;
}
c.defer_list_head_ = 0;
c.defer_list_size_ = 0;
c.state_.part_.back_link_ = 1;
c.state_.part_.is_current_ = 1;
long u;
if (index != collector_count - 1)
u = collector_count;
else
u = -(long)(collector_count * (collector_count - 1));
_InterlockedExchangeAdd(&master_->state_.whole_, u);
release_impl((index + 1) % collector_count, back_link_inc);
}
}
inline void pcx_thread::flush_impl()
{
using namespace pcx_int;
_mm_mfence();
master::state state = master_->state_;
last_seen_collector_index_ = state.part_.current_collector_;
collector& gc = collectors_[state.part_.current_collector_];
local_collector& lc = local_collectors_[state.part_.current_collector_];
lc.defer_head_->pcx_next_ = defer_tail_.pcx_next_;
lc.defer_head_ = defer_tail_.pcx_next_;
lc.defer_size_ += defer_size_;
defer_head_ = &defer_tail_;
defer_tail_.pcx_next_ = 0;
defer_size_ = 0;
if (master_->garbage_threshold_ < lc.defer_size_ || promote_)
{
master::state cmp;
master::state val;
do
{
cmp = master_->state_;
if (cmp.part_.current_collector_ != last_seen_collector_index_)
{
promote_ = 0;
return;
}
unsigned next_index = (last_seen_collector_index_ + 1) % collector_count;
if (cmp.part_.collector_tail_ == next_index)
return;
val = cmp;
val.part_.current_collector_ += 1;
val.part_.outer_counter_ = 0;
}
while (cmp.whole_ != _InterlockedCompareExchange(
(long*)&master_->state_.whole_, val.whole_, cmp.whole_));
last_seen_collector_index_ = val.part_.current_collector_;
promote_ = 0;
_InterlockedIncrement((long*)&master_->state_copy_.whole_);
_InterlockedExchangeAdd((long*)&gc.state_.whole_,
cmp.part_.outer_counter_ * counter_inc - is_current_inc);
}
}
__declspec(noinline)
inline void pcx_thread::local_flush()
{
using namespace pcx_int;
if (flush_tail_ == master_->state_.part_.collector_tail_)
return;
#ifdef PCX_DEBUG
std::ostringstream ss;
ss << "[PCX] thread " << this << " flush " << flush_tail_ << "\n";
OutputDebugStringA(ss.str().c_str());
#endif
local_collector& lc = local_collectors_[flush_tail_];
pcx_node* curr = lc.defer_tail_.pcx_next_;
while (curr)
{
#ifdef PCX_DEBUG
std::ostringstream ss;
ss << "[PCX] thread " << this << " destroy " << curr << "\n";
OutputDebugStringA(ss.str().c_str());
#endif
pcx_node* next = curr->pcx_next_;
curr->pcx_dtor_(curr);
curr = next;
}
lc.defer_head_ = &lc.defer_tail_;
lc.defer_tail_.pcx_next_ = 0;
lc.defer_size_ = 0;
flush_tail_ = (flush_tail_ + 1) % collector_count;
}
__declspec(noinline)
inline void pcx_thread::quiescent_impl()
{
using namespace pcx_int;
if (defer_size_)
flush_impl();
release_impl(collector_index_, counter_inc);
collector_index_ = acquire_impl();
}
inline void pcx_thread::acquire()
{
using namespace pcx_int;
recursion_count_ += 1;
if (1 != recursion_count_)
return;
if (is_acquired_)
return;
collector_index_ = acquire_impl();
last_seen_collector_index_ = collector_index_;
is_acquired_ = 1;
}
inline void pcx_thread::release()
{
using namespace pcx_int;
recursion_count_ -= 1;
if (0 == recursion_count_)
{
if (master_->state_copy_.part_.current_collector_ != collector_index_
|| promote_)
{
if (defer_size_)
flush_impl();
release_impl(collector_index_, counter_inc);
is_acquired_ = 0;
}
}
if (flush_tail_ != last_seen_collector_index_)
{
local_flush();
}
}
inline void pcx_thread::quiescent()
{
if (master_->state_copy_.part_.current_collector_ != collector_index_
|| promote_)
{
quiescent_impl();
}
if (flush_tail_ != last_seen_collector_index_)
{
local_flush();
}
}
inline void pcx_thread::defer(pcx_node* node, pcx_node::pcx_dtor_t dtor)
{
using namespace pcx_int;
node->pcx_next_ = 0;
node->pcx_dtor_ = dtor;
defer_head_->pcx_next_ = node;
defer_head_ = node;
defer_size_ += 1;
}
inline void pcx_thread::flush()
{
using namespace pcx_int;
if (recursion_count_)
return;
if (0 == is_acquired_)
return;
if (defer_size_)
flush_impl();
release_impl(collector_index_, counter_inc);
is_acquired_ = 0;
}
inline void pcx_thread::promote()
{
promote_ = 1;
}
inline void pcx_thread::init()
{
using namespace pcx_int;
master_ = &g_master;
collectors_ = g_collectors;
defer_head_ = &defer_tail_;
defer_tail_.pcx_next_ = 0;
for (unsigned i = 0; i != collector_count; ++i)
{
local_collectors_[i].defer_head_ = &local_collectors_[i].defer_tail_;
}
}
inline void pcx_thread::deinit()
{
flush();
}
}

View File

@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// ws_deque.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@@ -0,0 +1,10 @@
#pragma once
#pragma warning (disable: 4201)
//#define RL_GC
#define RL_MSVC_OUTPUT
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="mutex_business_logic"
ProjectGUID="{B03A7216-E196-44C6-8861-C77D90055512}"
RootNamespace="mutex_business_logic"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\mutex_business_logic.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,131 @@
#include "stdafx.h"
#include "../../relacy/relacy_std.hpp"
class business_logic
{
public:
typedef unsigned account_id_t;
typedef double balance_t;
business_logic()
{
pthread_rwlock_init(&accounts_guard, 0);
}
~business_logic()
{
pthread_rwlock_destroy(&accounts_guard);
}
bool add_account(account_id_t acc_id, balance_t balance)
{
pthread_rwlock_wrlock(&accounts_guard);
if (accounts.find(acc_id) != accounts.end())
{
pthread_rwlock_unlock(&accounts_guard);
return false;
}
accounts[acc_id].balance = balance;
pthread_rwlock_unlock(&accounts_guard);
return true;
}
bool transfer_balance(account_id_t acc_id1, account_id_t acc_id2, balance_t amount)
{
if (acc_id1 == acc_id2)
return true;
pthread_rwlock_rdlock(&accounts_guard);
if (accounts.find(acc_id1) == accounts.end()
|| accounts.find(acc_id2) == accounts.end())
{
pthread_rwlock_unlock(&accounts_guard);
return false;
}
account_info& acc1 = accounts[acc_id1];
account_info& acc2 = accounts[acc_id2];
if (acc_id1 > acc_id2)
{
pthread_mutex_lock(&acc1.mtx);
pthread_mutex_lock(&acc2.mtx);
}
else
{
pthread_mutex_lock(&acc2.mtx);
pthread_mutex_lock(&acc1.mtx);
}
pthread_rwlock_unlock(&accounts_guard);
acc1.balance -= amount;
acc2.balance += amount;
pthread_mutex_unlock(&acc1.mtx);
pthread_mutex_unlock(&acc2.mtx);
return true;
}
private:
struct account_info
{
balance_t balance;
pthread_mutex_t mtx;
account_info()
: balance()
{
pthread_mutex_init(&mtx, 0);
}
account_info(account_info const& acc)
: balance(acc.balance)
{
pthread_mutex_init(&mtx, 0);
}
~account_info()
{
pthread_mutex_destroy(&mtx);
}
};
typedef std::map<account_id_t, account_info> account_map_t;
account_map_t accounts;
pthread_rwlock_t accounts_guard;
};
struct business_logic_test : rl::test_suite<business_logic_test, 2>
{
business_logic bl;
static size_t const account_count = 4;
void before()
{
for (size_t i = 0; i != account_count; ++i)
{
bool rv = bl.add_account(i, i * 10.0);
RL_ASSERT(rv);
}
}
void thread(unsigned /*index*/)
{
business_logic::account_id_t acc1 = rl::rand(account_count);
business_logic::account_id_t acc2 = rl::rand(account_count);
bool rv = bl.transfer_balance(acc1, acc2, 1.0);
RL_ASSERT(rv);
}
};
int main()
{
rl::simulate<business_logic_test>();
}

View File

@@ -0,0 +1,14 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#define RL_MSVC_OUTPUT
//#define RL_DEBUGBREAK_ON_FAILURE
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="peterson"
ProjectGUID="{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
RootNamespace="peterson"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\peterson.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,200 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="peterson"
ProjectGUID="{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
RootNamespace="peterson"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\peterson.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,469 @@
#include "stdafx.h"
#include "../../relacy/relacy_std.hpp"
#include "../../relacy/windows.h"
struct peterson_mutex_test : rl::test_suite<peterson_mutex_test, 2>
{
std::atomic<int> flag0;
std::atomic<int> flag1;
std::atomic<int> turn;
rl::var<int> data;
void before()
{
flag0($) = 0;
flag1($) = 0;
turn($) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
flag0($).store(1);
turn($).store(1);
while (flag1($).load()
&& 1 == turn($).load());
data($) = 1;
flag0($).store(0);
}
else
{
flag1($).store(1);
turn($).store(0);
while (flag0($).load()
&& 0 == turn($).load());
data($) = 2;
flag1($).store(0);
}
}
};
struct peterson_mutex_test2 : rl::test_suite<peterson_mutex_test2, 2>
{
std::atomic<int> flag0;
std::atomic<int> flag1;
std::atomic<int> turn;
rl::var<int> data;
void before()
{
flag0($) = 0;
flag1($) = 0;
turn($) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
flag0.store(1, rl::memory_order_relaxed);
turn.exchange(1, rl::memory_order_acq_rel);
while (flag1.load(rl::memory_order_acquire)
&& 1 == turn.load(rl::memory_order_relaxed))
rl::yield(1, $);
data($) = 1;
flag0.store(0, rl::memory_order_release);
}
else
{
flag1.store(1, rl::memory_order_relaxed);
turn.exchange(0, rl::memory_order_acq_rel);
while (flag0.load(rl::memory_order_acquire)
&& 0 == turn.load(rl::memory_order_relaxed))
rl::yield(1, $);
data($) = 2;
flag1.store(0, rl::memory_order_release);
}
}
};
struct peterson_mutex_test3 : rl::test_suite<peterson_mutex_test3, 2>
{
std::atomic<int> flag0;
std::atomic<int> flag1;
std::atomic<int> turn;
rl::var<int> data;
void before()
{
flag0($) = 0;
flag1($) = 0;
turn($) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
flag0.store(1, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
turn.store(1, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
while (flag1.load(std::memory_order_acquire)
&& 1 == turn.load(std::memory_order_relaxed));
data($) = 1;
flag0.store(0, std::memory_order_release);
}
else
{
flag1.store(1, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
turn.store(0, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
while (flag0.load(std::memory_order_acquire)
&& 0 == turn.load(std::memory_order_relaxed));
data($) = 2;
flag1.store(0, std::memory_order_release);
}
}
};
// FAILS WITH DATA RACE
struct peterson_mutex_test4 : rl::test_suite<peterson_mutex_test4, 2>
{
std::atomic<int> flag0;
std::atomic<int> flag1;
std::atomic<int> turn;
rl::var<int> data;
void before()
{
flag0($) = 0;
flag1($) = 0;
turn($) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
flag0.exchange(1, rl::memory_order_acq_rel);
turn.store(1, rl::memory_order_release);
while (flag1.load(rl::memory_order_acquire)
&& 1 == turn.load(rl::memory_order_acquire))
rl::yield(1, $);
data($) = 1;
flag0.store(0, rl::memory_order_release);
}
else
{
flag1.exchange(1, rl::memory_order_acq_rel);
turn.store(0, rl::memory_order_release);
while (flag0.load(rl::memory_order_acquire)
&& 0 == turn.load(rl::memory_order_relaxed))
rl::yield(1, $);
data($) = 2;
flag1.store(0, rl::memory_order_release);
}
}
};
class eventcount
{
public:
typedef unsigned state_t;
eventcount()
{
state_.store(0, std::memory_order_relaxed);
sema_ = CreateSemaphore(0, 0, LONG_MAX, 0);
}
~eventcount()
{
CloseHandle(sema_);
}
state_t prepare()
{
return state_.fetch_add(waiters_inc, std::memory_order_seq_cst);
}
void retire()
{
state_.fetch_add((state_t)-(int)waiters_inc, std::memory_order_seq_cst);
}
void wait(state_t cmp)
{
WaitForSingleObject(sema_, INFINITE);
state_t cmp0 = state_.load(std::memory_order_seq_cst);
if ((cmp & generation_mask) == (cmp0 & generation_mask))
{
state_.fetch_add((state_t)-(int)waiters_inc, std::memory_order_seq_cst);
ReleaseSemaphore(sema_, 1, 0);
SwitchToThread();
}
}
void signal()
{
std::atomic_thread_fence(std::memory_order_seq_cst);
signal_relaxed();
}
void signal_relaxed()
{
state_t cmp = state_.load(std::memory_order_seq_cst);
if (0 == (cmp & waiters_mask))
return;
for (;;)
{
state_t xchg = (cmp & ~waiters_mask) + generation_inc;
if (state_.compare_exchange_weak(cmp, xchg, std::memory_order_seq_cst))
{
ReleaseSemaphore(sema_, cmp & waiters_mask, 0);
return;
}
if (0 == (cmp & waiters_mask))
return;
}
}
private:
std::atomic<state_t> state_;
HANDLE sema_;
static state_t const waiters_inc = 1;
static state_t const waiters_mask = (1 << 20) - 1;
static state_t const generation_inc = 1 << 20;
static state_t const generation_mask = ~waiters_mask;
eventcount(eventcount const&);
eventcount& operator = (eventcount const&);
};
class eventcount_blocking
{
public:
eventcount_blocking(eventcount& ec)
: ec_(ec)
{
cmp_ = ec_.prepare();
wait_ = false;
}
void wait()
{
RL_ASSERT(false == wait_);
wait_ = true;
ec_.wait(cmp_);
}
~eventcount_blocking()
{
if (false == wait_)
ec_.retire();
}
private:
eventcount& ec_;
eventcount::state_t cmp_;
bool wait_;
eventcount_blocking(eventcount_blocking const&);
eventcount_blocking& operator = (eventcount_blocking const&);
};
struct signaling_test : rl::test_suite<signaling_test, 6>
{
//rl::HANDLE var_wait_for_items;
//rl::CRITICAL_SECTION mtx_items_avail;
//std::atomic<unsigned> n_waiting_consumers;
//rl::var<unsigned> consumer_wait_generation;
//rl::var<unsigned> n_consumers_to_wakeup;
eventcount ec_;
static int const max_queue_length = 4;
int queue [max_queue_length];
int queue_head;
int queue_tail;
int queue_head_data;
int queue_tail_data;
void before()
{
//var_wait_for_items = rl::CreateEvent(0, 1, 0, 0, $);
//rl::InitializeCriticalSection(&mtx_items_avail, $);
//n_waiting_consumers($) = 0;
//consumer_wait_generation($) = 0;
//n_consumers_to_wakeup($) = 0;
for (int i = 0; i != max_queue_length; ++i)
queue[i] = 0;
queue_head = 0;
queue_tail = 0;
queue_head_data = 0;
queue_tail_data = 0;
}
void after()
{
//rl::CloseHandle(var_wait_for_items, $);
//rl::DeleteCriticalSection(&mtx_items_avail, $);
}
struct enqueue_desc
{
int pos;
void output(std::ostream& s) const
{
s << "enqueue " << pos;
}
};
void enqueue()
{
queue[queue_head++] = ++queue_head_data;
RL_HIST_IMPL(rl::ctx(), $, enqueue_desc) {queue_head - 1} RL_HIST_END();
signal();
}
void dequeue()
{
int my_pos = queue_tail++;
for (;;)
{
if (queue[my_pos])
{
RL_ASSERT(queue[my_pos] == my_pos + 1);
return;
}
wait(my_pos);
}
}
void signal()
{
ec_.signal();
/*
std::atomic_thread_fence($)(std::memory_order_seq_cst);
if (n_waiting_consumers($).load(std::memory_order_relaxed))
{
rl::EnterCriticalSection(&mtx_items_avail, $);
if (n_waiting_consumers($).load(std::memory_order_relaxed) > 0)
{
consumer_wait_generation($) += 1;
//RL_ASSERT(n_consumers_to_wakeup($) == 0);
n_consumers_to_wakeup($) = n_waiting_consumers($).load(std::memory_order_relaxed);
rl::SetEvent(var_wait_for_items, $);
}
rl::LeaveCriticalSection(&mtx_items_avail, $);
}
*/
}
void wait(int my_pos)
{
eventcount_blocking block (ec_);
if (queue[my_pos])
return;
block.wait();
/*
rl::EnterCriticalSection(&mtx_items_avail, $);
n_waiting_consumers($).store(n_waiting_consumers($).load(std::memory_order_relaxed) + 1, std::memory_order_relaxed);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
while (0 == queue[my_pos])
{
unsigned my_generation = consumer_wait_generation($);
for (;;)
{
rl::LeaveCriticalSection(&mtx_items_avail, $);
rl::WaitForSingleObject(var_wait_for_items, rl::RL_INFINITE, $);
rl::EnterCriticalSection(&mtx_items_avail, $);
if (n_consumers_to_wakeup($) > 0 && consumer_wait_generation($) != my_generation)
break;
}
if (--n_consumers_to_wakeup($) == 0)
rl::ResetEvent(var_wait_for_items, $);
}
n_waiting_consumers($).store(n_waiting_consumers($).load(std::memory_order_relaxed) - 1, std::memory_order_relaxed);
rl::LeaveCriticalSection(&mtx_items_avail, $);
*/
}
void thread(unsigned index)
{
if (index < rl::test_suite<signaling_test, 6>::params::thread_count/2+1)
{
enqueue();
}
else
{
dequeue();
}
}
};
int main()
{
rl::test_params p;
//p.search_type = rl::fair_context_bound_scheduler_type;
p.search_type = rl::sched_bound;
//p.context_bound = 1;
//p.execution_depth_limit = 100;
//p.iteration_count = 5000;
//p.initial_state = "280572";
//rl::simulate<signaling_test>(p);
rl::simulate<peterson_mutex_test>();
rl::simulate<peterson_mutex_test2>(p);
rl::simulate<peterson_mutex_test3>();
rl::simulate<peterson_mutex_test4>(p);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#define RL_MSVC_OUTPUT
//#define RL_DEBUGBREAK_ON_FAILURE
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,291 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="proxy_collector"
ProjectGUID="{31994C0C-3BAD-4F25-8BC8-3206FF349B29}"
RootNamespace="ref_counting"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="true"
FavorSizeOrSpeed="1"
OmitFramePointers="true"
EnableFiberSafeOptimizations="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
BufferSecurityCheck="false"
EnableEnhancedInstructionSet="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Profile|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="0"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalOptions="/Ob0"
EnableIntrinsicFunctions="true"
FavorSizeOrSpeed="1"
OmitFramePointers="true"
EnableFiberSafeOptimizations="true"
WholeProgramOptimization="false"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
BufferSecurityCheck="false"
EnableEnhancedInstructionSet="2"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\proxy_collector.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,299 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="proxy_collector"
ProjectGUID="{31994C0C-3BAD-4F25-8BC8-3206FF349B29}"
RootNamespace="ref_counting"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="true"
FavorSizeOrSpeed="1"
OmitFramePointers="true"
EnableFiberSafeOptimizations="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
BufferSecurityCheck="false"
EnableEnhancedInstructionSet="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Profile|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="0"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalOptions="/Ob0"
EnableIntrinsicFunctions="true"
FavorSizeOrSpeed="1"
OmitFramePointers="true"
EnableFiberSafeOptimizations="true"
WholeProgramOptimization="false"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
BufferSecurityCheck="false"
EnableEnhancedInstructionSet="2"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\original.hpp"
>
</File>
<File
RelativePath="..\proxy_collector.cpp"
>
</File>
<File
RelativePath="..\rtl.hpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,318 @@
#include "stdafx.h"
#include <stdint.h>
#include "../../relacy/relacy_std.hpp"
struct pc_sys_anchor;
struct pc_region;
struct pc_master;
typedef pc_region pc_node;
typedef void (pc_fp_dtor) (pc_region*);
struct pc_sys_anchor
{
int refcnt;
pc_region* region;
pc_sys_anchor()
{
}
pc_sys_anchor(int rc, pc_region* r = 0)
{
refcnt = rc;
region = r;
}
bool operator == (pc_sys_anchor const& right) const
{
return refcnt == right.refcnt
&& region == right.region;
}
pc_sys_anchor operator + (pc_sys_anchor const& right) const
{
pc_sys_anchor res;
res.refcnt = refcnt + right.refcnt;
res.region = (pc_region*)((intptr_t)region + (intptr_t)right.region);
return res;
}
pc_sys_anchor operator - (pc_sys_anchor const& right) const
{
pc_sys_anchor res;
res.refcnt = refcnt - right.refcnt;
res.region = (pc_region*)((intptr_t)region - (intptr_t)right.region);
return res;
}
};
std::ostream& operator << (std::ostream& s, pc_sys_anchor const& right)
{
return s << "{" << right.refcnt << "," << right.region << "}";
}
struct pc_region
{
std::atomic<pc_sys_anchor> next;
std::atomic<pc_region*> defer;
pc_region()
{
next($) = pc_sys_anchor(0, 0);
defer($) = 0;
}
void link(pc_region* next)
{
defer.store(next, rl::memory_order_relaxed);
}
void defer_node(pc_region* node)
{
pc_region* region = defer.exchange(node, rl::memory_order_release);
node->defer.store(region, rl::memory_order_relaxed);
}
};
struct pc_master
{
std::atomic<pc_sys_anchor> head;
pc_region stub_region;
pc_fp_dtor* fp_dtor;
pc_master(pc_fp_dtor* const dtor)
{
pc_sys_anchor src (0, &stub_region);
head.store(src, rl::memory_order_relaxed);
fp_dtor = dtor;
}
pc_region* acquire()
{
pc_sys_anchor cmp (head.load(rl::memory_order_relaxed));
pc_sys_anchor xchg;
do
{
xchg.refcnt = cmp.refcnt + 2;
xchg.region = cmp.region;
}
while (false == head.compare_exchange_weak(cmp, xchg, rl::memory_order_acquire));
return cmp.region;
}
void release(pc_region* region)
{
pc_sys_anchor prev = region->next.fetch_sub(2, rl::memory_order_acq_rel);
if (prev.refcnt == 3)
sys_dtor(region);
}
void mutate(pc_region* node)
{
pc_sys_anchor src (2, 0);
node->next.store(src, rl::memory_order_relaxed);
pc_sys_anchor xchg (0, node);
pc_sys_anchor cmp = head.load(rl::memory_order_relaxed);
while (false == head.compare_exchange_weak(cmp, xchg, std::memory_order_acq_rel));
pc_sys_anchor cmp2 = cmp.region->next.load(rl::memory_order_relaxed);
pc_sys_anchor xchg2;
do
{
xchg2 = pc_sys_anchor(cmp2.refcnt, node);
}
while (false == cmp.region->next.compare_exchange_weak(cmp2, xchg2, rl::memory_order_release));
pc_sys_anchor prev = cmp.region->next.fetch_add(cmp.refcnt + 1, rl::memory_order_acq_rel);
if (prev.refcnt == -cmp.refcnt)
sys_dtor(cmp.region);
}
void sys_dtor(pc_region* region)
{
int reset = 0;
pc_region* head = region;
pc_region* tail = region;
pc_sys_anchor nx = region->next.load(rl::memory_order_relaxed);
pc_region* next = nx.region;
while (next)
{
pc_sys_anchor prev = next->next.fetch_sub(2, rl::memory_order_acq_rel);
if (prev.refcnt != 3)
break;
tail = next;
nx = next->next.load(rl::memory_order_relaxed);
next = nx.region;
}
nx = tail->next.load(rl::memory_order_relaxed);
nx.region = 0;
tail->next.store(nx, rl::memory_order_relaxed);
while (head)
{
nx = head->next.load(rl::memory_order_relaxed);
pc_region* const next = nx.region;
pc_region* defer = head->defer.load(rl::memory_order_relaxed);
nx = head->next.load(rl::memory_order_relaxed);
RL_ASSERT(nx.refcnt == 1);
if (head != &stub_region)
{
head->defer.store(defer, rl::memory_order_relaxed);
defer = head;
}
else
{
reset = 1;
}
while (defer)
{
pc_region* const next = defer->defer.load(rl::memory_order_relaxed);
fp_dtor(defer);
defer = next;
}
head = next;
}
if (reset)
{
stub_region.defer.store(0, rl::memory_order_relaxed);
mutate(&stub_region);
}
}
};
struct foo_node
{
pc_node pcn;
std::atomic<foo_node*> next;
rl::var<int> data;
};
void foo_node_dtor(pc_node* pcn)
{
// yes, very fragile
foo_node* const n = (foo_node*)pcn;
delete n;
}
struct foo_list
{
std::atomic<foo_node*> head;
pc_master pc;
foo_list()
: head(0)
, pc(foo_node_dtor)
{
}
};
struct proxy_collector_test : rl::test_suite<proxy_collector_test, 4>
{
foo_list m_list;
void before()
{
m_list.head($) = 0;
}
void after()
{
foo_node* node = new foo_node;
m_list.pc.mutate(&node->pcn);
}
void thread(unsigned index)
{
if (index < 2)
{
pc_region* pcr = m_list.pc.acquire();
for (int i = 0; i != 4; ++i)
{
foo_node* node = m_list.head.load(rl::memory_order_acquire);
while (node)
{
foo_node* const next = node->next.load(rl::memory_order_acquire);
intptr_t volatile data = node->data($);
(void)data;
node = next;
}
if (2 == i)
{
m_list.pc.release(pcr);
pcr = m_list.pc.acquire();
}
}
m_list.pc.release(pcr);
}
else
{
pc_region* pcr = m_list.pc.acquire();
for (int i = 0; i != 4; ++i)
{
if (0 == (i % 2))
{
foo_node* node = new foo_node;
node->data($) = 1;
foo_node* cmp = m_list.head.load(rl::memory_order_relaxed);
do
{
node->next.store(cmp, rl::memory_order_relaxed);
}
while (false == m_list.head.compare_exchange_weak(cmp, node, rl::memory_order_release));
}
else
{
foo_node* node = m_list.head.load(rl::memory_order_acquire);
foo_node* next;
do
{
if (0 == node)
break;
next = node->next.load(rl::memory_order_relaxed);
}
while (false == m_list.head.compare_exchange_weak(node, next, rl::memory_order_acquire));
if (node)
{
//if (1 == i)
{
m_list.pc.mutate(&node->pcn);
}
//else
//{
// pcr->defer_node(&node->pcn);
//}
}
}
if (i % 2)
{
m_list.pc.release(pcr);
pcr = m_list.pc.acquire();
}
}
m_list.pc.release(pcr);
}
}
};
int main()
{
rl::test_params params;
params.iteration_count = 1000;
rl::simulate<proxy_collector_test>(params);
}

View File

@@ -0,0 +1,11 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#include "../../relacy/pch.hpp"
#include <sstream>

View File

@@ -0,0 +1,208 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="ref_counting"
ProjectGUID="{31994C0C-3BAD-4F25-8BC8-3206FF349B28}"
RootNamespace="ref_counting"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="true"
FavorSizeOrSpeed="1"
OmitFramePointers="true"
EnableFiberSafeOptimizations="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
BufferSecurityCheck="false"
EnableEnhancedInstructionSet="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\ref_counting.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="ref_counting"
ProjectGUID="{31994C0C-3BAD-4F25-8BC8-3206FF349B28}"
RootNamespace="ref_counting"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="true"
FavorSizeOrSpeed="1"
OmitFramePointers="true"
EnableFiberSafeOptimizations="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
BufferSecurityCheck="false"
EnableEnhancedInstructionSet="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\ref_counting.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,269 @@
#include "stdafx.h"
#include "../../relacy/relacy_std.hpp"
struct rc_object
{
std::atomic<int> rc;
rl::var<int> data;
void acquire()
{
rc.fetch_add(1, rl::memory_order_acquire);
}
void release()
{
if (1 == rc.fetch_sub(1, rl::memory_order_release))
{
rc.load(rl::memory_order_acquire);
data($) = 0;
delete this;
}
}
rc_object(int data)
: rc(1)
, data(data)
{
}
};
void post_to_channel(rl::atomic<rc_object*>& ch, rc_object* obj)
{
obj->acquire();
rl::backoff b;
for (;;)
{
rc_object* cmp = 0;
if (ch.compare_exchange_weak(cmp, obj, rl::memory_order_release))
break;
b.yield($);
}
}
rc_object* get_from_channel(rl::atomic<rc_object*>& ch)
{
return ch.exchange(0, rl::memory_order_acquire);
}
struct ref_counting_test : rl::test_suite<ref_counting_test, 2>
{
std::atomic<rc_object*> channel;
void before()
{
channel($) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
rc_object* obj = new rc_object (rand());
post_to_channel(channel, obj);
int data = obj->data($);
(void)data;
obj->release();
}
else if (1 == index)
{
rl::backoff b;
for (;;)
{
rc_object* obj = get_from_channel(channel);
if (obj)
{
int data = obj->data($);
(void)data;
obj->release();
break;
}
else
{
b.yield($);
}
}
}
}
};
struct ref_counting_test2 : rl::test_suite<ref_counting_test2, 3>
{
std::atomic<rc_object*> channel01;
std::atomic<rc_object*> channel02;
std::atomic<rc_object*> channel12;
std::atomic<rc_object*> channel21;
void before()
{
channel01($) = 0;
channel02($) = 0;
channel12($) = 0;
channel21($) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
{
rc_object* obj1 = new rc_object (rand());
post_to_channel(channel01, obj1);
volatile int data = obj1->data($);
(void)data;
obj1->release();
}
{
rc_object* obj2 = new rc_object (rand());
post_to_channel(channel02, obj2);
volatile int data = obj2->data($);
(void)data;
obj2->release();
}
}
else if (1 == index)
{
rl::backoff b;
bool ch0 = false;
bool ch2 = false;
while (!ch0 || !ch2)
{
{
rc_object* obj = get_from_channel(channel01);
if (obj)
{
post_to_channel(channel12, obj);
volatile int data = obj->data($);
(void)data;
obj->release();
ch0 = true;
}
else
{
b.yield($);
}
}
{
rc_object* obj = get_from_channel(channel21);
if (obj)
{
volatile int data = obj->data($);
(void)data;
obj->release();
ch2 = true;
}
else
{
b.yield($);
}
}
}
}
else
{
rl::backoff b;
bool ch0 = false;
bool ch1 = false;
while (!ch0 || !ch1)
{
{
rc_object* obj = get_from_channel(channel02);
if (obj)
{
post_to_channel(channel21, obj);
volatile int data = obj->data($);
(void)data;
obj->release();
ch0 = true;
}
else
{
b.yield($);
}
}
{
rc_object* obj = get_from_channel(channel12);
if (obj)
{
volatile int data = obj->data($);
(void)data;
obj->release();
ch1 = true;
}
else
{
b.yield($);
}
}
}
}
}
};
struct ref_counting_test3 : rl::test_suite<ref_counting_test3, 2>
{
std::atomic<rc_object*> channel;
void before()
{
channel($) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
rc_object* obj = new rc_object (rand());
post_to_channel(channel, obj);
volatile int data = obj->data($);
(void)data;
obj->release();
}
else if (1 == index)
{
rl::backoff b;
rc_object* obj = 0;
for (;;)
{
obj = get_from_channel(channel);
if (obj)
break;
else
b.yield($);
}
obj->acquire();
obj->release();
//volatile int data = obj->data($);
//(void)data;
obj->release();
}
}
};
int main()
{
rl::test_params params;
params.context_bound = 2;
params.iteration_count = 10000;
rl::simulate<ref_counting_test>(params);
std::cout << "count: " << params.stop_iteration << std::endl;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#include "../../relacy/pch.hpp"

View File

@@ -0,0 +1,11 @@
TARGET=smr
SRC=smr.cpp
HEADERS=stdafx.h
all: ${TARGET}
${TARGET}: Makefile ${SRC} ${HEADERS}
g++ ${SRC} -O3 -g -o ${TARGET}
clean:
rm ${TARGET}

View File

@@ -0,0 +1,199 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="smr"
ProjectGUID="{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}"
RootNamespace="smr"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\smr.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,199 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="smr"
ProjectGUID="{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}"
RootNamespace="smr"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\smr.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,189 @@
#include "stdafx.h"
#include "../../relacy/relacy_std.hpp"
unsigned const thread_count = 3;
unsigned const node_count = 6;
struct smr_test : rl::test_suite<smr_test, thread_count>
{
struct node
{
std::atomic<node*> next_;
rl::var<int> data_;
};
std::atomic<node*> head_;
std::atomic<node*> hp_ [thread_count];
rl::var<node*> defer_ [thread_count][thread_count];
rl::var<int> defer_size_ [thread_count];
void before()
{
head_.store(0, std::memory_order_relaxed);
for (size_t i = 0; i != thread_count; ++i)
{
hp_[i].store(0, std::memory_order_relaxed);
VAR(defer_size_[i]) = 0;
for (size_t j = 0; j != thread_count; ++j)
VAR(defer_[i][j]) = 0;
}
}
void push(unsigned index, int data)
{
node* n = new node ();
n->VAR(data_) = data;
node* next = head_.load(std::memory_order_relaxed);
for (;;)
{
n->next_.store(next, rl::memory_order_relaxed);
if (head_.compare_exchange_weak(next, n, rl::memory_order_release))
break;
}
}
int pop(unsigned index)
{
node* n = 0;
for (;;)
{
n = smr_acquire(index, head_);
if (0 == n)
break;
node* next = n->next_.load(rl::memory_order_relaxed);
if (head_.compare_exchange_weak(n, next, rl::memory_order_acquire))
break;
smr_release(index);
}
smr_release(index);
if (n)
{
int data = n->VAR(data_);
smr_defer(index, n);
return data;
}
else
{
return 0;
}
}
void smr_pump(unsigned index)
{
node* hp [thread_count] = {};
for (size_t i = 0; i != thread_count; ++i)
{
hp[i] = hp_[i].load(std::memory_order_relaxed);
}
for (size_t i = 0; i != thread_count; ++i)
{
node* nn = VAR(defer_[index][i]);
if (nn)
{
for (size_t j = 0; j != thread_count; ++j)
{
if (nn == hp[j])
{
nn = 0;
break;
}
}
if (nn)
{
VAR(defer_[index][i]) = 0;
VAR(defer_size_[index]) -= 1;
delete nn;
}
}
}
}
void smr_defer(unsigned index, node* n)
{
std::atomic_thread_fence(std::memory_order_seq_cst);
smr_pump(index);
if (VAR(defer_size_[index]) == thread_count)
{
delete n;
}
else
{
bool found = false;
for (size_t i = 0; i != thread_count; ++i)
{
if (VAR(defer_[index][i]) == 0)
{
VAR(defer_[index][i]) = n;
found = true;
break;
}
}
RL_ASSERT(found);
VAR(defer_size_[index]) += 1;
}
}
node* smr_acquire(unsigned index, std::atomic<node*>& n)
{
node* v = 0;
for (;;)
{
v = n.load(std::memory_order_relaxed);
hp_[index].store(v, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
node* v2 = n.load(std::memory_order_acquire);
if (v2 == v)
break;
}
return v;
}
void smr_release(unsigned index)
{
hp_[index].store(0, std::memory_order_relaxed);
}
void thread(unsigned index)
{
for (unsigned i = 0; i != node_count; ++i)
{
push(index, index * thread_count + i + 1);
}
for (unsigned i = 0; i != node_count; ++i)
{
int data = pop(index);
RL_ASSERT(0 != data);
}
}
void after()
{
for (unsigned i = 0; i != ::thread_count; ++i)
{
smr_pump(i);
}
}
};
int main()
{
rl::test_params p;
//p.collect_history = true;
//p.output_history = true;
//p.initial_state = "991172";
p.iteration_count = 1000;
rl::simulate<smr_test>(p);
}

View File

@@ -0,0 +1,10 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#define RL_MSVC_OUTPUT
#include "../../relacy/pch.hpp"

View File

@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="spsc_queue"
ProjectGUID="{2F0B1A3B-27CA-47D4-A9D1-5EC66BB0A85B}"
RootNamespace="spsc_queue"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../.."
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../../.."
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\spsc_queue.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,200 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="spsc_queue"
ProjectGUID="{3F32C4FA-E451-42BC-9E65-74129120B6E4}"
RootNamespace="spsc_queue"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\spsc_queue.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,202 @@
#include "stdafx.h"
#include "../../relacy/relacy_std.hpp"
template<typename T>
class nonblocking_spsc_queue
{
public:
nonblocking_spsc_queue()
{
node* n = new node ();
VAR(head) = n;
VAR(tail) = n;
}
~nonblocking_spsc_queue()
{
RL_ASSERT(VAR(head) == VAR(tail));
delete (node*)VAR(head);
}
void enqueue(T data)
{
node* n = new node (data);
VAR(head)->next.store(n, std::memory_order_release);
VAR(head) = n;
}
bool dequeue(T& data)
{
node* t = VAR(tail);
node* n = t->next.load(std::memory_order_acquire);
if (0 == n)
return false;
data = n->VAR(data);
delete t;
VAR(tail) = n;
return true;
}
private:
struct node
{
std::atomic<node*> next;
VAR_T(T) data;
node(T data = T())
: next(0)
, data(data)
{}
};
VAR_T(node*) head;
VAR_T(node*) tail;
};
struct nonblocking_spsc_queue_test : rl::test_suite<nonblocking_spsc_queue_test, 2>
{
nonblocking_spsc_queue<int> q;
void thread(unsigned thread_index)
{
if (0 == thread_index)
{
q.enqueue(11);
}
else
{
int data = 0;
while (false == q.dequeue(data))
{}
RL_ASSERT(11 == data);
}
}
};
class eventcount
{
public:
eventcount()
: count(0)
, waiters(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_acquire);
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<unsigned> count;
VAR_T(unsigned) 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 = VAR(waiters);
VAR(waiters) = 0;
guard.unlock($);
if (w)
cv.notify_all($);
}
}
};
template<typename T>
class spsc_queue : nonblocking_spsc_queue<T>
{
public:
typedef nonblocking_spsc_queue<T> base_t;
void enqueue(T data)
{
base_t::enqueue(data);
ec.signal/*_relaxed*/();
}
T dequeue()
{
T data;
bool res = base_t::dequeue(data);
while (false == res)
{
int cmp = ec.get();
res = base_t::dequeue(data);
if (res)
break;
ec.wait(cmp);
res = base_t::dequeue(data);
if (res)
break;
}
return data;
}
private:
eventcount ec;
};
struct spsc_queue_test : rl::test_suite<spsc_queue_test, 2>
{
spsc_queue<int> 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<nonblocking_spsc_queue_test>();
rl::simulate<spsc_queue_test>();
}

View File

@@ -0,0 +1,13 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
//#define RL_MSVC_OUTPUT
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,2 @@
lock-free stack
code contains several bugs: access to freed memory and ABA problem

View File

@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="stack"
ProjectGUID="{4D6D7FC3-66D1-4F80-B434-2FDCBBFBC9F5}"
RootNamespace="stack"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stack.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,196 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="stack"
ProjectGUID="{4D6D7FC3-66D1-4F80-B434-2FDCBBFBC9F5}"
RootNamespace="stack"
Keyword="Win32Proj"
TargetFrameworkVersion="0"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stack.cpp"
>
</File>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,105 @@
#include "stdafx.h"
#include "../../relacy/relacy_std.hpp"
// TEST FAILS WITH "ACCESS TO FREED MEMORY"
class stack
{
public:
stack()
: head_(0)
{
}
void push(int data)
{
rl::var<node*> n = new node ();
n($)->data_($) = data;
node* next = head_.load(rl::memory_order_relaxed);
for (;;)
{
n($)->next_.store(next, rl::memory_order_relaxed);
if (head_.compare_exchange_weak(next, n($), rl::memory_order_release))
break;
}
}
int pop()
{
node* n = head_.load(rl::memory_order_relaxed);
for (;;)
{
if (0 == n)
break;
node* next = n->next_.load(rl::memory_order_relaxed);
if (head_.compare_exchange_weak(n, next, rl::memory_order_acquire))
break;
}
if (n)
{
int data = n->data_($);
delete n;
return data;
}
else
{
return 0;
}
}
private:
struct node
{
std::atomic<node*> next_;
rl::var<int> data_;
};
std::atomic<node*> head_;
stack(stack const&);
stack& operator = (stack const&);
};
struct stack_test : rl::test_suite<stack_test, 4>
{
stack s_;
int produced_count_;
int consumed_count_;
void before()
{
produced_count_ = 0;
consumed_count_ = 0;
}
void after()
{
typedef rl::test_suite<stack_test, 4> base_t;
RL_ASSERT(base_t::params::thread_count == produced_count_);
RL_ASSERT(base_t::params::thread_count == consumed_count_);
}
void thread(unsigned /*index*/)
{
s_.push(rand() + 1);
produced_count_ += 1;
int data = s_.pop();
RL_ASSERT(data);
consumed_count_ += 1;
}
};
int main()
{
rl::simulate<stack_test>();
}

View File

@@ -0,0 +1,8 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#include "../../relacy/pch.hpp"

View File

@@ -0,0 +1,842 @@
#include "stdafx.h"
#include "../../relacy/windows.h"
/*
#define HANDLE rl::HANDLE
#define CreateSemaphoreA rl::RL_CreateSemaphore($)
#define CreateSemaphoreW rl::RL_CreateSemaphore($)
#ifndef CreateSemaphore
# define CreateSemaphore CreateSemaphoreW
#endif
//#define CRITICAL_SECTION rl::CRITICAL_SECTION
//#define InitializeCriticalSection rl::InitializeCriticalSection($)
#define CloseHandle rl::RL_CloseHandle($)
*/
#include <stddef.h>
#if defined(WIN32) && defined(_MSC_VER)
#include <windows.h>
#include <intrin.h>
class semaphore
{
public:
semaphore()
{
h_ = rl::CreateSemaphore(0, 0, LONG_MAX, 0, $);
}
~semaphore()
{
rl::CloseHandle(h_, $);
}
void wait()
{
rl::WaitForSingleObject(h_, rl::RL_INFINITE, $);
}
void post()
{
rl::ReleaseSemaphore(h_, 1, 0, $);
}
private:
rl::HANDLE h_;
semaphore(semaphore const&);
semaphore& operator = (semaphore const&);
};
class mutex
{
public:
mutex()
{
rl::InitializeCriticalSection(&cs_, $);
}
~mutex()
{
rl::DeleteCriticalSection(&cs_, $);
}
void lock()
{
rl::EnterCriticalSection(&cs_, $);
}
void unlock()
{
rl::LeaveCriticalSection(&cs_, $);
}
private:
rl::CRITICAL_SECTION cs_;
mutex(mutex const&);
mutex& operator = (mutex const&);
};
//void full_memory_fence()
//{
// _mm_mfence();
//}
//#define THREAD_LOCAL __declspec(thread)
#elif defined(POSIX) && defined(GCC)
#include <pthread.h>
#include <semaphore.h>
class semaphore
{
public:
semaphore()
{
sem_init(&sem_, 0, 0);
}
~semaphore()
{
sem_destroy(&sem_);
}
void wait()
{
sem_wait(&sem_);
}
void post()
{
sem_post(&sem_);
}
private:
sem_t sem_;
semaphore(semaphore const&);
semaphore& operator = (semaphore const&);
};
class mutex
{
public:
mutex()
{
pthread_mutex_init(&mutex_, 0);
}
~mutex()
{
pthread_mutex_destroy(&mutex_);
}
void lock()
{
pthread_mutex_lock(&mutex_);
}
void unlock()
{
pthread_mutex_unlock(&mutex_);
}
private:
pthread_mutex_t mutex_;
mutex(mutex const&);
mutex& operator = (mutex const&);
};
void full_memory_fence()
{
__sync_synchronize();
}
//#define THREAD_LOCAL __thread
#endif
class lock
{
public:
lock(mutex& m)
: m_(m)
{
m.lock();
}
~lock()
{
m_.unlock();
}
private:
mutex& m_;
lock(lock const&);
lock& operator = (lock const&);
};
/** simple single-threaded double-linked list
* nothing interesting
*/
class dlist
{
public:
struct node
{
rl::var<node*> prev_;
rl::var<node*> next_;
node()
{
prev_($) = 0;
next_($) = 0;
}
};
dlist()
{
reset();
}
void push(node* n)
{
size_t s = size_($).load(rl::memory_order_relaxed);
size_($).store(s + 1, rl::memory_order_relaxed);
n->next_($) = head_.next_($);
n->prev_($) = &head_;
head_.next_($)->prev_($) = n;
head_.next_($) = n;
}
node* pop()
{
if (size_($).load(rl::memory_order_relaxed) == 0)
return 0;
node* n = head_.next_($);
remove(n);
return n;
}
void remove(node* n)
{
size_t s = size_($).load(rl::memory_order_relaxed);
size_($).store(s - 1, rl::memory_order_relaxed);
n->prev_($)->next_($) = n->next_($);
n->next_($)->prev_($) = n->prev_($);
}
size_t size() const
{
return size_($).load(rl::memory_order_relaxed);
}
node* begin()
{
return head_.next_($);
}
void flush_to(dlist& target)
{
if (size_($).load(rl::memory_order_relaxed))
{
target.size_($).store(size_($).load(rl::memory_order_relaxed));
target.head_.next_($) = head_.next_($);
target.head_.next_($)->prev_($) = &target.head_;
target.tail_.prev_($) = tail_.prev_($);
target.tail_.prev_($)->next_($) = &target.tail_;
}
else
{
target.reset();
}
reset();
}
static bool not_last(node* n)
{
return n->next_($) != 0;
}
static node* get_next(node* n)
{
return n->next_($);
}
private:
rl::atomic<size_t> size_;
node head_;
node tail_;
void reset()
{
size_($) = 0;
head_.next_($) = &tail_;
head_.prev_($) = 0;
tail_.next_($) = 0;
tail_.prev_($) = &head_;
}
dlist(dlist const&);
dlist& operator = (dlist const&);
};
/** pre-thread descriptor for eventcount
*/
struct ec_thread
{
dlist::node node_;
semaphore sema_;
rl::var<unsigned> epoch_;
rl::atomic<bool> in_waitset_;
rl::var<bool> spurious_;
rl::var<void*> ctx_;
ec_thread()
{
epoch_($) = 0;
in_waitset_($) = false;
spurious_($) = false;
ctx_($) = 0;
}
~ec_thread()
{
if (spurious_($))
sema_.wait();
}
/*
static ec_thread* current()
{
static THREAD_LOCAL ec_thread* ec_thread_instance = 0;
ec_thread* instance = ec_thread_instance;
if (instance == 0)
{
instance = new ec_thread;
ec_thread_instance = instance;
}
return instance;
// instance must be destroyed in DllMain() callback
// or in pthread_key_create() callback
}
*/
private:
ec_thread(ec_thread const&);
ec_thread& operator = (ec_thread const&);
};
/** fine-grained eventcount implementation
*/
class eventcount
{
public:
eventcount()
{
epoch_($) = 0;
}
void prepare_wait(ec_thread* th = 0, void* ctx = 0)
{
RL_ASSERT(th);
// this is good place to pump previous spurious wakeup
if (th->spurious_($))
{
th->spurious_($) = false;
th->sema_.wait();
}
th->in_waitset_($).store(true, rl::memory_order_relaxed);
th->ctx_($) = ctx;
{
lock l (mtx_);
th->epoch_($) = epoch_($).load(rl::memory_order_relaxed);
waitset_.push(&th->node_);
}
rl::atomic_thread_fence($)(rl::memory_order_seq_cst);
}
void commit_wait(ec_thread* th = 0)
{
RL_ASSERT(th);
// this check is just an optimization
//if (th->epoch_($) == epoch_($).load(rl::memory_order_relaxed))
if (th->in_waitset_($).load(rl::memory_order_acquire))
th->sema_.wait();
else
cancel_wait(true, th); //!!! add 'th'
}
void cancel_wait(bool /*from_commit*/, ec_thread* th = 0)
{
RL_ASSERT(th);
// spurious wakeup will be pumped in the following prepare_wait()
th->spurious_($) = true;
// try to remove node from waitset
if (th->in_waitset_($).load(rl::memory_order_acquire))
{
lock l (mtx_);
if (th->in_waitset_($).load(rl::memory_order_relaxed))
{
// successfully removed from waitset,
// so there will be no spurious wakeup
th->in_waitset_($).store(false, rl::memory_order_relaxed);
th->spurious_($) = false;
waitset_.remove(&th->node_);
}
else
{
//if (from_commit)
//int volatile x = 0;
}
}
else
{
//RL_ASSERT(from_commit == false);
//if (from_commit)
// int volatile x = 0;
}
}
void notify_one()
{
rl::atomic_thread_fence($)(rl::memory_order_seq_cst);
notify_one_relaxed();
}
template<typename predicate_t>
void notify(predicate_t pred)
{
rl::atomic_thread_fence($)(rl::memory_order_seq_cst);
notify_relaxed(pred);
}
void notify_all()
{
rl::atomic_thread_fence($)(rl::memory_order_seq_cst);
notify_all_relaxed();
}
void notify_one_relaxed()
{
if (waitset_.size() == 0)
return;
dlist::node* n;
{
lock l (mtx_);
unsigned ep = epoch_($).load(rl::memory_order_relaxed);
epoch_($).store(ep + 1, rl::memory_order_relaxed);
n = waitset_.pop();
if (n)
to_ec_thread(n)->in_waitset_($).store(false, rl::memory_order_release);
}
if (n)
{
to_ec_thread(n)->sema_.post();
}
}
template<typename predicate_t>
void notify_relaxed(predicate_t pred)
{
if (waitset_.size() == 0)
return;
dlist temp;
{
lock l (mtx_);
unsigned ep = epoch_($).load(rl::memory_order_relaxed);
epoch_($).store(ep + 1, rl::memory_order_relaxed);
size_t size = waitset_.size();
size_t idx = 0;
dlist::node* n = waitset_.begin();
while (dlist::not_last(n))
{
dlist::node* next = dlist::get_next(n);
ec_thread* th = to_ec_thread(n);
if (pred(th->ctx_($), size, idx))
{
waitset_.remove(n);
temp.push(n);
th->in_waitset_($).store(false, rl::memory_order_release);
}
n = next;
idx += 1;
}
}
dlist::node* n = temp.begin();
while (dlist::not_last(n))
{
dlist::node* next = dlist::get_next(n);
to_ec_thread(n)->sema_.post();
n = next;
}
}
void notify_all_relaxed()
{
if (waitset_.size() == 0)
return;
dlist temp;
{
lock l (mtx_);
waitset_.flush_to(temp);
dlist::node* n = temp.begin();
while (dlist::not_last(n))
{
to_ec_thread(n)->in_waitset_($).store(false, rl::memory_order_release);
n = dlist::get_next(n);
}
unsigned ep = epoch_($).load(rl::memory_order_relaxed);
epoch_($).store(ep + 1, rl::memory_order_relaxed);
}
dlist::node* n = temp.begin();
while (dlist::not_last(n))
{
dlist::node* next = dlist::get_next(n);
to_ec_thread(n)->sema_.post();
n = next;
}
}
class wait_guard;
private:
mutex mtx_;
dlist waitset_;
rl::atomic<unsigned>epoch_;
ec_thread* to_ec_thread(dlist::node* n)
{
return (ec_thread*)((char*)n - offsetof(ec_thread, node_));
}
eventcount(eventcount const&);
eventcount& operator = (eventcount const&);
};
class eventcount::wait_guard
{
public:
wait_guard(eventcount& ec, ec_thread* th = 0, void* ctx = 0)
: ec_(ec)
, th_(th)
, wait_(false)
{
ec_.prepare_wait(th_, ctx);
}
void commit_wait()
{
assert(false == wait_);
wait_ = true;
ec_.commit_wait(th_);
}
~wait_guard()
{
if (false == wait_)
ec_.cancel_wait(false, th_);
}
private:
eventcount& ec_;
ec_thread* th_;
bool wait_;
wait_guard(wait_guard const&);
wait_guard& operator = (wait_guard const&);
};
struct scheduler
{
struct tbb_thread
{
ec_thread th;
};
eventcount ec_;
tbb_thread* threads_;
bool volatile is_permanently_open_;
void wait_while_pool_is_empty(tbb_thread* th)
{
if (is_permanently_open_)
return;
eventcount::wait_guard wait (ec_, &th->th);
if (pool_is_empty())
wait.commit_wait();
}
void notify_about_new_task_available()
{
ec_.notify_one_relaxed();
}
void notify_about_new_task_available_with_preference(tbb_thread* preference)
{
struct local
{
tbb_thread* preference_;
bool fired_;
bool operator () (void* ctx, size_t count, size_t idx)
{
tbb_thread* th = (tbb_thread*)ctx;
if (th == preference_)
{
fired_ = true;
return true;
}
else if (idx == count - 1 && fired_ == false)
{
return true;
}
else
{
return false;
}
}
}
pred = {preference};
ec_.notify_relaxed(pred);
}
void notify_about_list_of_tasks_available(size_t total_count, size_t preference_count, tbb_thread** preferences)
{
struct local
{
size_t remain_to_signal_;
size_t preference_count_;
tbb_thread** preferences_;
bool operator () (void* ctx, size_t count, size_t idx)
{
tbb_thread* th = (tbb_thread*)ctx;
size_t remain_in_waitset = count - idx;
if (remain_in_waitset <= remain_to_signal_)
{
return true;
}
else
{
for (size_t i = 0; i != preference_count_; ++i)
{
if (preferences_[i] == th)
{
remain_to_signal_ -= 1;
return true;
}
}
}
return false;
}
}
pred = {total_count, preference_count, preferences};
ec_.notify_relaxed(pred);
}
bool pool_is_empty()
{
return true;
}
};
struct queue
{
rl::atomic<int> producer_idx_;
rl::atomic<int> consumer_idx_;
rl::atomic<void*>* buffer_;
eventcount ec_;
queue()
{
producer_idx_($) = 0;
consumer_idx_($) = 0;
buffer_ = RL_NEW_ARR(rl::atomic<void*>, 10);
for (size_t i = 0; i != 10; ++i)
buffer_[i]($) = 0;
}
~queue()
{
RL_DELETE_ARR(buffer_);
}
void enqueue(void* data)
{
int idx = producer_idx_($).fetch_add(1) + 1; // atomic
buffer_[idx]($).store(data, rl::memory_order_relaxed);
struct local
{
int idx_;
bool operator () (void* ctx, size_t /*count*/, size_t /*idx*/)
{
return idx_ == (*(rl::var<int>*)ctx)($);
}
}
pred = {idx};
ec_.notify(pred); // not relaxed!!!
}
void* dequeue(ec_thread* th)
{
int idx = consumer_idx_($).fetch_add(1) + 1; // atomic
void* data = buffer_[idx]($).load(rl::memory_order_relaxed);
if (data)
return data;
for (;;)
{
rl::var<int> idxv (idx);
eventcount::wait_guard wait (ec_, th, &idxv);
data = buffer_[idx]($).load(rl::memory_order_relaxed);
if (data)
{
return data;
}
wait.commit_wait();
idxv($) = 0;
data = buffer_[idx]($).load(rl::memory_order_relaxed);
if (data)
{
return data;
}
rl::yield($, 1);
//RL_ASSERT(false);
}
}
};
class condition_variable
{
eventcount ec_;
public:
void wait(mutex& mtx, ec_thread* th)
{
eventcount::wait_guard wait (ec_, th);
mtx.unlock();
wait.commit_wait();
mtx.lock();
}
void signal()
{
ec_.notify_one();
}
void broadcast()
{
ec_.notify_all();
}
};
struct queue_test : rl::test_suite<queue_test, 4>
{
ec_thread threads_ [6];
queue q_;
void thread(unsigned index)
{
if (index < 2)
{
q_.enqueue((void*)(index*2+1));
q_.enqueue((void*)(index*2+2));
}
else
{
int data1 = (int)q_.dequeue(&threads_[index]);
RL_ASSERT(data1 >= 1 && data1 <= 6);
int data2 = (int)q_.dequeue(&threads_[index]);
RL_ASSERT(data2 >= 1 && data2 <= 6);
}
}
};
struct condvar_test : rl::test_suite<condvar_test, 3>
{
rl::var<int> stage;
condition_variable cv;
mutex mtx;
ec_thread th [3];
void before()
{
stage($) = 0;
}
void thread(unsigned index)
{
if (0 == index)
{
mtx.lock();
stage($) += 1;
cv.broadcast();
while (stage($) != 2)
cv.wait(mtx, &th[index]);
mtx.unlock();
}
else if (1 == index)
{
mtx.lock();
while (stage($) != 1)
cv.wait(mtx, &th[index]);
stage($) += 1;
cv.broadcast();
mtx.unlock();
}
else if (2 == index)
{
mtx.lock();
while (stage($) != 2)
cv.wait(mtx, &th[index]);
mtx.unlock();
}
}
};
int main()
{
rl::test_params p;
p.iteration_count = 100000000;
//p.initial_state = "30000000";
//p.search_type = rl::fair_context_bound_scheduler_type;
rl::simulate<queue_test>(p);
//rl::simulate<condvar_test>(p);
}

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="eventcount"
ProjectGUID="{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E5}"
RootNamespace="eventcount"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
<File
RelativePath="..\eventcount.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="eventcount"
ProjectGUID="{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E5}"
RootNamespace="eventcount"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
<File
RelativePath="..\eventcount.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// ws_deque.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@@ -0,0 +1,6 @@
#pragma once
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="ws_deque"
ProjectGUID="{0B597F19-DEBB-4832-B520-9A93A286D595}"
RootNamespace="ws_deque"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
<File
RelativePath="..\ws_deque.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="ws_deque"
ProjectGUID="{0B597F19-DEBB-4832-B520-9A93A286D595}"
RootNamespace="ws_deque"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
<File
RelativePath="..\ws_deque.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,14 @@
#pragma once
#ifdef NDEBUG
# define _SECURE_SCL 0
#endif
#define RL_MSVC_OUTPUT
//#define RL_DEBUGBREAK_ON_FAILURE
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,287 @@
#include "stdafx.h"
#include "../../relacy/relacy_std.hpp"
using namespace std;
using rl::var;
template<typename T>
class ws_deque
{
public:
ws_deque()
{
VAR(m_mask) = initial_size - 1;
m_headIndex.store(0, memory_order_relaxed);
m_tailIndex.store(0, memory_order_relaxed);
VAR(m_array) = new atomic<T> [initial_size];
VAR(m_arraySize) = initial_size;
}
~ws_deque()
{
delete [] VAR(m_array);
}
bool IsEmpty() const
{
return m_headIndex.load(memory_order_acquire)
>= m_tailIndex.load(memory_order_acquire);
}
size_t Count() const
{
return m_tailIndex.load(memory_order_acquire)
- m_headIndex.load(memory_order_acquire);
}
void push(T item)
{
size_t tail = m_tailIndex.load(memory_order_acquire);
if (tail < m_headIndex.load(memory_order_acquire) + VAR(m_mask))
{
VAR(m_array)[tail & VAR(m_mask)].store(item, memory_order_relaxed);
m_tailIndex.store(tail + 1, memory_order_release);
}
else
{
m_foreignLock.lock($);
size_t head = m_headIndex.load(memory_order_acquire);
size_t count = Count();
if (count >= VAR(m_mask))
{
size_t arraySize = m_arraySize($);
size_t mask = VAR(m_mask);
atomic<T>* newArray = new atomic<T> [arraySize * 2];
atomic<T>* arr = m_array($);
//!!! for (size_t i = 0; i != arraySize; ++i)
for (size_t i = 0; i != count; ++i)
newArray[i].store(arr[(i + head) & mask].load(memory_order_seq_cst), memory_order_relaxed);
delete [] VAR(m_array);
VAR(m_array) = newArray;
VAR(m_arraySize) = arraySize * 2;
m_headIndex.store(0, memory_order_release);
m_tailIndex.store(count, memory_order_release);
tail = count;
VAR(m_mask) = (mask * 2) | 1;
}
VAR(m_array)[tail & VAR(m_mask)].store(item, memory_order_relaxed);
m_tailIndex.store(tail + 1, memory_order_release);
m_foreignLock.unlock($);
}
}
bool pop(T& item)
{
size_t tail = m_tailIndex.load(memory_order_acquire);
if (tail == 0)
return false;
tail -= 1;
m_tailIndex.store(tail, memory_order_release);
atomic_thread_fence(memory_order_seq_cst);
if (m_headIndex.load(memory_order_acquire) <= tail)
{
item = VAR(m_array)[tail & VAR(m_mask)].load(memory_order_relaxed);
return true;
}
else
{
m_foreignLock.lock($);
if (m_headIndex.load(memory_order_acquire) <= tail)
{
item = VAR(m_array)[tail & VAR(m_mask)].load(memory_order_relaxed);
m_foreignLock.unlock($);
return true;
}
else
{
m_tailIndex.store(tail + 1, memory_order_release);
m_foreignLock.unlock($);
return false;
}
}
}
bool steal(T& item)
{
if (false == m_foreignLock.try_lock($))
return false;
size_t head = m_headIndex.load(memory_order_acquire);
m_headIndex.store(head + 1, memory_order_release);
atomic_thread_fence(memory_order_seq_cst);
if (head < m_tailIndex.load(memory_order_acquire))
{
item = VAR(m_array)[head & VAR(m_mask)].load(memory_order_relaxed);
m_foreignLock.unlock($);
return true;
}
else
{
m_headIndex.store(head, memory_order_release);
m_foreignLock.unlock($);
return false;
}
}
private:
static size_t const initial_size = 2;
var<atomic<T>*> m_array;
var<size_t> m_mask;
var<size_t> m_arraySize;
atomic<size_t> m_headIndex;
atomic<size_t> m_tailIndex;
mutex m_foreignLock;
};
struct ws_deque_test0 : rl::test_suite<ws_deque_test0, 4>
{
ws_deque<int> q;
void before()
{
}
void after()
{
}
void thread(unsigned index)
{
if (0 == index)
{
for (size_t i = 0; i != 4; ++i)
{
q.push(10);
}
for (size_t i = 0; i != 5; ++i)
{
int p = 0;
bool res = q.pop(p);
RL_ASSERT(10 == p || false == res);
}
for (size_t i = 0; i != 4; ++i)
{
q.push(10);
int p = 0;
bool res = q.pop(p);
RL_ASSERT(10 == p || false == res);
}
for (size_t i = 0; i != 4; ++i)
{
q.push(10);
q.push(10);
int p = 0;
bool res = q.pop(p);
RL_ASSERT(10 == p || false == res);
p = 0;
res = q.pop(p);
RL_ASSERT(10 == p || false == res);
}
for (size_t i = 0; i != 4; ++i)
{
q.push(10);
q.push(10);
q.push(10);
int p = 0;
bool res = q.pop(p);
RL_ASSERT(10 == p || false == res);
}
for (size_t i = 0; i != 14; ++i)
{
q.push(10);
int p = 0;
bool res = q.pop(p);
RL_ASSERT(10 == p || false == res);
}
}
else
{
for (size_t i = 0; i != 4; ++i)
{
int p = 0;
bool res = q.steal(p);
RL_ASSERT(10 == p || false == res);
}
}
}
};
struct ws_deque_test : rl::test_suite<ws_deque_test, 2>
{
ws_deque<int> q;
bool state [2];
void before()
{
state[0] = true;
state[1] = true;
}
void after()
{
RL_ASSERT(state[0] == false);
RL_ASSERT(state[1] == false);
}
void thread(unsigned index)
{
if (0 == index)
{
q.push(1);
q.push(2);
int item = 0;
bool res = q.pop(item);
RL_ASSERT(res && item == 2);
RL_ASSERT(state[1]);
state[1] = false;
item = 0;
res = q.pop(item);
if (res)
{
RL_ASSERT(state[0]);
state[0] = false;
}
item = 0;
res = q.pop(item);
RL_ASSERT(res == false);
}
else
{
int item = 0;
bool res = q.steal(item);
if (res)
{
RL_ASSERT(item == 1);
RL_ASSERT(state[0]);
state[0] = false;
}
}
}
};
int main()
{
rl::simulate<ws_deque_test0>();
rl::simulate<ws_deque_test>();
}

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="ws_deque"
ProjectGUID="{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}"
RootNamespace="ws_deque"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\stdafx.h"
>
</File>
<File
RelativePath="..\ws_deque.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// ws_deque.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@@ -0,0 +1,6 @@
#pragma once
#include "../../relacy/pch.hpp"
#include "../../relacy/relacy_std.hpp"

View File

@@ -0,0 +1,690 @@
#include "stdafx.h"
struct pdr
{
__declspec(thread) static pdr* instance;
static size_t const defer_limit = 1024;
typedef void(*dtor_f)(void*);
struct entry_t
{
dtor_f dtor;
void* ctx;
};
entry_t defer_list [defer_limit];
size_t pos;
size_t pos0;
size_t thread_count;
size_t th [4];
void init(size_t count)
{
//assert(0 == instance);
instance = this;
thread_count = count;
pos = 0;
pos0 = 0;
for (size_t i = 0; i != thread_count; ++i)
{
th[i] = defer_limit;
}
}
void fini()
{
for (size_t i = 0; i != thread_count; ++i)
{
assert(th[i] == defer_limit);
}
for (size_t i = pos0; i != pos; ++i)
{
assert(defer_list[i].dtor);
defer_list[i].dtor(defer_list[i].ctx);
}
assert(this == instance);
instance = 0;
}
void lock()
{
std::atomic_thread_fence($)(std::memory_order_seq_cst);
assert(th[rl::ctx().threadx_->index_] == defer_limit);
th[rl::ctx().threadx_->index_] = pos;
}
void unlock()
{
assert(th[rl::ctx().threadx_->index_] != defer_limit);
th[rl::ctx().threadx_->index_] = defer_limit;
pump();
}
template<typename T>
static void dtor_impl(void* p)
{
RL_DELETE(static_cast<T*>(p));
}
template<typename T>
void defer(T* p)
{
std::atomic_thread_fence($)(std::memory_order_seq_cst);
assert(pos < defer_limit);
entry_t& e = defer_list[pos++];
e.dtor = &pdr::dtor_impl<T>;
e.ctx = p;
pump();
}
void pump()
{
if (pos0 == pos)
return;
size_t min_pos = pos;
for (size_t i = 0; i != thread_count; ++i)
{
if (th[i] < min_pos)
min_pos = th[i];
}
for (size_t i = pos0; i != min_pos; ++i)
{
assert(defer_list[i].dtor);
defer_list[i].dtor(defer_list[i].ctx);
}
pos0 = min_pos;
}
};
pdr* pdr::instance = 0;
void pdr_lock()
{
assert(pdr::instance);
pdr::instance->lock();
}
void pdr_unlock()
{
assert(pdr::instance);
pdr::instance->unlock();
}
template<typename T>
void pdr_defer(T* p)
{
assert(pdr::instance);
pdr::instance->defer(p);
}
class ws_deque
{
public:
ws_deque()
{
bottom_.block_($) = 0;
bottom_.real_block_id_ = 0;
bottom_.real_index_ = 0;
bottom_.block_id_ = 0;
bottom_.index_ = 0;
bottom_.block_seq_ = 0;
bottom_.check_order_ = 1;
top::info t = {};
top_.block_($) = 0;
top_.info_($) = t;
alloc_block();
bottom_.block_id_ = bottom_.block_($)->header_.id_;
top_.block_($) = bottom_.block_($);
t.top_block_id_ = static_cast<unsigned short>(top_.block_($).load()->header_.id_);
t.bottom_block_id_ = static_cast<unsigned short>(top_.block_($).load()->header_.id_);
top_.info_($) = t;
}
~ws_deque()
{
for (block* p = top_.block_($), *next; p; p = next)
{
next = p->header_.next_($).load(std::memory_order_relaxed);
RL_DELETE(p);
}
}
void push(void* const& i)
{
pdr_lock();
push_unbalanced(i);
rebalance();
pdr_unlock();
}
void push_unbalanced(void* i)
{
RL_ASSERT(bottom_.block_($)->header_.id_);
bottom_.block_($)->data_[bottom_.real_index_]($).store(i, std::memory_order_release);
if (block::item_count - 1 != bottom_.real_index_)
{
bottom_.real_index_ += 1;
}
else
{
alloc_block();
}
}
void rebalance()
{
if (0 == --bottom_.check_order_)
{
check_bottom();
}
}
void* pop()
{
pdr_lock();
rebalance();
void* p = pop_unbalanced();
pdr_unlock();
return p;
}
void* pop_unbalanced()
{
//!!! optimize
//! fast-path for empty deque
//! make comparasion faster
if ((bottom_.block_id_ != bottom_.real_block_id_
|| bottom_.index_ != bottom_.real_index_)
&& bottom_.real_index_)
{
bottom_.real_index_ -= 1;
void* i = bottom_.block_($)->data_[bottom_.real_index_]($).load(std::memory_order_consume);
return i;
}
else
{
return pop_unbalanced_slow();
}
}
void* pop_unbalanced_slow()
{
if (0 == bottom_.real_index_)
{
if (bottom_.real_block_id_ > bottom_.block_id_)
{
return pop_slow();
}
else
{
return 0;
}
}
else
{
void* i;
pop_check_result const rv = pop_check(i);
if (pop_check_cont != rv)
return pop_check_succ == rv ? i : 0;
return pop_unbalanced(); // recursion, must succeed
}
}
void* steal()
{
pdr_lock();
retry:
for (;;)
{
block* old_b = top_.block_($).load(std::memory_order_acquire);
block* b = old_b;
top::info old = top_.info_($).load(std::memory_order_consume);
if (old.top_index_ == old.bottom_index_
&& old.top_block_id_ == old.bottom_block_id_)
{
pdr_unlock();
return 0;
}
if (b->header_.id_ != old.top_block_id_)
{
do
{
b = b->header_.next_($).load(std::memory_order_relaxed);
//RL_ASSERT(b);
//!!! temp stub - is it right?
// it seems that we always return 0 after we hit this goto
if (0 == b)
goto retry;
}
//!!! AV
// b == 0
while (b->header_.id_ != old.top_block_id_);
if (top_.block_($).compare_swap(old_b, b, std::memory_order_seq_cst))
{
block* cur_b = old_b;
do
{
pdr_defer(cur_b);
cur_b = cur_b->header_.next_($).load(std::memory_order_relaxed);
}
while (cur_b != b);
}
}
block* next_block = 0;
top::info mod = old;
void* i = b->data_[mod.top_index_]($).load(std::memory_order_consume);
if (block::item_count - 1 == mod.top_index_)
{
next_block = b->header_.next_($).load(std::memory_order_relaxed);
mod.top_block_id_ += 1;
mod.top_index_ = 0;
}
else
{
mod.top_index_ += 1;
}
if (top_.info_($).compare_swap(old, mod, std::memory_order_seq_cst))
{
if (next_block)
{
if (top_.block_($).compare_swap(b, next_block))
{
pdr_defer(b);
}
}
pdr_unlock();
return i;
}
}
}
unsigned size() const
{
top::info const top = top_.info_($).load(std::memory_order_relaxed);
//unsigned volatile const top_block_id = top_.info_.part_.top_block_id_;
//unsigned volatile const top_index = top_.info_.part_.top_index_;
if (bottom_.real_block_id_ == top.top_block_id_)
{
unsigned const size = bottom_.real_index_ - top.top_index_;
return size;
}
else
{
unsigned size = bottom_.real_index_;
size += block::item_count - top.top_index_;
size += (bottom_.real_block_id_ - top.top_block_id_ - 1) * block::item_count;
return size;
}
}
private:
struct block
{
struct header
{
std::atomic<block*> next_;
std::atomic<block*> prev_;
ws_deque* deque_;
unsigned id_;
//!!!
~header()
{
id_ = 0;
}
};
static unsigned const item_count = 2;
header header_;
std::atomic<void*> data_ [item_count];
};
struct bottom
{
rl::var<block*> block_;
unsigned check_order_;
unsigned real_block_id_;
unsigned real_index_;
unsigned block_id_;
unsigned index_;
unsigned block_seq_;
};
struct top
{
struct info
{
unsigned short top_index_;
unsigned short top_block_id_;
unsigned short bottom_index_;
unsigned short bottom_block_id_;
bool operator == (info const& x) const
{
return top_index_ == x.top_index_
&& top_block_id_ == x.top_block_id_
&& bottom_index_ == x.bottom_index_
&& bottom_block_id_ == x.bottom_block_id_;
}
friend std::ostream& operator << (std::ostream& ss, info const& x)
{
return ss << "{" << x.top_index_
<< "," << x.top_block_id_
<< "," << x.bottom_index_
<< "," << x.bottom_block_id_ << "}";
}
};
std::atomic<block*> block_;
std::atomic<info> info_;
};
bottom bottom_;
char pad1 [64];
top top_;
char pad2 [64];
void alloc_block()
{
//!!! check whether we already have next block in
// bottom_.block_->header_.next_
block* b = bottom_.block_($) ? bottom_.block_($)->header_.next_($).load(std::memory_order_relaxed) : 0;
if (0 == b)
{
b = RL_NEW block;
b->header_.deque_ = this;
bottom_.block_seq_ += 1;
//!!!
if (bottom_.block_seq_ > 0xffff) __asm int 3;
bottom_.block_seq_ &= 0xffff;
b->header_.id_ = bottom_.block_seq_;
b->header_.prev_($).store(bottom_.block_($), std::memory_order_relaxed);
if (bottom_.block_($))
bottom_.block_($)->header_.next_($).store(b, std::memory_order_relaxed);
b->header_.next_($).store(0, std::memory_order_relaxed);
}
else
{
b = b;
bottom_.block_seq_ += 1;
//__asm int 3;
}
bottom_.block_($) = b;
bottom_.real_block_id_ = b->header_.id_;
bottom_.real_index_ = 0;
}
enum pop_check_result {pop_check_fail, pop_check_succ, pop_check_cont};
pop_check_result pop_check(void*& i)
{
check_bottom();
if (bottom_.block_id_ == bottom_.real_block_id_
&& bottom_.index_ == bottom_.real_index_)
{
top::info const top = top_.info_($).load(std::memory_order_seq_cst);
if ((bottom_.block_id_ == top.top_block_id_
&& bottom_.index_ == (unsigned)top.top_index_ + 1)
|| (bottom_.block_id_ == (unsigned)top.top_block_id_ + 1
&& block::item_count - 1 == top.top_index_
&& 0 == bottom_.index_ ))
{
__asm int 3;
i = steal();
if (i)
return pop_check_succ;
}
return pop_check_fail;
}
return pop_check_cont;
}
void* pop_slow()
{
bottom_.block_seq_ -= 1;
bottom_.block_seq_ &= 0xffff;
bottom_.block_($) = bottom_.block_($)->header_.prev_($).load(std::memory_order_relaxed);
//!!! AV: when core count set to 16
// bottom_.block_ = 0
// bottom.real_block_id = 1
// bottom.block_id = 8
//!!! AV in xscale too (thread count is 4)
// the same variables values
bottom_.real_block_id_ = bottom_.block_($)->header_.id_;
bottom_.real_index_ = block::item_count - 1;
top::info i = top_.info_($).load(std::memory_order_relaxed);
RL_ASSERT(bottom_.block_($)->header_.id_ == bottom_.block_seq_);
RL_ASSERT((bottom_.real_block_id_ == i.bottom_block_id_ && bottom_.real_index_ >= i.bottom_index_)
|| (bottom_.real_block_id_ > i.bottom_block_id_));
void* v = bottom_.block_($)->data_[block::item_count - 1]($).load(std::memory_order_consume);
return v;
}
void check_bottom()
{
//!!! must leave at least 1 element unreserved
// because owner have to steal it
for (;;)
{
top::info old = top_.info_($).load(std::memory_order_relaxed);
unsigned const top_block_id = old.top_block_id_;
unsigned const top_index = old.top_index_;
if (bottom_.real_block_id_ == top_block_id
&& bottom_.real_index_ == top_index)
{
bottom_.check_order_ = 2;
return;
}
unsigned const s = size();
unsigned const r = reserved();
if (!(0 == r || (r > 1 && 4*r > 3*s)))
{
//bottom_.check_order_ = 2;
//!!! bottom_.check_order_ = s / 8 + 2;
bottom_.check_order_ = s / 2 + 2;
return;
}
unsigned r2 = s*3/4 + 1;
if (r2 >= s)
r2 = s - 1;
unsigned bottom_block_id;
unsigned bottom_index;
if (r2 + top_index < block::item_count)
{
bottom_block_id = top_block_id;
bottom_index = top_index + r2;
}
else
{
unsigned const r3 = r2 + top_index;
bottom_block_id = top_block_id + r3 / block::item_count;
bottom_index = r3 % block::item_count;
}
top::info i;
i.top_block_id_ = static_cast<unsigned short>(top_block_id);
i.top_index_ = static_cast<unsigned short>(top_index);
i.bottom_block_id_ = static_cast<unsigned short>(bottom_block_id);
i.bottom_index_ = static_cast<unsigned short>(bottom_index);
/*
bottom volatile btm = bottom_;
if (i.part_.top_block_id_ > i.part_.bottom_block_id_)
__asm int 3;
if (i.part_.top_block_id_ == i.part_.bottom_block_id_
&& i.part_.top_index_ >= i.part_.bottom_index_)
__asm int 3;
if (i.part_.bottom_block_id_ > btm.real_block_id_)
__asm int 3;
if (i.part_.bottom_block_id_ == btm.real_block_id_
&& i.part_.bottom_index_ > btm.real_index_)
__asm int 3;
*/
if (top_.info_($).compare_swap(old, i, std::memory_order_seq_cst))
{
bottom_.block_id_ = bottom_block_id;
bottom_.index_ = bottom_index;
//!!! bottom_.check_order_ = s / 8 + 2;
bottom_.check_order_ = s / 2 + 2;
return;
}
}
}
unsigned reserved() const
{
if (bottom_.real_block_id_ == bottom_.block_id_)
{
unsigned const reserved = bottom_.real_index_ - bottom_.index_;
return reserved;
}
else
{
unsigned reserved = bottom_.real_index_;
reserved += block::item_count - bottom_.index_;
reserved += (bottom_.real_block_id_ - bottom_.block_id_ - 1) * block::item_count;
return reserved;
}
}
};
int x = 0;
struct ws_deque_test : rl::test_suite<ws_deque_test, 4>
{
ws_deque q;
pdr p;
void before()
{
p.init(4);
}
void after()
{
p.fini();
}
void thread(unsigned index)
{
if (0 == index)
{
for (size_t i = 0; i != 4; ++i)
{
q.push((void*)10);
}
for (size_t i = 0; i != 5; ++i)
{
void* p = q.pop();
RL_ASSERT((void*)10 == p || 0 == p);
}
for (size_t i = 0; i != 4; ++i)
{
q.push((void*)10);
void* p = q.pop();
RL_ASSERT((void*)10 == p || 0 == p);
}
for (size_t i = 0; i != 4; ++i)
{
q.push((void*)10);
q.push((void*)10);
void* p = q.pop();
RL_ASSERT((void*)10 == p || 0 == p);
p = q.pop();
RL_ASSERT((void*)10 == p || 0 == p);
}
for (size_t i = 0; i != 4; ++i)
{
q.push((void*)10);
q.push((void*)10);
q.push((void*)10);
void* p = q.pop();
RL_ASSERT((void*)10 == p || 0 == p);
}
for (size_t i = 0; i != 14; ++i)
{
q.push((void*)10);
void* p = q.pop();
RL_ASSERT((void*)10 == p || 0 == p);
}
}
else
{
for (size_t i = 0; i != 4; ++i)
{
void* p = q.steal();
RL_ASSERT((void*)10 == p || 0 == p);
}
}
}
};
int main()
{
rl::test_params p;
p.iteration_count = 1000000;
rl::simulate<ws_deque_test>(p);
}

View File

@@ -0,0 +1,723 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_ATOMIC_HPP
#define RL_ATOMIC_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "context.hpp"
#include "memory_order.hpp"
#include "signature.hpp"
#include "atomic_events.hpp"
#include "waitset.hpp"
#include "rmw.hpp"
namespace rl
{
template<typename T>
class atomic;
template<bool> struct bool_t {};
template<typename T>
class atomic_proxy_const
{
public:
atomic_proxy_const(atomic<T> const /*volatile*/& var, debug_info_param info)
: var_(const_cast<atomic<T>&>(var))
, info_(info)
{
}
T load(memory_order mo = mo_seq_cst) const
{
return var_.load(mo, info_);
}
operator T () const
{
return load();
}
protected:
atomic<T>& var_;
debug_info info_;
atomic_proxy_const& operator = (atomic_proxy_const const&);
};
template<typename T>
class atomic_proxy : public atomic_proxy_const<T>
{
public:
typedef typename atomic_add_type<T>::type add_type;
atomic_proxy(atomic<T> /*volatile*/& var, debug_info_param info)
: atomic_proxy_const<T>(var, info)
{
}
void store(T value, memory_order mo = mo_seq_cst)
{
this->var_.store(value, mo, this->info_);
}
bool compare_exchange_weak(T& cmp, T xchg, memory_order mo = mo_seq_cst)
{
return this->var_.compare_exchange(bool_t<true>(), cmp, xchg, mo, this->info_);
}
bool compare_exchange_weak(T& cmp, T xchg, memory_order mo, memory_order failure_mo)
{
return this->var_.compare_exchange(bool_t<true>(), cmp, xchg, mo, failure_mo, this->info_);
}
bool compare_exchange_strong(T& cmp, T xchg, memory_order mo = mo_seq_cst)
{
return this->var_.compare_exchange(bool_t<false>(), cmp, xchg, mo, this->info_);
}
bool compare_exchange_strong(T& cmp, T xchg, memory_order mo, memory_order failure_mo)
{
return this->var_.compare_exchange(bool_t<false>(), cmp, xchg, mo, failure_mo, this->info_);
}
T exchange(T xchg, memory_order mo = mo_seq_cst)
{
return this->var_.rmw(rmw_type_t<rmw_type_swap>(), xchg, mo, this->info_);
}
T fetch_add(add_type value, memory_order mo = mo_seq_cst)
{
return this->var_.rmw(rmw_type_t<rmw_type_add>(), value, mo, this->info_);
}
T fetch_sub(add_type value, memory_order mo = mo_seq_cst)
{
return this->var_.rmw(rmw_type_t<rmw_type_sub>(), value, mo, this->info_);
}
T fetch_and(T value, memory_order mo = mo_seq_cst)
{
return this->var_.rmw(rmw_type_t<rmw_type_and>(), value, mo, this->info_);
}
T fetch_or(T value, memory_order mo = mo_seq_cst)
{
return this->var_.rmw(rmw_type_t<rmw_type_or>(), value, mo, this->info_);
}
T fetch_xor(T value, memory_order mo = mo_seq_cst)
{
return this->var_.rmw(rmw_type_t<rmw_type_xor>(), value, mo, this->info_);
}
T operator = (T value)
{
store(value);
return value;
}
T operator ++ (int)
{
return fetch_add(1);
}
T operator -- (int)
{
return fetch_sub(1);
}
T operator ++ ()
{
return fetch_add(1) + 1;
}
T operator -- ()
{
return fetch_sub(1) - 1;
}
T operator += (add_type value)
{
return fetch_add(value) + value;
}
T operator -= (add_type value)
{
return fetch_sub(value) + value;
}
T operator &= (T value)
{
return fetch_and(value) & value;
}
T operator |= (T value)
{
return fetch_or(value) | value;
}
T operator ^= (T value)
{
return fetch_xor(value) ^ value;
}
};
template<typename T, bool strong_init>
class generic_atomic
{
public:
generic_atomic()
{
context& c = ctx();
RL_VERIFY(false == c.invariant_executing);
impl_ = c.atomic_ctor(this);
initialized_ = false;
value_ = T();
already_failed_ = false;
if (val(strong_init))
{
unsigned const index = c.threadx_->atomic_init(impl_);
last_index_ = index;
initialized_ = true;
history_[index] = T();
value_ = T();
}
}
~generic_atomic()
{
context& c = ctx();
RL_VERIFY(false == c.invariant_executing);
sign_.check($);
c.atomic_dtor(impl_);
}
T debug_value() const
{
sign_.check($);
return value_;
}
RL_INLINE
T load(memory_order mo, debug_info_param info) const
{
RL_VERIFY(mo_release != mo);
RL_VERIFY(mo_acq_rel != mo);
switch (mo)
{
case mo_relaxed: return load_impl<mo_relaxed, &thread_info_base::atomic_load_relaxed>(info);
case mo_consume: return load_impl<mo_consume, &thread_info_base::atomic_load_acquire>(info);
case mo_acquire: return load_impl<mo_acquire, &thread_info_base::atomic_load_acquire>(info);
case mo_seq_cst: return load_impl<mo_seq_cst, &thread_info_base::atomic_load_seq_cst>(info);
default: break;
}
RL_VERIFY(false);
return T();
}
RL_INLINE
void store(T v, memory_order mo, debug_info_param info)
{
RL_VERIFY(mo_acquire != mo);
RL_VERIFY(mo_acq_rel != mo);
switch (mo)
{
case mo_relaxed: return store_impl<mo_relaxed, &thread_info_base::atomic_store_relaxed>(v, info);
case mo_release: return store_impl<mo_release, &thread_info_base::atomic_store_release>(v, info);
case mo_seq_cst: return store_impl< mo_seq_cst, &thread_info_base::atomic_store_seq_cst>(v, info);
default: break;
}
RL_VERIFY(false);
}
RL_INLINE
bool compare_exchange_weak(T& cmp, T xchg, memory_order mo, debug_info_param info)
{
return compare_exchange(bool_t<true>(), cmp, xchg, mo, info);
}
RL_INLINE
bool compare_exchange_strong(T& cmp, T xchg, memory_order mo, debug_info_param info)
{
return compare_exchange(bool_t<false>(), cmp, xchg, mo, info);
}
RL_INLINE
bool compare_exchange_weak(T& cmp, T xchg, memory_order mo, debug_info_param info, memory_order failure_mo, debug_info_param)
{
return compare_exchange(bool_t<true>(), cmp, xchg, mo, failure_mo, info);
}
RL_INLINE
bool compare_exchange_strong(T& cmp, T xchg, memory_order mo, debug_info_param info, memory_order failure_mo, debug_info_param)
{
return compare_exchange(bool_t<false>(), cmp, xchg, mo, failure_mo, info);
}
template<bool spurious_failures>
RL_INLINE
bool compare_exchange(bool_t<spurious_failures>, T& cmp, T xchg, memory_order mo, debug_info_param info)
{
switch (mo)
{
case mo_relaxed: return compare_swap_impl<spurious_failures, mo_relaxed, &thread_info_base::atomic_rmw_relaxed, mo_relaxed, &thread_info_base::atomic_load_relaxed_rmw>(cmp, xchg, info);
case mo_consume: return compare_swap_impl<spurious_failures, mo_consume, &thread_info_base::atomic_rmw_acquire, mo_consume, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
case mo_acquire: return compare_swap_impl<spurious_failures, mo_acquire, &thread_info_base::atomic_rmw_acquire, mo_acquire, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
case mo_release: return compare_swap_impl<spurious_failures, mo_release, &thread_info_base::atomic_rmw_release, mo_relaxed, &thread_info_base::atomic_load_relaxed_rmw>(cmp, xchg, info);
case mo_acq_rel: return compare_swap_impl<spurious_failures, mo_acq_rel, &thread_info_base::atomic_rmw_acq_rel, mo_acquire, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
case mo_seq_cst: return compare_swap_impl<spurious_failures, mo_seq_cst, &thread_info_base::atomic_rmw_seq_cst, mo_seq_cst, &thread_info_base::atomic_load_seq_cst_rmw>(cmp, xchg, info);
}
RL_VERIFY(false);
return false;
}
template<bool spurious_failures>
RL_INLINE
bool compare_exchange(bool_t<spurious_failures>, T& cmp, T xchg, memory_order mo, memory_order failure_mo, debug_info_param info)
{
switch (mo)
{
case mo_relaxed:
{
RL_VERIFY(mo_relaxed == failure_mo);
return compare_swap_impl<spurious_failures, mo_relaxed, &thread_info_base::atomic_rmw_relaxed, mo_relaxed, &thread_info_base::atomic_load_relaxed_rmw>(cmp, xchg, info);
}
case mo_consume:
{
RL_VERIFY(mo_relaxed == failure_mo || mo_consume == failure_mo);
switch (failure_mo)
{
case mo_relaxed: return compare_swap_impl<spurious_failures, mo_consume, &thread_info_base::atomic_rmw_acquire, mo_relaxed, &thread_info_base::atomic_load_relaxed_rmw>(cmp, xchg, info);
case mo_consume: return compare_swap_impl<spurious_failures, mo_consume, &thread_info_base::atomic_rmw_acquire, mo_consume, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
default: RL_VERIFY(false); return false;
}
}
case mo_acquire:
{
RL_VERIFY(mo_relaxed == failure_mo || mo_consume == failure_mo || mo_acquire == failure_mo);
switch (failure_mo)
{
case mo_relaxed: return compare_swap_impl<spurious_failures, mo_acquire, &thread_info_base::atomic_rmw_acquire, mo_relaxed, &thread_info_base::atomic_load_relaxed_rmw>(cmp, xchg, info);
case mo_consume: return compare_swap_impl<spurious_failures, mo_acquire, &thread_info_base::atomic_rmw_acquire, mo_consume, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
case mo_acquire: return compare_swap_impl<spurious_failures, mo_acquire, &thread_info_base::atomic_rmw_acquire, mo_acquire, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
default: RL_VERIFY(false); return false;
}
}
case mo_release:
{
RL_VERIFY(mo_relaxed == failure_mo);
return compare_swap_impl<spurious_failures, mo_release, &thread_info_base::atomic_rmw_release, mo_relaxed, &thread_info_base::atomic_load_relaxed_rmw>(cmp, xchg, info);
}
case mo_acq_rel:
{
RL_VERIFY(mo_relaxed == failure_mo || mo_consume == failure_mo || mo_acquire == failure_mo);
switch (failure_mo)
{
case mo_relaxed: return compare_swap_impl<spurious_failures, mo_acq_rel, &thread_info_base::atomic_rmw_acq_rel, mo_relaxed, &thread_info_base::atomic_load_relaxed_rmw>(cmp, xchg, info);
case mo_consume: return compare_swap_impl<spurious_failures, mo_acq_rel, &thread_info_base::atomic_rmw_acq_rel, mo_consume, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
case mo_acquire: return compare_swap_impl<spurious_failures, mo_acq_rel, &thread_info_base::atomic_rmw_acq_rel, mo_acquire, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
default: RL_VERIFY(false); return false;
}
}
case mo_seq_cst:
{
RL_VERIFY(mo_relaxed == failure_mo || mo_consume == failure_mo || mo_acquire == failure_mo || mo_seq_cst == failure_mo);
switch (failure_mo)
{
case mo_relaxed: return compare_swap_impl<spurious_failures, mo_seq_cst, &thread_info_base::atomic_rmw_seq_cst, mo_relaxed, &thread_info_base::atomic_load_relaxed_rmw>(cmp, xchg, info);
case mo_consume: return compare_swap_impl<spurious_failures, mo_seq_cst, &thread_info_base::atomic_rmw_seq_cst, mo_consume, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
case mo_acquire: return compare_swap_impl<spurious_failures, mo_seq_cst, &thread_info_base::atomic_rmw_seq_cst, mo_acquire, &thread_info_base::atomic_load_acquire_rmw>(cmp, xchg, info);
case mo_seq_cst: return compare_swap_impl<spurious_failures, mo_seq_cst, &thread_info_base::atomic_rmw_seq_cst, mo_seq_cst, &thread_info_base::atomic_load_seq_cst_rmw>(cmp, xchg, info);
default: RL_VERIFY(false); return false;
}
}
}
RL_VERIFY(false);
return false;
}
T exchange(T xchg, memory_order mo, debug_info_param info)
{
return rmw(rmw_type_t<rmw_type_swap>(), xchg, mo, info);
}
T fetch_add(typename atomic_add_type<T>::type value, memory_order mo, debug_info_param info)
{
return rmw(rmw_type_t<rmw_type_add>(), value, mo, info);
}
T fetch_sub(typename atomic_add_type<T>::type value, memory_order mo, debug_info_param info)
{
return rmw(rmw_type_t<rmw_type_sub>(), value, mo, info);
}
T fetch_and(T value, memory_order mo, debug_info_param info)
{
return rmw(rmw_type_t<rmw_type_and>(), value, mo, info);
}
T fetch_or(T value, memory_order mo, debug_info_param info)
{
return rmw(rmw_type_t<rmw_type_or>(), value, mo, info);
}
T fetch_xor(T value, memory_order mo, debug_info_param info)
{
return rmw(rmw_type_t<rmw_type_xor>(), value, mo, info);
}
template<typename Y, rmw_type_e type>
RL_INLINE
T rmw(rmw_type_t<type>, Y op, memory_order mo, debug_info_param info)
{
switch (mo)
{
case mo_relaxed: return rmw_impl<Y, mo_relaxed, &thread_info_base::atomic_rmw_relaxed>(rmw_type_t<type>(), op, info);
case mo_consume: return rmw_impl<Y, mo_consume, &thread_info_base::atomic_rmw_acquire>(rmw_type_t<type>(), op, info);
case mo_acquire: return rmw_impl<Y, mo_acquire, &thread_info_base::atomic_rmw_acquire>(rmw_type_t<type>(), op, info);
case mo_release: return rmw_impl<Y, mo_release, &thread_info_base::atomic_rmw_release>(rmw_type_t<type>(), op, info);
case mo_acq_rel: return rmw_impl<Y, mo_acq_rel, &thread_info_base::atomic_rmw_acq_rel>(rmw_type_t<type>(), op, info);
case mo_seq_cst: return rmw_impl<Y, mo_seq_cst, &thread_info_base::atomic_rmw_seq_cst>(rmw_type_t<type>(), op, info);
}
RL_VERIFY(false);
return T();
}
unpark_reason wait(context& c, bool is_timed, bool allow_spurious_wakeup, debug_info_param info)
{
sign_.check(info);
return c.threadx_->atomic_wait(impl_, is_timed, allow_spurious_wakeup, info);
}
thread_id_t wake(context& c, thread_id_t count, debug_info_param info)
{
sign_.check(info);
return c.threadx_->atomic_wake(impl_, count, info);
}
private:
T value_;
T history_ [atomic_history_size];
atomic_data* impl_;
unsigned last_index_;
signature<987654321> sign_;
bool initialized_;
bool already_failed_;
template<memory_order mo, unsigned (thread_info_base::*impl)(atomic_data* RL_RESTRICT data)>
T load_impl(debug_info_param info) const
{
context& c = ctx();
c.sched();
sign_.check(info);
if (false == c.invariant_executing)
{
unsigned const index = (c.threadx_->*impl)(impl_);
if ((unsigned)-1 == index)
{
RL_HIST(atomic_load_event<T>) {this, T(), mo, false} RL_HIST_END();
RL_ASSERT_IMPL(false, test_result_unitialized_access, "", info);
}
T const v = history_[index];
RL_HIST(atomic_load_event<T>) {this, v, mo, last_index_ != index} RL_HIST_END();
return v;
}
else
{
if (false == initialized_)
{
RL_HIST(atomic_load_event<T>) {this, T(), mo, false} RL_HIST_END();
RL_ASSERT_IMPL(false, test_result_unitialized_access, "", info);
}
return value_;
}
}
template<memory_order mo, unsigned (thread_info_base::*impl)(atomic_data* RL_RESTRICT data)>
void store_impl(T v, debug_info_param info)
{
context& c = ctx();
RL_VERIFY(false == c.invariant_executing);
c.sched();
sign_.check(info);
unsigned const index = (c.threadx_->*impl)(impl_);
T const prev = value_;
last_index_ = index;
history_[index] = v;
value_ = v;
initialized_ = true;
RL_HIST(atomic_store_event<T>) {this, prev, v, mo} RL_HIST_END();
}
template<bool spurious_failures, memory_order mo, unsigned (thread_info_base::*impl)(atomic_data* RL_RESTRICT data, bool&), memory_order failure_mo, unsigned (thread_info_base::*failure_impl)(atomic_data* RL_RESTRICT data)>
bool compare_swap_impl(T& cmp, T xchg, debug_info_param info)
{
context& c = ctx();
RL_VERIFY(false == c.invariant_executing);
c.sched();
sign_.check(info);
if (false == initialized_)
{
RL_HIST(atomic_load_event<T>) {this, T(), mo, false} RL_HIST_END();
RL_ASSERT_IMPL(false, test_result_unitialized_access, "", info);
}
bool success = false;
bool spurious_failure = false;
bool aba = false;
T const cmpv = cmp;
T const current = value_;
if (current == cmpv)
{
if (val(spurious_failures))
{
if (c.is_random_sched())
{
spurious_failure = (0 == c.rand(4, sched_type_cas_fail));
}
else
{
if (false == already_failed_)
{
spurious_failure = 0 == c.rand(2, sched_type_cas_fail);
if (spurious_failure)
already_failed_ = true;
}
}
}
if (false == spurious_failure)
{
success = true;
unsigned const index = (c.threadx_->*impl)(impl_, aba);
value_ = xchg;
last_index_ = index;
history_[index] = xchg;
}
}
if (false == success)
{
(c.threadx_->*failure_impl)(impl_);
cmp = current;
}
RL_HIST(atomic_cas_event<T>) {RL_INFO, this, current, cmpv, xchg, mo, success, spurious_failure, aba} RL_HIST_END();
return success;
}
template<typename Y, memory_order mo, unsigned (thread_info_base::*impl)(atomic_data* RL_RESTRICT data, bool&), rmw_type_e type>
T rmw_impl(rmw_type_t<type>, Y op, debug_info_param info)
{
context& c = ctx();
RL_VERIFY(false == c.invariant_executing);
c.sched();
sign_.check(info);
if (false == initialized_)
{
RL_HIST(atomic_load_event<T>) {this, T(), mo, false} RL_HIST_END();
RL_ASSERT_IMPL(false, test_result_unitialized_access, "", info);
}
bool aba;
unsigned const index = (c.threadx_->*impl)(impl_, aba);
T const prev_value = value_;
T const new_value = perform_rmw(rmw_type_t<type>(), prev_value, op);
value_ = new_value;
last_index_ = index;
history_[index] = new_value;
typedef atomic_rmw_event<T, Y> atomic_rmw_event_t;
RL_HIST(atomic_rmw_event_t) {RL_INFO, this, prev_value, op, new_value, mo, type} RL_HIST_END();
return prev_value;
}
RL_NOCOPY(generic_atomic);
};
template<typename T>
class atomic : public generic_atomic<T, false>
{
public:
atomic()
{
}
/*explicit*/ atomic(T value)
{
this->store(value, mo_relaxed, $);
}
atomic_proxy_const<T> operator () (debug_info_param info) const /*volatile*/
{
return atomic_proxy_const<T>(*this, info);
}
atomic_proxy<T> operator () (debug_info_param info) /*volatile*/
{
return atomic_proxy<T>(*this, info);
}
bool is_lock_free() const /*volatile*/
{
return true;
}
friend class atomic_proxy<T>;
friend class atomic_proxy_const<T>;
RL_NOCOPY(atomic);
};
typedef atomic<bool> atomic_bool;
typedef atomic<void*> atomic_address;
typedef atomic<char> atomic_char;
typedef atomic<signed char> atomic_schar;
typedef atomic<unsigned char> atomic_uchar;
typedef atomic<short> atomic_short;
typedef atomic<unsigned short> atomic_ushort;
typedef atomic<int> atomic_int;
typedef atomic<unsigned int> atomic_uint;
typedef atomic<long> atomic_long;
typedef atomic<unsigned long> atomic_ulong;
typedef atomic<long long> atomic_llong;
typedef atomic<unsigned long long> atomic_ullong;
//typedef atomic<char16_t> atomic_char16_t;
//typedef atomic<char32_t> atomic_char32_t;
typedef atomic<wchar_t> atomic_wchar_t;
//typedef atomic<int_least8_t> atomic_int_least8_t;
//typedef atomic<uint_least8_t> atomic_uint_least8_t;
//typedef atomic<int_least16_t> atomic_int_least16_t;
//typedef atomic<uint_least16_t> atomic_uint_least16_t;
//typedef atomic<int_least32_t> atomic_int_least32_t;
//typedef atomic<uint_least32_t> atomic_uint_least32_t;
//typedef atomic<int_least64_t> atomic_int_least64_t;
//typedef atomic<uint_least64_t> atomic_uint_least64_t;
//typedef atomic<int_fast8_t> atomic_int_fast8_t;
//typedef atomic<uint_fast8_t> atomic_uint_fast8_t;
//typedef atomic<int_fast16_t> atomic_int_fast16_t;
//typedef atomic<uint_fast16_t> atomic_uint_fast16_t;
//typedef atomic<int_fast32_t> atomic_int_fast32_t;
//typedef atomic<uint_fast32_t> atomic_uint_fast32_t;
//typedef atomic<int_fast64_t> atomic_int_fast64_t;
//typedef atomic<uint_fast64_t> atomic_uint_fast64_t;
typedef atomic<intptr_t> atomic_intptr_t;
typedef atomic<uintptr_t> atomic_uintptr_t;
typedef atomic<size_t> atomic_size_t;
//typedef atomic<ssize_t> atomic_ssize_t;
typedef atomic<ptrdiff_t> atomic_ptrdiff_t;
//typedef atomic<intmax_t> atomic_intmax_t;
//typedef atomic<uintmax_t> atomic_uintmax_t;
template<thread_id_t thread_count>
struct atomic_data_impl : atomic_data
{
typedef thread_info<thread_count> thread_info_t;
struct history_record
{
timestamp_t acq_rel_order_ [thread_count];
timestamp_t first_seen_order_ [thread_count];
bool busy_;
bool seq_cst_;
thread_id_t thread_id_;
timestamp_t acq_rel_timestamp_;
};
static size_t const history_size = atomic_history_size;
aligned<history_record> history_ [history_size];
unsigned current_index_;
waitset<thread_count> futex_ws_;
sync_var<thread_count> futex_sync_;
atomic_data_impl()
{
current_index_ = 0;
history_record& rec = history_[0];
history_[atomic_history_size - 1].busy_ = false;
rec.busy_ = false;
rec.seq_cst_ = false;
rec.thread_id_ = (thread_id_t)-1;
}
atomic_data_impl(thread_info_t& th)
{
current_index_ = 0;
history_[atomic_history_size - 1].busy_ = false;
history_record& rec = history_[0];
rec.busy_ = true;
rec.seq_cst_ = false;
rec.thread_id_ = th.index_;
th.own_acq_rel_order_ += 1;
rec.acq_rel_timestamp_ = th.own_acq_rel_order_;
foreach<thread_count>(rec.acq_rel_order_, assign_zero);
foreach<thread_count>(rec.first_seen_order_, assign<(timestamp_t)-1>);
rec.first_seen_order_[th.index_] = th.own_acq_rel_order_;
}
};
}
#endif

View File

@@ -0,0 +1,148 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_ATOMIC_EVENTS_HPP
#define RL_ATOMIC_EVENTS_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "memory_order.hpp"
#include "rmw.hpp"
namespace rl
{
template<typename T> class atomic;
template<typename T, bool strong_init> class generic_atomic;
template<typename T>
struct atomic_add_type
{
typedef T type;
typedef T output_type;
};
template<typename T>
struct atomic_add_type<T*>
{
typedef ptrdiff_t type;
typedef void* output_type;
};
template<typename T>
struct atomic_cas_event
{
typedef typename atomic_add_type<T>::output_type type;
debug_info var_info_;
void const* var_addr_;
type cur_value_;
type cmp_value_;
type xchg_value_;
memory_order mo_;
bool success_;
bool spurious_failure_;
bool aba_;
void output(std::ostream& s) const
{
s << "<" << std::hex << var_addr_ << std::dec << ">"
<< " CAS "
<< (success_ ? "succ " : "fail ")
<< (spurious_failure_ ? "[SPURIOUSLY] " : "")
<< (aba_ ? "[ABA] " : "")
<< "orig=" << cur_value_
<< ", cmp=" << cmp_value_
<< ", xchg=" << xchg_value_
<< ", order=" << format(mo_);
}
};
template<typename T>
struct atomic_load_event
{
typedef typename atomic_add_type<T>::output_type type;
void const* var_addr_;
type value_;
memory_order mo_;
bool not_current_;
void output(std::ostream& s) const
{
s << "<" << std::hex << var_addr_ << std::dec << ">"
<< " atomic load, value=" << value_
<< (not_current_ ? " [NOT CURRENT]" : "")
<< ", order=" << format(mo_);
}
};
template<typename T>
struct atomic_store_event
{
typedef typename atomic_add_type<T>::output_type type;
void const* var_addr_;
type prev_value_;
type value_;
memory_order mo_;
void output(std::ostream& s) const
{
s << "<" << std::hex << var_addr_ << std::dec << ">"
<< " atomic store, value=" << value_
<< ", (prev value=" << prev_value_ << ")"
<< ", order=" << format(mo_);
}
};
template<typename T, typename Y>
struct atomic_rmw_event
{
typedef typename atomic_add_type<T>::output_type type;
debug_info var_info_;
void const* var_addr_;
type prev_value_;
Y op_value_;
type new_value_;
memory_order mo_;
rmw_type_e type_;
void output(std::ostream& s) const
{
s << "<" << std::hex << var_addr_ << std::dec << ">"
<< " " << format(type_) << " "
<< ", prev=" << prev_value_
<< ", arg=" << op_value_
<< ", new=" << new_value_
<< ", order=" << format(mo_);
}
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More