mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Add ReaderWriterQueue and update ConcurrentQueue
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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(-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(-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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
};
|
||||
|
||||
} }
|
||||
@@ -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) {
|
||||
auto 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 result = 0;
|
||||
test_type test;
|
||||
const char* failReason;
|
||||
if (singleSeed) {
|
||||
if (!run_test(seed, SINGLE_SEED_ITERATIONS, test, failReason)) {
|
||||
result = 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);
|
||||
|
||||
int 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) {
|
||||
result = 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 result;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
acc09bbe65a1837a08912774c7fed178547514e6
|
||||
include/icsneo/third-party/concurrentqueue/tests/relacy/relacy/example/cli_ws_deque/cli_ws_deque.cpp
Vendored
+195
@@ -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>();
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli_ws_deque", "cli_ws_deque.vcproj", "{0B597F19-DEBB-4832-B520-9A93A286D595}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0B597F19-DEBB-4832-B520-9A93A286D595}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0B597F19-DEBB-4832-B520-9A93A286D595}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0B597F19-DEBB-4832-B520-9A93A286D595}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0B597F19-DEBB-4832-B520-9A93A286D595}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+205
@@ -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>
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
|
||||
|
||||
+181
@@ -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>();
|
||||
}
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "condvar", "condvar.vcproj", "{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Vendored
+205
@@ -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>
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "condvar", "condvar.vcproj", "{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Vendored
+205
@@ -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>
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
+14
@@ -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"
|
||||
|
||||
|
||||
|
||||
include/icsneo/third-party/concurrentqueue/tests/relacy/relacy/example/eao_blocking/eao_blocking.cpp
Vendored
+178
@@ -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);
|
||||
}
|
||||
};
|
||||
*/
|
||||
Vendored
+707
@@ -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);
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eventcount", "eventcount.vcproj", "{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E5}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E5}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E5}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E5}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+203
@@ -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>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eventcount", "eventcount.vcproj", "{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+203
@@ -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>
|
||||
Vendored
+8
@@ -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
@@ -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"
|
||||
|
||||
Vendored
+316
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Vendored
+17
@@ -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);
|
||||
}
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "examples", "examples.vcproj", "{6CC59CF8-408B-441B-8F65-15651210CB82}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{6CC59CF8-408B-441B-8F65-15651210CB82}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{6CC59CF8-408B-441B-8F65-15651210CB82}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{6CC59CF8-408B-441B-8F65-15651210CB82}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{6CC59CF8-408B-441B-8F65-15651210CB82}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+208
@@ -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>
|
||||
+202
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
+16
@@ -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"
|
||||
|
||||
|
||||
|
||||
+195
@@ -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>();
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "java_ws_deque", "java_ws_deque.vcproj", "{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+205
@@ -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>
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
|
||||
|
||||
+511
@@ -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);
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mpmc", "mpmc.vcproj", "{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Vendored
+207
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+8
@@ -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
@@ -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"
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mutex_business_logic", "mutex_business_logic.vcproj", "{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+205
@@ -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>
|
||||
+131
@@ -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>();
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
include/icsneo/third-party/concurrentqueue/tests/relacy/relacy/example/mutex_business_logic/stdafx.h
Vendored
+14
@@ -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"
|
||||
|
||||
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "peterson", "peterson.vcproj", "{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+205
@@ -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>
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "peterson", "peterson.vcproj", "{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+200
@@ -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>
|
||||
Vendored
+469
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
+14
@@ -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"
|
||||
|
||||
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proxy_collector", "proxy_collector.vcproj", "{31994C0C-3BAD-4F25-8BC8-3206FF349B29}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B29}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B29}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B29}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B29}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+291
@@ -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>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proxy_collector", "proxy_collector.vcproj", "{31994C0C-3BAD-4F25-8BC8-3206FF349B29}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B29}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B29}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B29}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B29}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+299
@@ -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>
|
||||
+318
@@ -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);
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define _SECURE_SCL 0
|
||||
#endif
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ref_counting", "ref_counting.vcproj", "{31994C0C-3BAD-4F25-8BC8-3206FF349B28}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B28}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B28}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B28}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B28}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+208
@@ -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>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ref_counting", "ref_counting.vcproj", "{31994C0C-3BAD-4F25-8BC8-3206FF349B28}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B28}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B28}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B28}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{31994C0C-3BAD-4F25-8BC8-3206FF349B28}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+207
@@ -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>
|
||||
include/icsneo/third-party/concurrentqueue/tests/relacy/relacy/example/ref_counting/ref_counting.cpp
Vendored
+269
@@ -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;
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define _SECURE_SCL 0
|
||||
#endif
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
+11
@@ -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}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smr", "smr.vcproj", "{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Vendored
+199
@@ -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>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smr", "smr.vcproj", "{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{BC168133-5E3D-4691-BA15-8E0FD61DFDB5}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Vendored
+199
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define _SECURE_SCL 0
|
||||
#endif
|
||||
|
||||
#define RL_MSVC_OUTPUT
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spsc_queue", "spsc_queue.vcproj", "{2F0B1A3B-27CA-47D4-A9D1-5EC66BB0A85B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2F0B1A3B-27CA-47D4-A9D1-5EC66BB0A85B}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{2F0B1A3B-27CA-47D4-A9D1-5EC66BB0A85B}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{2F0B1A3B-27CA-47D4-A9D1-5EC66BB0A85B}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{2F0B1A3B-27CA-47D4-A9D1-5EC66BB0A85B}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+207
@@ -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>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spsc_queue", "spsc_queue.vcproj", "{D4756EE9-3953-4E17-B1B5-E89F853303C1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D4756EE9-3953-4E17-B1B5-E89F853303C1}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+200
@@ -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>
|
||||
Vendored
+202
@@ -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>();
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
+13
@@ -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"
|
||||
|
||||
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
lock-free stack
|
||||
code contains several bugs: access to freed memory and ABA problem
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stack", "stack.vcproj", "{4D6D7FC3-66D1-4F80-B434-2FDCBBFBC9F5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4D6D7FC3-66D1-4F80-B434-2FDCBBFBC9F5}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{4D6D7FC3-66D1-4F80-B434-2FDCBBFBC9F5}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{4D6D7FC3-66D1-4F80-B434-2FDCBBFBC9F5}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{4D6D7FC3-66D1-4F80-B434-2FDCBBFBC9F5}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Vendored
+201
@@ -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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user