Lesson 35 - Get Compute Auth Token Working

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
Relacy Race Detector
Copyright (c) 2008-2013, Dmitry S. Vyukov
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
- The name of the owner may not be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE OWNER "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1 @@
acc09bbe65a1837a08912774c7fed178547514e6

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,83 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_FENCE_HPP
#define RL_FENCE_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "context.hpp"
#include "memory_order.hpp"
namespace rl
{
struct atomic_fence_event
{
memory_order mo_;
bool is_thread_fence_;
void output(std::ostream& s) const
{
s << (is_thread_fence_ ? "" : "compiler ")
<< format(mo_) << " fence";
}
};
RL_INLINE
void atomic_thread_fence(memory_order mo, debug_info_param info)
{
context& c = ctx();
RL_VERIFY(false == c.invariant_executing);
switch (mo)
{
case mo_relaxed:
RL_VERIFY(false);
break;
case mo_consume:
case mo_acquire:
c.atomic_thread_fence_acquire();
break;
case mo_release:
c.atomic_thread_fence_release();
break;
case mo_acq_rel:
c.atomic_thread_fence_acq_rel();
break;
case mo_seq_cst:
c.atomic_thread_fence_seq_cst();
break;
}
RL_HIST(atomic_fence_event) {mo, true} RL_HIST_END();
}
RL_INLINE
void atomic_signal_fence(memory_order mo, debug_info_param info)
{
context& c = ctx();
RL_HIST(atomic_fence_event) {mo, false} RL_HIST_END();
}
}
#endif

View File

@@ -0,0 +1,57 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_BACKOFF_HPP
#define RL_BACKOFF_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "context_base.hpp"
namespace rl
{
inline void yield(unsigned count, debug_info_param info)
{
ctx().yield(count, info);
}
template<unsigned factor_t, unsigned add_t>
class backoff_t
{
public:
backoff_t()
: count_(1)
{
}
void yield(debug_info_param info)
{
rl::yield(count_, info);
count_ = count_ * factor_t + add_t;
}
private:
unsigned count_;
};
typedef backoff_t<1, 0> backoff;
typedef backoff_t<1, 1> linear_backoff;
typedef backoff_t<2, 0> exp_backoff;
}
#endif

View File

