Initial Commit - Lesson 31 (Commit #1)
This commit is contained in:
5
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/README.txt
vendored
Normal file
5
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/README.txt
vendored
Normal 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
|
||||
114
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/corealgo.h
vendored
Normal file
114
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/corealgo.h
vendored
Normal 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;
|
||||
}
|
||||
62
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/enqueue_dequeue_many.cpp
vendored
Normal file
62
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/enqueue_dequeue_many.cpp
vendored
Normal 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;
|
||||
}
|
||||
37
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/enqueue_dequeue_one.cpp
vendored
Normal file
37
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/enqueue_dequeue_one.cpp
vendored
Normal 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;
|
||||
}
|
||||
14
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/makefile
vendored
Normal file
14
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/CDSChecker/makefile
vendored
Normal 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
|
||||
84
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/common/simplethread.cpp
vendored
Normal file
84
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/common/simplethread.cpp
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
162
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/common/simplethread.h
vendored
Normal file
162
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/common/simplethread.h
vendored
Normal 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;
|
||||
};
|
||||
144
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/common/systemtime.cpp
vendored
Normal file
144
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/common/systemtime.cpp
vendored
Normal 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
|
||||
33
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/common/systemtime.h
vendored
Normal file
33
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/common/systemtime.h
vendored
Normal 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);
|
||||
}
|
||||
669
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/corealgos.h
vendored
Normal file
669
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/corealgos.h
vendored
Normal 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;
|
||||
};
|
||||
|
||||
} }
|
||||
867
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/fuzztests/fuzztests.cpp
vendored
Normal file
867
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/fuzztests/fuzztests.cpp
vendored
Normal 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;
|
||||
}
|
||||
9
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/fuzztests/makefile
vendored
Normal file
9
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/fuzztests/makefile
vendored
Normal 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)
|
||||
200
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/freelist.cpp
vendored
Normal file
200
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/freelist.cpp
vendored
Normal 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;
|
||||
}
|
||||
722
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/integrated.cpp
vendored
Normal file
722
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/integrated.cpp
vendored
Normal 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;
|
||||
}
|
||||
29
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/makefile
vendored
Normal file
29
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/makefile
vendored
Normal 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)
|
||||
25
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/CHANGES
vendored
Normal file
25
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/CHANGES
vendored
Normal 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)
|
||||
|
||||
19
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/LICENSE
vendored
Normal file
19
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/LICENSE
vendored
Normal 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.
|
||||
1
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/VERSION
vendored
Normal file
1
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/VERSION
vendored
Normal file
@@ -0,0 +1 @@
|
||||
acc09bbe65a1837a08912774c7fed178547514e6
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
|
||||
|
||||
181
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/condvar/condvar.cpp
vendored
Normal file
181
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/condvar/condvar.cpp
vendored
Normal 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>();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
|
||||
|
||||
511
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/mpmc/mpmc.cpp
vendored
Normal file
511
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/mpmc/mpmc.cpp
vendored
Normal 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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
481
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/mpmc/pcx.h
vendored
Normal file
481
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/mpmc/pcx.h
vendored
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
10
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/mpmc/stdafx.h
vendored
Normal file
10
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/mpmc/stdafx.h
vendored
Normal 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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define _SECURE_SCL 0
|
||||
#endif
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define _SECURE_SCL 0
|
||||
#endif
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
@@ -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}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
189
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/smr/smr.cpp
vendored
Normal file
189
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/smr/smr.cpp
vendored
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
10
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/smr/stdafx.h
vendored
Normal file
10
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/smr/stdafx.h
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define _SECURE_SCL 0
|
||||
#endif
|
||||
|
||||
#define RL_MSVC_OUTPUT
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
lock-free stack
|
||||
code contains several bugs: access to freed memory and ABA problem
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
105
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/stack/stack.cpp
vendored
Normal file
105
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/example/stack/stack.cpp
vendored
Normal 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>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define _SECURE_SCL 0
|
||||
#endif
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
#include "../../relacy/relacy_std.hpp"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
#include "../../relacy/relacy_std.hpp"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
723
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/relacy/atomic.hpp
vendored
Normal file
723
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/relacy/atomic.hpp
vendored
Normal 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
|
||||
148
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/relacy/atomic_events.hpp
vendored
Normal file
148
Plugins/GameLiftServerSDK/ThirdParty/concurrentqueue/tests/relacy/relacy/relacy/atomic_events.hpp
vendored
Normal 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
Reference in New Issue
Block a user