Add ReaderWriterQueue and update ConcurrentQueue

This commit is contained in:
Paul Hollinsky
2020-03-09 13:38:14 -04:00
parent 9ac3fd56bd
commit 42780dc610
1629 changed files with 306008 additions and 868 deletions
@@ -0,0 +1,511 @@
#include "stdafx.h"
#ifdef RL_TEST
#define ATOMIC(x) rl::atomic<x>
#define VAR(x) rl::var<x>
#define ATOMIC_FETCH_ADD(x, v) x($).fetch_add(v)
#define ATOMIC_COMPARE_EXCHANGE(x, c, v) x($).compare_exchange(c, v)
#define LOAD_ACQ(x) x($).load(rl::memory_order_acquire)
#define STORE_REL(x, v) x($).store(v, rl::memory_order_release)
#else
#define ATOMIC(x) x volatile
#define VAR(x) x
#define ATOMIC_FETCH_ADD(x, v) _InterlockedExchangeAdd((long*)&x, v)
#define ATOMIC_COMPARE_EXCHANGE(x, c, v) interlocked_compare_exchange(x, c, v)
#define LOAD_ACQ(x) x
#define STORE_REL(x, v) x = v
template<typename T>
bool interlocked_compare_exchange(T& x, T& c, T v)
{
T c0 = _InterlockedCompareExchange((long*)&x), v, c);
if (c0 == c)
{
return true;
}
else
{
c = c0;
return false;
}
}
#endif
//#include "pcx.h"
/*
template<typename T>
class mpmcq
{
public:
mpmcq()
{
STORE_REL(head_, alloc_block());
STORE_REL(tail_, LOAD_ACQ(head_));
}
void enqueue(T v)
{
for (;;)
{
block* b = LOAD_ACQ(head_);
unsigned raw = ATOMIC_FETCH_ADD(b->state_, state_head_inc);
unsigned idx = raw >> state_head_pos;
if (idx < item_count)
{
STORE_REL(b->data_[idx], v);
return;
}
unsigned last = raw & state_last_msk;
if (0 == last)
{
ATOMIC_COMPARE_EXCHANGE(head_, b, b+1);
}
else
{
block* b2 = LOAD_ACQ(b->next_);
if (b2)
{
ATOMIC_COMPARE_EXCHANGE(head_, b, b2);
}
else
{
b2 = alloc_block();
block* b3 = 0;
if (ATOMIC_COMPARE_EXCHANGE(b->next_, b3, b2))
{
ATOMIC_COMPARE_EXCHANGE(head_, b, b2);
}
else
{
for (;;)
{
b = LOAD_ACQ(head_);
while (0 == (LOAD_ACQ(b->state_) & state_last_msk))
b = b + 1;
while (LOAD_ACQ(b->next_))
b = LOAD_ACQ(b->next_) + block_count - 1;
b3 = 0;
if (ATOMIC_COMPARE_EXCHANGE(b->next_, b3, b2))
break;
}
}
}
}
}
}
T dequeue()
{
for (;;)
{
block* b = LOAD_ACQ(tail_);
unsigned cmp = LOAD_ACQ(b->state_);
unsigned tail = cmp & (state_last_msk - 1);
if (tail < item_count)
{
unsigned head = cmp >> state_head_pos;
if (tail < head)
{
unsigned xchg = cmp + state_tail_inc;
if (ATOMIC_COMPARE_EXCHANGE(b->state_, cmp, xchg))
{
for (;;)
{
T v = LOAD_ACQ(b->data_[tail]);
if (v != T())
return v;
rl::yield($);
}
}
}
else
{
return T();
}
}
else
{
unsigned last = cmp & state_last_msk;
if (0 == last)
{
ATOMIC_COMPARE_EXCHANGE(tail_, b, b+1);
}
else
{
block* b2 = LOAD_ACQ(b->next_);
if (0 == b2)
return T();
ATOMIC_COMPARE_EXCHANGE(tail_, b, b2);
}
}
}
}
private:
static unsigned const state_head_pos = 7;
static unsigned const state_head_inc = 1 << state_head_pos;
static unsigned const state_last_msk = 1 << 6;
static unsigned const state_tail_inc = 1 << 0;
static unsigned const item_count = 2;
static unsigned const block_count = 16;
struct block
{
//unsigned head_ : 24;
//unsigned last_ : 1;
//unsigned tail_ : 7;
ATOMIC(unsigned) state_;
ATOMIC(block*) next_;
ATOMIC(T) data_ [item_count];
};
struct superblock
{
block blocks_ [block_count];
};
char pad0_ [64];
ATOMIC(block*) head_;
char pad1_ [64];
ATOMIC(block*) tail_;
char pad2_ [64];
block* alloc_block()
{
superblock* sb = RL_NEW(superblock);
for (int x = 0; x != block_count; ++x)
{
block* b = &sb->blocks_[x];
STORE_REL(b->state_, 0);
STORE_REL(b->next_, 0);
for (int y = 0; y != item_count; ++y)
{
STORE_REL(b->data_[y], 0);
}
}
STORE_REL(sb->blocks_[block_count - 1].state_, 1 * state_head_inc + 1 * state_tail_inc + state_last_msk);
return &sb->blocks_[0];
}
};
struct test_mpmc : rl::test_suite<test_mpmc, 6>
{
mpmcq<int> q;
void thread(unsigned idx)
{
if (idx < thread_count / 2)
{
for (int i = 0; i != 2; ++i)
q.enqueue(1);
}
else
{
for (int i = 0; i != 2; ++i)
q.dequeue();
}
}
};
*/
struct thread_node
{
rl::var<thread_node*> next;
rl::var<size_t> count;
rl::var<size_t> unconsumed;
rl::HANDLE sema;
rl::CRITICAL_SECTION mtx;
};
void on_thread_exit(thread_node*& t_thread_node)
{
thread_node* head = t_thread_node;
thread_node* my = 0;
if (head)
{
rl::EnterCriticalSection(&head->mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
if (head->next($))
{
my = head->next($);
head->next($) = (thread_node*)my->next($);
}
else
{
my = head;
}
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&head->mtx, $);
while (my->unconsumed($))
{
rl::WaitForSingleObject(my->sema, rl::RL_INFINITE, $);
my->unconsumed($) -= 1;
}
rl::DeleteCriticalSection(&my->mtx, $);
rl::CloseHandle(my->sema, $);
RL_DELETE(my);
}
}
struct eventcount
{
eventcount()
{
root($) = 0;
rl::InitializeCriticalSection(&mtx, $);
}
~eventcount()
{
rl::DeleteCriticalSection(&mtx, $);
}
void prepare_wait(thread_node*& t_thread_node)
{
thread_node* my = 0;
thread_node* head = t_thread_node;
if (head)
{
rl::EnterCriticalSection(&head->mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
//RL_ASSERT(head->status == stat_root);
RL_ASSERT (root($) != head);
if (head->next($))
{
my = head->next($);
head->next($) = (thread_node*)my->next($);
my->next($) = 0;
//node_status st;
//if (stat_bucket != (st = (node_status)_InterlockedExchange(&my->status, stat_private)))
// __asm int 3;
RL_ASSERT (0 == my->count($));
}
else
{
my = head;
//node_status st;
//if (stat_root != (st = (node_status)_InterlockedExchange(&my->status, stat_private)))
// __asm int 3;
RL_ASSERT(0 == my->count($));
}
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&head->mtx, $);
}
else
{
my = RL_NEW thread_node;
my->next($) = 0;
my->count($) = 0;
my->unconsumed($) = 0;
my->sema = rl::CreateSemaphore(0, 0, LONG_MAX, 0, $);
//my->status = stat_private;
rl::InitializeCriticalSection(&my->mtx, $);
}
while (my->unconsumed($))
{
rl::WaitForSingleObject(my->sema, rl::RL_INFINITE, $);
my->unconsumed($) -= 1;
}
RL_ASSERT(0 == my->next($));
RL_ASSERT(0 == my->count($));
//if (my->status != stat_private) __asm int 3;
rl::EnterCriticalSection(&mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
RL_ASSERT(root($) != my);
if (root($))
{
my->next($) = (thread_node*)((thread_node*)root($))->next($);
((thread_node*)root($))->next($) = my;
//node_status st;
//if (stat_private != (st = (node_status)_InterlockedExchange(&my->status, stat_bucket)))
// __asm int 3;
my = root($);
}
else
{
root($) = my;
//node_status st;
//if (stat_private != (st = (node_status)_InterlockedExchange(&my->status, stat_root)))
// __asm int 3;
}
((thread_node*)root($))->count($) += 1;
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
t_thread_node = my;
}
void wait(thread_node*& t_thread_node)
{
thread_node* head = t_thread_node;
if (head == root($))
{
rl::WaitForSingleObject(head->sema, rl::RL_INFINITE, $);
}
else
{
rl::EnterCriticalSection(&head->mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
head->unconsumed($) += 1;
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&head->mtx, $);
}
}
void retire_wait(thread_node*& t_thread_node)
{
thread_node* head = t_thread_node;
if (head == root($))
{
rl::EnterCriticalSection(&mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
if (head == root($))
{
thread_node* my = 0;
head->count($) -= 1;
if (head->next($))
{
my = head->next($);
head->next($) = (thread_node*)my->next($);
my->next($) = 0;
}
else
{
my = head;
root($) = 0;
}
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
//my->status = stat_root;
t_thread_node = my;
return;
}
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
}
rl::EnterCriticalSection(&head->mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
head->unconsumed($) += 1;
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&head->mtx, $);
}
void signal_all()
{
//std::
//_mm_mfence();
thread_node* head = root($);
if (0 == head)
return;
rl::EnterCriticalSection(&mtx, $);
std::atomic_thread_fence($)(std::memory_order_seq_cst);
if (head != root($))
{
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
return;
}
size_t count = head->count($);
head->count($) = 0;
root($) = 0;
std::atomic_thread_fence($)(std::memory_order_seq_cst);
rl::LeaveCriticalSection(&mtx, $);
rl::ReleaseSemaphore(head->sema, count, 0, $);
}
std::atomic<thread_node*> root;
rl::CRITICAL_SECTION mtx;
};
struct test_ec : rl::test_suite<test_ec, 8>
{
std::atomic<int> x [2];
eventcount ec;
void before()
{
x[0]($) = 0;
x[1]($) = 0;
}
void thread(unsigned idx)
{
if (idx < 4)
{
for (int i = 0; i != 3; ++i)
{
x[idx % 2]($).fetch_add(1);
ec.signal_all();
}
}
else
{
thread_node* my = 0;
for (int i = 0; i != 3; ++i)
{
for (;;)
{
int cmp = x[idx % 2]($);
if (cmp > 0)
{
if (x[idx % 2]($).compare_exchange(cmp, cmp - 1))
break;
}
else
{
for (;;)
{
ec.prepare_wait(my);
cmp = x[idx % 2]($);
if (cmp > 0)
{
ec.retire_wait(my);
break;
}
ec.wait(my);
cmp = x[idx % 2]($);
if (cmp > 0)
{
break;
}
}
}
}
}
on_thread_exit(my);
}
}
};
int main()
{
rl::test_params p;
p.iteration_count = 20000000;
p.initial_state = "10000000";
rl::simulate<test_ec>(p);
}
@@ -0,0 +1,20 @@
Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mpmc", "mpmc.vcproj", "{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Debug|Win32.ActiveCfg = Debug|Win32
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Debug|Win32.Build.0 = Debug|Win32
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Release|Win32.ActiveCfg = Release|Win32
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -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>
@@ -0,0 +1,481 @@
#pragma once
#include <intrin.h>
#pragma intrinsic (_InterlockedExchangeAdd)
#pragma intrinsic (_InterlockedCompareExchange)
//#define PCX_DEBUG
#ifdef PCX_DEBUG
#include <sstream>
#include <windows.h>
#endif
namespace rl
{
size_t const cacheline_size = 64;
struct pcx_node
{
typedef void (*pcx_dtor_t)(pcx_node*);
ATOMIC(pcx_node*) pcx_next_;
ATOMIC(pcx_dtor_t) pcx_dtor_;
};
namespace pcx_int
{
unsigned const word_bits = 32;
unsigned const collector_bits = 4;
unsigned const collector_count = 1 << collector_bits;
unsigned const counter_inc = 1 << (collector_bits * 2);
unsigned const is_current_inc = 1;
unsigned const back_link_inc = 2;
struct master;
struct collector;
struct local_collector
{
pcx_node* defer_head_;
pcx_node defer_tail_;
unsigned defer_size_;
};
struct thread_int
{
pcx_int::master* master_;
pcx_int::collector* collectors_;
unsigned recursion_count_;
unsigned is_acquired_;
unsigned collector_index_;
unsigned last_seen_collector_index_;
unsigned flush_tail_;
pcx_node* defer_head_;
pcx_node defer_tail_;
unsigned defer_size_;
unsigned promote_;
local_collector local_collectors_ [collector_count];
};
}
class pcx_thread : private pcx_int::thread_int
{
public:
static pcx_thread& get();
void acquire();
void release();
void defer(pcx_node* node, pcx_node::pcx_dtor_t dtor);
void flush();
void promote();
void quiescent();
void init();
void deinit();
private:
unsigned acquire_impl();
void release_impl(unsigned, unsigned);
void flush_impl();
void local_flush();
void quiescent_impl();
friend void init();
friend void deinit();
friend void thread_callback(bool);
};
namespace pcx_int
{
struct master
{
char pad0_ [64];
unsigned garbage_threshold_;
char pad1_ [64];
struct state_part
{
unsigned current_collector_ : collector_bits;
unsigned collector_tail_ : collector_bits;
unsigned outer_counter_ : word_bits - 2 * collector_bits;
};
union state
{
long whole_;
state_part part_;
};
state state_;
char pad2_ [64];
state state_copy_;
char pad3_ [64];
};
struct collector
{
char pad0_ [64];
pcx_node* defer_list_head_;
unsigned defer_list_size_;
char pad1_ [64];
struct state_part
{
unsigned is_current_ : 1;
unsigned back_link_ : 1;
unsigned pad_ : collector_bits * 2 - 2;
unsigned inner_counter_ : word_bits - 2 * collector_bits;
};
union state
{
long whole_;
state_part part_;
};
state state_;
char pad2_ [64];
};
__declspec(selectany)
master g_master;
__declspec(selectany)
collector g_collectors [collector_count];
__declspec(selectany, thread)
thread_int* g_thread_instance;
typedef void (__stdcall nt_tls_cb_t)(void*, unsigned long, void*);
nt_tls_cb_t on_tls_callback;
#pragma data_seg(push, old_seg)
#pragma data_seg(".CRT$XLB")
__declspec(selectany, dllexport)
nt_tls_cb_t* volatile p_thread_callback = on_tls_callback;
#pragma data_seg(pop, old_seg)
inline void __stdcall on_tls_callback(void*, unsigned long reason, void*)
{
if (1 == reason)
{
init();
thread_callback(true);
}
else if (0 == reason)
{
thread_callback(false);
deinit();
}
if (2 == reason)
{
thread_callback(true);
}
else if (3 == reason)
{
thread_callback(false);
}
}
}
inline void init()
{
using namespace pcx_int;
master& m = g_master;
m.garbage_threshold_ = 128;
m.state_.part_.current_collector_ = 0;
m.state_.part_.collector_tail_ = 0;
m.state_.part_.outer_counter_ = 0;
m.state_copy_.part_.current_collector_ = 0;
m.state_copy_.part_.collector_tail_ = 0;
m.state_copy_.part_.outer_counter_ = 0;
for (unsigned i = 0; i != collector_count; ++i)
{
collector& c = g_collectors[i];
c.defer_list_head_ = 0;
c.defer_list_size_ = 0;
c.state_.part_.is_current_ = 1;
c.state_.part_.back_link_ = 1;
c.state_.part_.inner_counter_ = 0;
}
g_collectors[0].state_.part_.back_link_ = 0;
}
inline void deinit()
{
using namespace pcx_int;
pcx_thread::get().release_impl(g_master.state_.part_.current_collector_, is_current_inc);
}
inline void thread_callback(bool init)
{
if (init)
{
g_thread_instance = RL_NEW pcx_thread ();
pcx_thread::get().init();
}
else
{
pcx_thread::get().deinit();
RL_DELETE(g_thread_instance);
g_thread_instance = 0;
}
}
inline pcx_thread& pcx_thread::get()
{
return static_cast<pcx_thread&>(*pcx_int::g_thread_instance);
}
inline unsigned pcx_thread::acquire_impl()
{
using namespace pcx_int;
long const prev =
_InterlockedExchangeAdd(
&master_->state_.whole_, counter_inc);
master::state_part u = {prev};
#ifdef PCX_DEBUG
std::ostringstream ss;
ss << "[PCX] thread " << this << " acquire " << u.current_collector_ << "\n";
OutputDebugStringA(ss.str().c_str());
#endif
if (u.current_collector_ == flush_tail_
&& local_collectors_[flush_tail_].defer_size_)
{
local_flush();
}
return u.current_collector_;
}
inline void pcx_thread::release_impl(unsigned index, unsigned count)
{
using namespace pcx_int;
collector& c = collectors_[index];
unsigned const prev =
_InterlockedExchangeAdd(
&c.state_.whole_, (unsigned)-(int)count);
#ifdef PCX_DEBUG
std::ostringstream ss;
ss << "[PCX] thread " << this << " release " << index << "\n";
OutputDebugStringA(ss.str().c_str());
#endif
if (0 == prev - count)
{
pcx_node* curr = c.defer_list_head_;
while (curr)
{
pcx_node* next = curr->pcx_next_;
curr->pcx_dtor_(curr);
curr = next;
}
c.defer_list_head_ = 0;
c.defer_list_size_ = 0;
c.state_.part_.back_link_ = 1;
c.state_.part_.is_current_ = 1;
long u;
if (index != collector_count - 1)
u = collector_count;
else
u = -(long)(collector_count * (collector_count - 1));
_InterlockedExchangeAdd(&master_->state_.whole_, u);
release_impl((index + 1) % collector_count, back_link_inc);
}
}
inline void pcx_thread::flush_impl()
{
using namespace pcx_int;
_mm_mfence();
master::state state = master_->state_;
last_seen_collector_index_ = state.part_.current_collector_;
collector& gc = collectors_[state.part_.current_collector_];
local_collector& lc = local_collectors_[state.part_.current_collector_];
lc.defer_head_->pcx_next_ = defer_tail_.pcx_next_;
lc.defer_head_ = defer_tail_.pcx_next_;
lc.defer_size_ += defer_size_;
defer_head_ = &defer_tail_;
defer_tail_.pcx_next_ = 0;
defer_size_ = 0;
if (master_->garbage_threshold_ < lc.defer_size_ || promote_)
{
master::state cmp;
master::state val;
do
{
cmp = master_->state_;
if (cmp.part_.current_collector_ != last_seen_collector_index_)
{
promote_ = 0;
return;
}
unsigned next_index = (last_seen_collector_index_ + 1) % collector_count;
if (cmp.part_.collector_tail_ == next_index)
return;
val = cmp;
val.part_.current_collector_ += 1;
val.part_.outer_counter_ = 0;
}
while (cmp.whole_ != _InterlockedCompareExchange(
(long*)&master_->state_.whole_, val.whole_, cmp.whole_));
last_seen_collector_index_ = val.part_.current_collector_;
promote_ = 0;
_InterlockedIncrement((long*)&master_->state_copy_.whole_);
_InterlockedExchangeAdd((long*)&gc.state_.whole_,
cmp.part_.outer_counter_ * counter_inc - is_current_inc);
}
}
__declspec(noinline)
inline void pcx_thread::local_flush()
{
using namespace pcx_int;
if (flush_tail_ == master_->state_.part_.collector_tail_)
return;
#ifdef PCX_DEBUG
std::ostringstream ss;
ss << "[PCX] thread " << this << " flush " << flush_tail_ << "\n";
OutputDebugStringA(ss.str().c_str());
#endif
local_collector& lc = local_collectors_[flush_tail_];
pcx_node* curr = lc.defer_tail_.pcx_next_;
while (curr)
{
#ifdef PCX_DEBUG
std::ostringstream ss;
ss << "[PCX] thread " << this << " destroy " << curr << "\n";
OutputDebugStringA(ss.str().c_str());
#endif
pcx_node* next = curr->pcx_next_;
curr->pcx_dtor_(curr);
curr = next;
}
lc.defer_head_ = &lc.defer_tail_;
lc.defer_tail_.pcx_next_ = 0;
lc.defer_size_ = 0;
flush_tail_ = (flush_tail_ + 1) % collector_count;
}
__declspec(noinline)
inline void pcx_thread::quiescent_impl()
{
using namespace pcx_int;
if (defer_size_)
flush_impl();
release_impl(collector_index_, counter_inc);
collector_index_ = acquire_impl();
}
inline void pcx_thread::acquire()
{
using namespace pcx_int;
recursion_count_ += 1;
if (1 != recursion_count_)
return;
if (is_acquired_)
return;
collector_index_ = acquire_impl();
last_seen_collector_index_ = collector_index_;
is_acquired_ = 1;
}
inline void pcx_thread::release()
{
using namespace pcx_int;
recursion_count_ -= 1;
if (0 == recursion_count_)
{
if (master_->state_copy_.part_.current_collector_ != collector_index_
|| promote_)
{
if (defer_size_)
flush_impl();
release_impl(collector_index_, counter_inc);
is_acquired_ = 0;
}
}
if (flush_tail_ != last_seen_collector_index_)
{
local_flush();
}
}
inline void pcx_thread::quiescent()
{
if (master_->state_copy_.part_.current_collector_ != collector_index_
|| promote_)
{
quiescent_impl();
}
if (flush_tail_ != last_seen_collector_index_)
{
local_flush();
}
}
inline void pcx_thread::defer(pcx_node* node, pcx_node::pcx_dtor_t dtor)
{
using namespace pcx_int;
node->pcx_next_ = 0;
node->pcx_dtor_ = dtor;
defer_head_->pcx_next_ = node;
defer_head_ = node;
defer_size_ += 1;
}
inline void pcx_thread::flush()
{
using namespace pcx_int;
if (recursion_count_)
return;
if (0 == is_acquired_)
return;
if (defer_size_)
flush_impl();
release_impl(collector_index_, counter_inc);
is_acquired_ = 0;
}
inline void pcx_thread::promote()
{
promote_ = 1;
}
inline void pcx_thread::init()
{
using namespace pcx_int;
master_ = &g_master;
collectors_ = g_collectors;
defer_head_ = &defer_tail_;
defer_tail_.pcx_next_ = 0;
for (unsigned i = 0; i != collector_count; ++i)
{
local_collectors_[i].defer_head_ = &local_collectors_[i].defer_tail_;
}
}
inline void pcx_thread::deinit()
{
flush();
}
}
@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// ws_deque.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file
@@ -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"