@@ -0,0 +1,144 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_BASE_HPP
#define RL_BASE_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "pch.hpp"
#include "platform.hpp"
namespace rl
{
size_t const subsequent_timed_wait_limit = 4;
}
#define RL_TEST
#ifdef RL_JAVA_MODE
# define RL_GC
# define RL_NO_MALLOC
# define RL_JAVA_API
# define RL_JAVA_MM
#endif
#ifdef RL_CLI_MODE
# define RL_GC
# define RL_NO_MALLOC
# define RL_CLI_API
# define RL_CLI_MM
#endif
#ifdef RL_POSIX_MODE
# define RL_POSIX_API
#endif
#ifdef RL_WIN_MODE
# define RL_WIN_API
#endif
#ifdef RL_CPP_MODE
# define RL_CPP_API
# define RL_CPP_MM
#endif
#if defined(RL_JAVA_MM) || defined(RL_CLI_MM)
# define RL_IMPROVED_SEQ_CST_FENCE
# define RL_IMPROVED_SEQ_CST_RMW
#endif
namespace rl
{
#define RL_NOCOPY(CLASS) \
private: \
CLASS(CLASS const&); \
CLASS& operator = (CLASS const&);
/**/
template<typename T = void>
class nocopy
{
nocopy(nocopy const&);
nocopy& operator = (nocopy const&);
protected:
nocopy() {}
};
template<size_t sz, size_t base = 4>
struct align_pad
{
template<bool perfect, bool fit, int fake> struct helper
{
struct type { char pad [base - sz]; };
};
template<int fake> struct helper<true, true, fake>
{
struct type {};
};
template<bool perfect, int fake> struct helper<perfect, false, fake>
{
typedef typename align_pad<sz, base * 2>::type type;
};
typedef typename helper<sz == base, sz <= base, 0>::type type;
};
template<typename T>
struct aligned : T, align_pad<sizeof(T)>::type
{};
template<typename T>
T val(T x)
{
return x;
}
}
#include "defs.hpp"
#define RL_INFO ::rl::debug_info(__FUNCTION__, __FILE__, __LINE__)
#define $ RL_INFO
#ifdef RL_DO_ASSERT
# if RL_DO_ASSERT
# define RL_DO_ASSERT_IMPL
# endif
#else
# ifdef _DEBUG
# define RL_DO_ASSERT_IMPL
# endif
#endif
#ifdef _MSC_VER
# define RL_INT3() __debugbreak(); abort()
#else
# define RL_INT3() abort()
#endif
#ifdef RL_DO_ASSERT_IMPL
# define RL_VERIFY(x) do { if (!((void)0, (x))) { \
::rl::assert_failed(#x, $); RL_INT3(); } } while ((void)0, 0)
#else
# define RL_VERIFY(x) (void)0
#endif
#endif

View File

@@ -0,0 +1,52 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_CLI_HPP
#define RL_CLI_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "context_base.hpp"
#include "atomic_fence.hpp"
namespace rl
{
struct Thread
{
static void MemoryBarrier(debug_info_param info)
{
atomic_thread_fence(mo_seq_cst, info);
}
template<typename T>
static T VolatileRead(generic_atomic<T, true> const& v, debug_info_param info)
{
return v.load(mo_acquire, info);
}
template<typename T>
static void VolatileWrite(generic_atomic<T, true>& v, T x, debug_info_param info)
{
v.store(x, mo_release, info);
}
static void SpinWait(int iterations, debug_info_param info)
{
ctx().yield(iterations, info);
}
};
}
#endif

View File

@@ -0,0 +1,67 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_CLI_INTERLOCKED_HPP
#define RL_CLI_INTERLOCKED_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "atomic.hpp"
namespace rl
{
struct Interlocked
{
template<typename T>
static T Add(generic_atomic<T, true>& v, T x, debug_info_param info)
{
T result = v.rmw(rmw_type_t<rmw_type_add>(), x, mo_seq_cst, info) + x;
return result;
}
template<typename T>
static T CompareExchange(generic_atomic<T, true>& v, T xchg, T cmp, debug_info_param info)
{
v.compare_exchange(bool_t<false>(), cmp, xchg, mo_seq_cst, mo_seq_cst, info);
return cmp;
}
template<typename T>
static T Increment(generic_atomic<T, true>& v, debug_info_param info)
{
return Add(v, (T)1, info);
}
template<typename T>
static T Decrement(generic_atomic<T, true>& v, debug_info_param info)
{
return Add(v, (T)-1, info);
}
template<typename T>
static T Exchange(generic_atomic<T, true>& v, T x, debug_info_param info)
{
T result = v.rmw(rmw_type_t<rmw_type_swap>(), x, mo_seq_cst, info);
return result;
}
template<typename T>
static T Read(generic_atomic<T, true> const& v, debug_info_param info)
{
return v.load(mo_acquire, info);
}
};
}
#endif

View File

@@ -0,0 +1,158 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_CLI_VAR_HPP
#define RL_CLI_VAR_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "atomic.hpp"
namespace rl
{
template<typename T> class nvar;
template<typename T>
class nvar_proxy
{
public:
typedef typename atomic_add_type<T>::type add_type;
template<typename Y> friend class nvar;
operator T () const
{
return load();
}
T operator = (T value)
{
store(value);
return value;
}
T operator = (nvar_proxy const& r)
{
T const value = r.load();
store(value);
return *this;
}
T operator ++ (int)
{
T tmp = load();
store(tmp + 1);
return tmp;
}
T operator -- (int)
{
T tmp = load();
store(tmp - 1);
return tmp;
}
T operator ++ ()
{
T tmp = load();
store(tmp + 1);
return tmp + 1;
}
T operator -- ()
{
T tmp = load();
store(tmp - 1);
return tmp - 1;
}
T operator += (add_type value)
{
T tmp = load();
store(tmp + value);
return tmp + value;
}
T operator -= (add_type value)
{
T tmp = load();
store(tmp - value);
return tmp - value;
}
private:
nvar<T>& var_;
debug_info info_;
nvar_proxy(nvar<T>& var, debug_info_param info)
: var_(var)
, info_(info)
{
}
T load() const
{
return var_.load(mo_relaxed, info_);
}
void store(T value)
{
var_.store(value, mo_relaxed, info_);
}
};
template<typename T>
class nvar : public generic_atomic<T, true>
{
public:
typedef nvar_proxy<T> proxy_t;
friend class nvar_proxy<T>;
nvar()
{
}
explicit nvar(T value)
{
this->store(value, mo_relaxed, $);
}
nvar(nvar const& r)
{
T const value = r.load(mo_relaxed, $);
this->store(value, mo_relaxed, $);
}
nvar(proxy_t const& r)
{
T const value = r.load();
this->store(value, mo_relaxed, r.info_);
}
proxy_t operator () (debug_info_param info)
{
return proxy_t(*this, info);
}
private:
nvar& operator = (nvar const&);
};
}
#endif

View File

@@ -0,0 +1,161 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_CLI_VOLATILE_HPP
#define RL_CLI_VOLATILE_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "atomic.hpp"
//!!! fix Java volatiles!
// they must be modeled as seq_cst stores/loads
namespace rl
{
template<typename T> class nvolatile;
template<typename T>
class nvolatile_proxy
{
public:
typedef typename atomic_add_type<T>::type add_type;
template<typename Y> friend class nvolatile;
operator T () const
{
return load();
}
T operator = (T value)
{
store(value);
return value;
}
T operator = (nvolatile_proxy const& r)
{
T const value = r.load();
store(value);
return *this;
}
T operator ++ (int)
{
T tmp = load();
store(tmp + 1);
return tmp;
}
T operator -- (int)
{
T tmp = load();
store(tmp - 1);
return tmp;
}
T operator ++ ()
{
T tmp = load();
store(tmp + 1);
return tmp + 1;
}
T operator -- ()
{
T tmp = load();
store(tmp - 1);
return tmp - 1;
}
T operator += (add_type value)
{
T tmp = load();
store(tmp + value);
return tmp + value;
}
T operator -= (add_type value)
{
T tmp = load();
store(tmp - value);
return tmp - value;
}
private:
nvolatile<T>& var_;
debug_info info_;
nvolatile_proxy(nvolatile<T>& var, debug_info_param info)
: var_(var)
, info_(info)
{
}
T load() const
{
return var_.load(mo_acquire, info_);
}
void store(T value)
{
var_.store(value, mo_release, info_);
}
};
template<typename T>
class nvolatile : public generic_atomic<T, true>
{
public:
typedef nvolatile_proxy<T> proxy_t;
friend class nvolatile_proxy<T>;
nvolatile()
{
}
explicit nvolatile(T value)
{
//??? whether here must be mo_relaxed or mo_release?
this->store(value, mo_release, $);
}
nvolatile(nvolatile const& r)
{
T const value = r.load(mo_acquire, $);
//??? whether here must be mo_relaxed or mo_release?
this->store(value, mo_release, $);
}
nvolatile(proxy_t const& r)
{
T const value = r.var_.load(mo_acquire, r.info_);
//??? whether here must be mo_relaxed or mo_release?
this->store(value, mo_release, r.info_);
}
proxy_t operator () (debug_info_param info)
{
return proxy_t(*this, info);
}
};
}
#endif

View File

@@ -0,0 +1,81 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_CONTEXT_ADDR_HASH_HPP
#define RL_CONTEXT_ADDR_HASH_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
namespace rl
{
struct context_addr_hash_iface
{
virtual size_t get_addr_hash (void const* p) = 0;
virtual ~context_addr_hash_iface () {} // to calm down g++
};
template<typename base_t, thread_id_t thread_count>
class context_addr_hash_impl : protected base_t
{
public:
context_addr_hash_impl(thread_id_t thread_count_param, test_params& params)
: base_t(thread_count_param, params)
{
}
void iteration_begin()
{
base_t::iteration_begin();
hash_map_.clear();
hash_seq_ = 0;
}
private:
struct entry
{
uintptr_t ptr_;
size_t hash_;
};
typedef map<void const*, size_t>::type hash_map_t;
hash_map_t hash_map_;
size_t hash_seq_;
virtual size_t get_addr_hash (void const* p)
{
//!!! accept 'table size' to do 'hash % table_size'
// will give more information for state exploration
hash_map_t::iterator iter (hash_map_.find(p));
if (iter != hash_map_.end() && iter->first == p)
{
return iter->second;
}
else
{
//!!! distribute hashes more randomly, use rand()
size_t hash = hash_seq_++;
hash_map_.insert(std::make_pair(p, hash));
return hash;
}
}
};
}
#endif

View File

@@ -0,0 +1,332 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_CONTEXT_BASE_HPP
#define RL_CONTEXT_BASE_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "history.hpp"
#include "memory.hpp"
#include "test_result.hpp"
#include "slab_allocator.hpp"
#include "test_params.hpp"
#include "random.hpp"
#include "foreach.hpp"
#include "thread_base.hpp"
#include "context_addr_hash.hpp"
#ifdef RL_DEBUGBREAK_ON_ASSERT
# ifdef _MSC_VER
# define RL_DEBUGBREAK_ON_ASSERT_IMPL {if (IsDebuggerPresent()) __debugbreak();}
# else
# define RL_DEBUGBREAK_ON_ASSERT_IMPL {__asm("int3");}
# endif
#else
# define RL_DEBUGBREAK_ON_ASSERT_IMPL
#endif
#ifdef RL_DEBUGBREAK_ON_FAILURE
# ifdef _MSC_VER
# define RL_DEBUGBREAK_ON_FAILURE_IMPL {if (IsDebuggerPresent()) __debugbreak();}
# else
# define RL_DEBUGBREAK_ON_FAILURE_IMPL {__asm("int3");}
# endif
#else
# define RL_DEBUGBREAK_ON_FAILURE_IMPL
#endif
namespace rl
{
class thread_info_base;
struct atomic_data {};
struct var_data
{
virtual void init(thread_info_base& th) = 0;
virtual bool store(thread_info_base& th) = 0;
virtual bool load(thread_info_base& th) = 0;
virtual ~var_data() {} // just to calm down gcc
};
struct generic_mutex_data;
struct condvar_data;
struct sema_data;
struct event_data;
struct user_msg_event
{
string msg_;
void output(std::ostream& s) const
{
s << msg_;
}
};
class context;
template<int fake = 0>
struct context_holder
{
static context* instance_;
static long volatile ctx_seq;
};
template<int fake>
long volatile context_holder<fake>::ctx_seq = 0;
class context
: public thread_local_context_iface
, public context_addr_hash_iface
, nocopy<>
{
public:
static context& instance()
{
//!!! disabled for check in operator new RL_VERIFY(context_holder<>::instance_);
return *context_holder<>::instance_;
}
static bool is_instance()
{
return context_holder<>::instance_;
}
virtual atomic_data* atomic_ctor(void* ctx) = 0;
virtual void atomic_dtor(atomic_data* data) = 0;
virtual var_data* var_ctor() = 0;
virtual void var_dtor(var_data* data) = 0;
virtual generic_mutex_data* mutex_ctor(bool is_rw, bool is_exclusive_recursive, bool is_shared_recursive, bool failing_try_lock) = 0;
virtual void mutex_dtor(generic_mutex_data* m) = 0;
virtual condvar_data* condvar_ctor(bool allow_spurious_wakeups) = 0;
virtual void condvar_dtor(condvar_data* cv) = 0;
virtual sema_data* sema_ctor(bool spurious_wakeups, unsigned initial_count, unsigned max_count) = 0;
virtual void sema_dtor(sema_data* cv) = 0;
virtual event_data* event_ctor(bool manual_reset, bool initial_state) = 0;
virtual void event_dtor(event_data* cv) = 0;
virtual void rl_global_fence() = 0;
virtual void sched() = 0;
virtual void yield(unsigned count, debug_info_param info) = 0;
virtual void fail_test(char const* desc, test_result_e res, debug_info_param info) = 0;
virtual void rl_until(char const* desc, debug_info_param info) = 0;
virtual void* alloc(size_t size, bool is_array, debug_info_param info) = 0;
#ifdef RL_GC
virtual void* alloc(size_t size, bool is_array, void(*dtor)(void*), debug_info_param info) = 0;
#endif
virtual void free(void* p, bool is_array, debug_info_param info) = 0;
virtual void* alloc(size_t size) = 0;
virtual void free(void* p) = 0;
virtual size_t prev_alloc_size() = 0;
virtual void set_debug_info(debug_info_param info) = 0;
virtual void fiber_proc_impl(int thread_index) = 0;
virtual unpark_reason park_current_thread(bool is_timed,
bool allow_spurious_wakeup,
bool do_switch,
debug_info_param info) = 0;
virtual void unpark_thread(thread_id_t th, bool do_switch, debug_info_param info) = 0;
virtual void switch_back(debug_info_param info) = 0;
virtual void atomic_thread_fence_acquire() = 0;
virtual void atomic_thread_fence_release() = 0;
virtual void atomic_thread_fence_acq_rel() = 0;
virtual void atomic_thread_fence_seq_cst() = 0;
virtual unsigned rand(unsigned limit, sched_type t) = 0;
virtual win_waitable_object* create_thread(void*(*fn)(void*), void* ctx) = 0;
virtual unpark_reason wfmo_park(void** ws,
win_waitable_object** wo,
size_t count,
bool wait_all,
bool is_timed,
debug_info_param info) = 0;
int get_errno();
void set_errno(int value);
thread_info_base* threadx_;
timestamp_t* seq_cst_fence_order_;
bool invariant_executing;
RL_INLINE bool collecting_history() const
{
return params_.collect_history && false == invariant_executing;
}
template<typename event_t>
void exec_log(debug_info_param info, event_t const& ev);
void exec_log_msg(debug_info_param info, char const* msg)
{
user_msg_event ev = {msg};
exec_log(info, ev);
}
bool is_random_sched() const
{
return is_random_sched_;
}
unsigned get_ctx_seq() const
{
return ctx_seq_;
}
void disable_preemption();
void enable_preemption();
virtual thread_id_t get_thread_count() const = 0;
thread_id_t current_thread() const
{
return threadx_->index_;
}
void iteration_begin()
{
}
protected:
history_mgr history_;
test_params& params_;
unsigned disable_preemption_;
int disable_alloc_;
context(thread_id_t thread_count, test_params& params)
: history_(*params.output_stream, thread_count)
, params_(params)
, disable_alloc_(1)
{
RL_VERIFY(0 == context_holder<>::instance_);
context_holder<>::instance_ = this;
is_random_sched_ = params_.search_type == random_scheduler_type;
#ifdef _MSC_VER
ctx_seq_ = _InterlockedExchangeAdd(&context_holder<>::ctx_seq, 1) + 1;
#else
ctx_seq_ = __sync_fetch_and_add(&context_holder<>::ctx_seq, 1) + 1;
#endif
}
virtual ~context()
{
RL_VERIFY(this == context_holder<>::instance_);
context_holder<>::instance_ = 0;
}
private:
bool is_random_sched_;
unsigned ctx_seq_;
};
template<int fake>
context* context_holder<fake>::instance_ = 0;
inline context& ctx()
{
return context::instance();
}
inline bool is_ctx()
{
return context::is_instance();
}
inline int get_errno()
{
return ctx().get_errno();
}
inline void set_errno(int value)
{
return ctx().set_errno(value);
}
class preemption_disabler : nocopy<>
{
public:
preemption_disabler(context& c)
: c_(c)
{
c_.disable_preemption();
}
~preemption_disabler()
{
c_.enable_preemption();
}
private:
context& c_;
};
}
#define RL_HIST_IMPL(C, INFO, TYPE) \
do { \
if (C.collecting_history()) { \
rl::debug_info const& rl_info_c = INFO; \
rl::context& rl_hist_c = C; \
TYPE ev = \
/**/
#define RL_HIST_END() \
; \
rl_hist_c.exec_log(rl_info_c, ev); \
} \
} while ((void)0, 0) \
/**/
#define RL_HIST_CTX(TYPE) RL_HIST_IMPL((*this), info, TYPE)
#define RL_HIST(TYPE) RL_HIST_IMPL(c, info, TYPE)
#define RL_LOG(desc) rl::ctx().exec_log_msg(RL_INFO, desc)
#ifdef _MSC_VER
# define RL_ASSERT_IMPL(x, res, str, info) do {if (!((void)0, (x))) {{RL_DEBUGBREAK_ON_ASSERT_IMPL} rl::ctx().fail_test(str, res, info);}} while ((void)0, 0)
#else
# define RL_ASSERT_IMPL(x, res, str, info) do {if (!((void)0, (x))) rl::ctx().fail_test(str, res, info);} while ((void)0, 0)
#endif
#define RL_ASSERT(x) RL_ASSERT_IMPL(x, rl::test_result_user_assert_failed, "assertion: " #x, RL_INFO)
#define RL_UNTIL(x) do {if ((x)) rl::ctx().rl_until(#x, RL_INFO);} while ((void)0, 0)
#endif

View File

@@ -0,0 +1,72 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_CONTEXT_BASE_IMPL_HPP
#define RL_CONTEXT_BASE_IMPL_HPP
#ifdef _MSC_VER
# pragma once
#endif
namespace rl
{
/*
inline void context::disable_history()
{
RL_VERIFY(threadx_);
threadx_->disable_history_ += 1;
}
inline void context::enable_history()
{
RL_VERIFY(threadx_);
RL_VERIFY(threadx_->disable_history_);
threadx_->disable_history_ -= 1;
}
*/
inline void context::disable_preemption()
{
disable_preemption_ += 1;
}
inline void context::enable_preemption()
{
disable_preemption_ -= 1;
}
inline int context::get_errno()
{
RL_VERIFY(threadx_);
return threadx_->errno_;
}
inline void context::set_errno(int value)
{
RL_VERIFY(threadx_);
threadx_->errno_ = value;
}
template<typename event_t>
void context::exec_log(debug_info_param info, event_t const& ev)
{
RL_VERIFY(collecting_history());
disable_alloc_ += 1;
history_.exec_log(threadx_ ? threadx_->index_ : -1, info, ev, params_.output_history);
disable_alloc_ -= 1;
}
}
#endif

View File

@@ -0,0 +1,168 @@
/* Relacy Race Detector
* Copyright (c) 2008-2013, Dmitry S. Vyukov
* All rights reserved.
* This software is provided AS-IS with no warranty, either express or implied.
* This software is distributed under a license and may not be copied,
* modified or distributed except as expressly authorized under the
* terms of the license contained in the file LICENSE in this distribution.
*/
#ifndef RL_CONTEXT_BOUND_SCHEDULER_HPP
#define RL_CONTEXT_BOUND_SCHEDULER_HPP
#ifdef _MSC_VER
# pragma once
#endif
#include "base.hpp"
#include "full_search_scheduler.hpp"
#include "foreach.hpp"
namespace rl
{
template<thread_id_t thread_count>
struct context_bound_scheduler_thread_info : tree_search_scheduler_thread_info<thread_count>
{
unsigned sched_count_;
unsigned forced_context_switch_count_;
void reset(test_params& params)
{
tree_search_scheduler_thread_info<thread_count>::reset(params);
sched_count_ = 0;
forced_context_switch_count_ = 0;
}
};
template<thread_id_t thread_count>
class context_bound_scheduler
: public tree_search_scheduler<context_bound_scheduler<thread_count>
, context_bound_scheduler_thread_info<thread_count>, thread_count>
{
public:
typedef tree_search_scheduler<context_bound_scheduler<thread_count>
, context_bound_scheduler_thread_info<thread_count>, thread_count> base_t;
typedef typename base_t::thread_info_t thread_info_t;
typedef typename base_t::shared_context_t shared_context_t;
context_bound_scheduler(test_params& params, shared_context_t& ctx, thread_id_t dynamic_thread_count)
: base_t(params, ctx, dynamic_thread_count)
{
}
thread_id_t iteration_begin_impl()
{
switches_remain_ = this->params_.context_bound;
return base_t::iteration_begin_impl();
}
bool can_switch(thread_info_t& t)
{
t.sched_count_ += 1;
return switches_remain_ != 0;
}
void on_switch(thread_info_t& t)
{
if (t.state_ == thread_state_running)
{
RL_VERIFY(switches_remain_);
switches_remain_ -= 1;
}
else
{
t.forced_context_switch_count_ += 1;
}
}
double iteration_count_approx()
{
return 1.0;
/*
iteration_t const P = thread_count;
iteration_t const C0 = this->params_.context_bound;
iteration_t total = 1;//factorial(P);// * power(P, P * C0);
for (iteration_t i = 0; i != P - 1; ++i)
total *= power(i + 1, C0 + 1);
//if (C0)
// total *= power(P - 1, P - 1);
if (val(P) > 1)
{
for (iteration_t i = 0; i != P; ++i)
{
iteration_t const N = this->threads_[i].sched_count_;
iteration_t const C = C0 + this->threads_[i].forced_context_switch_count_;
//total *= (iteration_t)pow((double)(threads_[i].sched_count_ + 2) * (thread_count - 1), (int)(params_.context_bound + threads_[i].forced_context_switch_count_));
total *= factorial(N, C) / factorial(C);
//C$ += C + 1;
//total *= (int)(params_.context_bound + threads_[i].forced_context_switch_count_));
}
//total *= factorial(C$);
}
else
{
total = 1;
}
//iteration_t total = (iteration_t)pow((double)sched_count / thread_count + 1, (int)(params_.context_bound * thread_count + forced_context_switch_mean_ + 0.5));
//total *= thread_count;
//total *= (iteration_t)pow((double)thread_count - 1, thread_count);
for (size_t i = 0; i != this->stree_.size(); ++i)
{
if (this->stree_[i].type_ != sched_type_sched)
{
total *= this->stree_[i].count_;
}
}
return (double)total;
*/
}
private:
unsigned switches_remain_;
template<typename T>
static T factorial(T x, T i)
{
if (0 == i)
return 1;
T r = x;
for (--i; i; --i)
r *= x - i;
return r;
}
template<typename T>
static T factorial(T x)
{
if (0 == x)
return 1;
T r = x;
for (T i = x - 1; i; --i)
r *= i;
return r;
}
template<typename T>
static T power(T x, T y)
{
if (0 == y)
return 1;
T r = x;
for (T i = y - 1; i; --i)
r *= x;
return r;
}
RL_NOCOPY(context_bound_scheduler);
};
}
#endif

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