mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-06 18:08:37 +02:00
Add ReaderWriterQueue and update ConcurrentQueue
This commit is contained in:
+511
@@ -0,0 +1,511 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
|
||||
#ifdef RL_TEST
|
||||
#define ATOMIC(x) rl::atomic<x>
|
||||
#define VAR(x) rl::var<x>
|
||||
#define ATOMIC_FETCH_ADD(x, v) x($).fetch_add(v)
|
||||
#define ATOMIC_COMPARE_EXCHANGE(x, c, v) x($).compare_exchange(c, v)
|
||||
#define LOAD_ACQ(x) x($).load(rl::memory_order_acquire)
|
||||
#define STORE_REL(x, v) x($).store(v, rl::memory_order_release)
|
||||
#else
|
||||
#define ATOMIC(x) x volatile
|
||||
#define VAR(x) x
|
||||
#define ATOMIC_FETCH_ADD(x, v) _InterlockedExchangeAdd((long*)&x, v)
|
||||
#define ATOMIC_COMPARE_EXCHANGE(x, c, v) interlocked_compare_exchange(x, c, v)
|
||||
#define LOAD_ACQ(x) x
|
||||
#define STORE_REL(x, v) x = v
|
||||
|
||||
template<typename T>
|
||||
bool interlocked_compare_exchange(T& x, T& c, T v)
|
||||
{
|
||||
T c0 = _InterlockedCompareExchange((long*)&x), v, c);
|
||||
if (c0 == c)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = c0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//#include "pcx.h"
|
||||
|
||||
|
||||
/*
|
||||
template<typename T>
|
||||
class mpmcq
|
||||
{
|
||||
public:
|
||||
mpmcq()
|
||||
{
|
||||
STORE_REL(head_, alloc_block());
|
||||
STORE_REL(tail_, LOAD_ACQ(head_));
|
||||
}
|
||||
|
||||
void enqueue(T v)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
block* b = LOAD_ACQ(head_);
|
||||
unsigned raw = ATOMIC_FETCH_ADD(b->state_, state_head_inc);
|
||||
unsigned idx = raw >> state_head_pos;
|
||||
if (idx < item_count)
|
||||
{
|
||||
STORE_REL(b->data_[idx], v);
|
||||
return;
|
||||
}
|
||||
unsigned last = raw & state_last_msk;
|
||||
if (0 == last)
|
||||
{
|
||||
ATOMIC_COMPARE_EXCHANGE(head_, b, b+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
block* b2 = LOAD_ACQ(b->next_);
|
||||
if (b2)
|
||||
{
|
||||
ATOMIC_COMPARE_EXCHANGE(head_, b, b2);
|
||||
}
|
||||
else
|
||||
{
|
||||
b2 = alloc_block();
|
||||
block* b3 = 0;
|
||||
if (ATOMIC_COMPARE_EXCHANGE(b->next_, b3, b2))
|
||||
{
|
||||
ATOMIC_COMPARE_EXCHANGE(head_, b, b2);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
b = LOAD_ACQ(head_);
|
||||
while (0 == (LOAD_ACQ(b->state_) & state_last_msk))
|
||||
b = b + 1;
|
||||
while (LOAD_ACQ(b->next_))
|
||||
b = LOAD_ACQ(b->next_) + block_count - 1;
|
||||
b3 = 0;
|
||||
if (ATOMIC_COMPARE_EXCHANGE(b->next_, b3, b2))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T dequeue()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
block* b = LOAD_ACQ(tail_);
|
||||
unsigned cmp = LOAD_ACQ(b->state_);
|
||||
unsigned tail = cmp & (state_last_msk - 1);
|
||||
if (tail < item_count)
|
||||
{
|
||||
unsigned head = cmp >> state_head_pos;
|
||||
if (tail < head)
|
||||
{
|
||||
unsigned xchg = cmp + state_tail_inc;
|
||||
if (ATOMIC_COMPARE_EXCHANGE(b->state_, cmp, xchg))
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
T v = LOAD_ACQ(b->data_[tail]);
|
||||
if (v != T())
|
||||
return v;
|
||||
rl::yield($);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return T();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned last = cmp & state_last_msk;
|
||||
if (0 == last)
|
||||
{
|
||||
ATOMIC_COMPARE_EXCHANGE(tail_, b, b+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
block* b2 = LOAD_ACQ(b->next_);
|
||||
if (0 == b2)
|
||||
return T();
|
||||
ATOMIC_COMPARE_EXCHANGE(tail_, b, b2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static unsigned const state_head_pos = 7;
|
||||
static unsigned const state_head_inc = 1 << state_head_pos;
|
||||
static unsigned const state_last_msk = 1 << 6;
|
||||
static unsigned const state_tail_inc = 1 << 0;
|
||||
|
||||
static unsigned const item_count = 2;
|
||||
static unsigned const block_count = 16;
|
||||
|
||||
struct block
|
||||
{
|
||||
//unsigned head_ : 24;
|
||||
//unsigned last_ : 1;
|
||||
//unsigned tail_ : 7;
|
||||
ATOMIC(unsigned) state_;
|
||||
ATOMIC(block*) next_;
|
||||
ATOMIC(T) data_ [item_count];
|
||||
};
|
||||
|
||||
struct superblock
|
||||
{
|
||||
block blocks_ [block_count];
|
||||
};
|
||||
|
||||
char pad0_ [64];
|
||||
ATOMIC(block*) head_;
|
||||
char pad1_ [64];
|
||||
ATOMIC(block*) tail_;
|
||||
char pad2_ [64];
|
||||
|
||||
block* alloc_block()
|
||||
{
|
||||
superblock* sb = RL_NEW(superblock);
|
||||
for (int x = 0; x != block_count; ++x)
|
||||
{
|
||||
block* b = &sb->blocks_[x];
|
||||
STORE_REL(b->state_, 0);
|
||||
STORE_REL(b->next_, 0);
|
||||
for (int y = 0; y != item_count; ++y)
|
||||
{
|
||||
STORE_REL(b->data_[y], 0);
|
||||
}
|
||||
}
|
||||
STORE_REL(sb->blocks_[block_count - 1].state_, 1 * state_head_inc + 1 * state_tail_inc + state_last_msk);
|
||||
return &sb->blocks_[0];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct test_mpmc : rl::test_suite<test_mpmc, 6>
|
||||
{
|
||||
mpmcq<int> q;
|
||||
|
||||
void thread(unsigned idx)
|
||||
{
|
||||
if (idx < thread_count / 2)
|
||||
{
|
||||
for (int i = 0; i != 2; ++i)
|
||||
q.enqueue(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i != 2; ++i)
|
||||
q.dequeue();
|
||||
}
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
|
||||
struct thread_node
|
||||
{
|
||||
rl::var<thread_node*> next;
|
||||
rl::var<size_t> count;
|
||||
rl::var<size_t> unconsumed;
|
||||
rl::HANDLE sema;
|
||||
rl::CRITICAL_SECTION mtx;
|
||||
};
|
||||
|
||||
|
||||
void on_thread_exit(thread_node*& t_thread_node)
|
||||
{
|
||||
thread_node* head = t_thread_node;
|
||||
thread_node* my = 0;
|
||||
if (head)
|
||||
{
|
||||
rl::EnterCriticalSection(&head->mtx, $);
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
if (head->next($))
|
||||
{
|
||||
my = head->next($);
|
||||
head->next($) = (thread_node*)my->next($);
|
||||
}
|
||||
else
|
||||
{
|
||||
my = head;
|
||||
}
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&head->mtx, $);
|
||||
|
||||
while (my->unconsumed($))
|
||||
{
|
||||
rl::WaitForSingleObject(my->sema, rl::RL_INFINITE, $);
|
||||
my->unconsumed($) -= 1;
|
||||
}
|
||||
|
||||
rl::DeleteCriticalSection(&my->mtx, $);
|
||||
rl::CloseHandle(my->sema, $);
|
||||
RL_DELETE(my);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct eventcount
|
||||
{
|
||||
eventcount()
|
||||
{
|
||||
root($) = 0;
|
||||
rl::InitializeCriticalSection(&mtx, $);
|
||||
}
|
||||
|
||||
~eventcount()
|
||||
{
|
||||
rl::DeleteCriticalSection(&mtx, $);
|
||||
}
|
||||
|
||||
void prepare_wait(thread_node*& t_thread_node)
|
||||
{
|
||||
thread_node* my = 0;
|
||||
thread_node* head = t_thread_node;
|
||||
if (head)
|
||||
{
|
||||
rl::EnterCriticalSection(&head->mtx, $);
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
//RL_ASSERT(head->status == stat_root);
|
||||
RL_ASSERT (root($) != head);
|
||||
if (head->next($))
|
||||
{
|
||||
my = head->next($);
|
||||
head->next($) = (thread_node*)my->next($);
|
||||
my->next($) = 0;
|
||||
|
||||
//node_status st;
|
||||
//if (stat_bucket != (st = (node_status)_InterlockedExchange(&my->status, stat_private)))
|
||||
// __asm int 3;
|
||||
RL_ASSERT (0 == my->count($));
|
||||
}
|
||||
else
|
||||
{
|
||||
my = head;
|
||||
|
||||
//node_status st;
|
||||
//if (stat_root != (st = (node_status)_InterlockedExchange(&my->status, stat_private)))
|
||||
// __asm int 3;
|
||||
RL_ASSERT(0 == my->count($));
|
||||
}
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&head->mtx, $);
|
||||
}
|
||||
else
|
||||
{
|
||||
my = RL_NEW thread_node;
|
||||
my->next($) = 0;
|
||||
my->count($) = 0;
|
||||
my->unconsumed($) = 0;
|
||||
my->sema = rl::CreateSemaphore(0, 0, LONG_MAX, 0, $);
|
||||
//my->status = stat_private;
|
||||
rl::InitializeCriticalSection(&my->mtx, $);
|
||||
}
|
||||
|
||||
while (my->unconsumed($))
|
||||
{
|
||||
rl::WaitForSingleObject(my->sema, rl::RL_INFINITE, $);
|
||||
my->unconsumed($) -= 1;
|
||||
}
|
||||
|
||||
RL_ASSERT(0 == my->next($));
|
||||
RL_ASSERT(0 == my->count($));
|
||||
//if (my->status != stat_private) __asm int 3;
|
||||
|
||||
rl::EnterCriticalSection(&mtx, $);
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
RL_ASSERT(root($) != my);
|
||||
if (root($))
|
||||
{
|
||||
my->next($) = (thread_node*)((thread_node*)root($))->next($);
|
||||
((thread_node*)root($))->next($) = my;
|
||||
|
||||
//node_status st;
|
||||
//if (stat_private != (st = (node_status)_InterlockedExchange(&my->status, stat_bucket)))
|
||||
// __asm int 3;
|
||||
|
||||
my = root($);
|
||||
}
|
||||
else
|
||||
{
|
||||
root($) = my;
|
||||
|
||||
//node_status st;
|
||||
//if (stat_private != (st = (node_status)_InterlockedExchange(&my->status, stat_root)))
|
||||
// __asm int 3;
|
||||
}
|
||||
((thread_node*)root($))->count($) += 1;
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&mtx, $);
|
||||
t_thread_node = my;
|
||||
}
|
||||
|
||||
void wait(thread_node*& t_thread_node)
|
||||
{
|
||||
thread_node* head = t_thread_node;
|
||||
if (head == root($))
|
||||
{
|
||||
rl::WaitForSingleObject(head->sema, rl::RL_INFINITE, $);
|
||||
}
|
||||
else
|
||||
{
|
||||
rl::EnterCriticalSection(&head->mtx, $);
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
head->unconsumed($) += 1;
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&head->mtx, $);
|
||||
}
|
||||
}
|
||||
|
||||
void retire_wait(thread_node*& t_thread_node)
|
||||
{
|
||||
thread_node* head = t_thread_node;
|
||||
if (head == root($))
|
||||
{
|
||||
rl::EnterCriticalSection(&mtx, $);
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
if (head == root($))
|
||||
{
|
||||
thread_node* my = 0;
|
||||
head->count($) -= 1;
|
||||
if (head->next($))
|
||||
{
|
||||
my = head->next($);
|
||||
head->next($) = (thread_node*)my->next($);
|
||||
my->next($) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
my = head;
|
||||
root($) = 0;
|
||||
}
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&mtx, $);
|
||||
//my->status = stat_root;
|
||||
t_thread_node = my;
|
||||
return;
|
||||
}
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&mtx, $);
|
||||
}
|
||||
rl::EnterCriticalSection(&head->mtx, $);
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
head->unconsumed($) += 1;
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&head->mtx, $);
|
||||
}
|
||||
|
||||
void signal_all()
|
||||
{
|
||||
//std::
|
||||
//_mm_mfence();
|
||||
thread_node* head = root($);
|
||||
if (0 == head)
|
||||
return;
|
||||
rl::EnterCriticalSection(&mtx, $);
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
if (head != root($))
|
||||
{
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&mtx, $);
|
||||
return;
|
||||
}
|
||||
size_t count = head->count($);
|
||||
head->count($) = 0;
|
||||
root($) = 0;
|
||||
std::atomic_thread_fence($)(std::memory_order_seq_cst);
|
||||
rl::LeaveCriticalSection(&mtx, $);
|
||||
rl::ReleaseSemaphore(head->sema, count, 0, $);
|
||||
}
|
||||
|
||||
std::atomic<thread_node*> root;
|
||||
rl::CRITICAL_SECTION mtx;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct test_ec : rl::test_suite<test_ec, 8>
|
||||
{
|
||||
std::atomic<int> x [2];
|
||||
eventcount ec;
|
||||
|
||||
void before()
|
||||
{
|
||||
x[0]($) = 0;
|
||||
x[1]($) = 0;
|
||||
}
|
||||
|
||||
void thread(unsigned idx)
|
||||
{
|
||||
if (idx < 4)
|
||||
{
|
||||
for (int i = 0; i != 3; ++i)
|
||||
{
|
||||
x[idx % 2]($).fetch_add(1);
|
||||
ec.signal_all();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
thread_node* my = 0;
|
||||
for (int i = 0; i != 3; ++i)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
int cmp = x[idx % 2]($);
|
||||
if (cmp > 0)
|
||||
{
|
||||
if (x[idx % 2]($).compare_exchange(cmp, cmp - 1))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
ec.prepare_wait(my);
|
||||
cmp = x[idx % 2]($);
|
||||
if (cmp > 0)
|
||||
{
|
||||
ec.retire_wait(my);
|
||||
break;
|
||||
}
|
||||
ec.wait(my);
|
||||
cmp = x[idx % 2]($);
|
||||
if (cmp > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
on_thread_exit(my);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
rl::test_params p;
|
||||
p.iteration_count = 20000000;
|
||||
p.initial_state = "10000000";
|
||||
rl::simulate<test_ec>(p);
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mpmc", "mpmc.vcproj", "{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Vendored
+207
@@ -0,0 +1,207 @@
|
||||
<?xml version="1.0" encoding="windows-1251"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8,00"
|
||||
Name="mpmc"
|
||||
ProjectGUID="{ECB64178-A35E-4EB2-9EB0-BD72D6F7B6E4}"
|
||||
RootNamespace="mpmc"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="2"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<File
|
||||
RelativePath="..\mpmc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\pcx.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\stdafx.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\stdafx.h"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
+481
@@ -0,0 +1,481 @@
|
||||
#pragma once
|
||||
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic (_InterlockedExchangeAdd)
|
||||
#pragma intrinsic (_InterlockedCompareExchange)
|
||||
|
||||
//#define PCX_DEBUG
|
||||
|
||||
#ifdef PCX_DEBUG
|
||||
#include <sstream>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace rl
|
||||
{
|
||||
|
||||
size_t const cacheline_size = 64;
|
||||
|
||||
struct pcx_node
|
||||
{
|
||||
typedef void (*pcx_dtor_t)(pcx_node*);
|
||||
ATOMIC(pcx_node*) pcx_next_;
|
||||
ATOMIC(pcx_dtor_t) pcx_dtor_;
|
||||
};
|
||||
|
||||
namespace pcx_int
|
||||
{
|
||||
unsigned const word_bits = 32;
|
||||
unsigned const collector_bits = 4;
|
||||
unsigned const collector_count = 1 << collector_bits;
|
||||
unsigned const counter_inc = 1 << (collector_bits * 2);
|
||||
unsigned const is_current_inc = 1;
|
||||
unsigned const back_link_inc = 2;
|
||||
|
||||
struct master;
|
||||
struct collector;
|
||||
|
||||
struct local_collector
|
||||
{
|
||||
pcx_node* defer_head_;
|
||||
pcx_node defer_tail_;
|
||||
unsigned defer_size_;
|
||||
};
|
||||
|
||||
struct thread_int
|
||||
{
|
||||
pcx_int::master* master_;
|
||||
pcx_int::collector* collectors_;
|
||||
unsigned recursion_count_;
|
||||
unsigned is_acquired_;
|
||||
unsigned collector_index_;
|
||||
unsigned last_seen_collector_index_;
|
||||
unsigned flush_tail_;
|
||||
pcx_node* defer_head_;
|
||||
pcx_node defer_tail_;
|
||||
unsigned defer_size_;
|
||||
unsigned promote_;
|
||||
local_collector local_collectors_ [collector_count];
|
||||
};
|
||||
}
|
||||
|
||||
class pcx_thread : private pcx_int::thread_int
|
||||
{
|
||||
public:
|
||||
static pcx_thread& get();
|
||||
|
||||
void acquire();
|
||||
void release();
|
||||
void defer(pcx_node* node, pcx_node::pcx_dtor_t dtor);
|
||||
void flush();
|
||||
void promote();
|
||||
void quiescent();
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
private:
|
||||
unsigned acquire_impl();
|
||||
void release_impl(unsigned, unsigned);
|
||||
void flush_impl();
|
||||
void local_flush();
|
||||
void quiescent_impl();
|
||||
friend void init();
|
||||
friend void deinit();
|
||||
friend void thread_callback(bool);
|
||||
};
|
||||
|
||||
namespace pcx_int
|
||||
{
|
||||
struct master
|
||||
{
|
||||
char pad0_ [64];
|
||||
|
||||
unsigned garbage_threshold_;
|
||||
|
||||
char pad1_ [64];
|
||||
|
||||
struct state_part
|
||||
{
|
||||
unsigned current_collector_ : collector_bits;
|
||||
unsigned collector_tail_ : collector_bits;
|
||||
unsigned outer_counter_ : word_bits - 2 * collector_bits;
|
||||
};
|
||||
|
||||
union state
|
||||
{
|
||||
long whole_;
|
||||
state_part part_;
|
||||
};
|
||||
|
||||
state state_;
|
||||
|
||||
char pad2_ [64];
|
||||
|
||||
state state_copy_;
|
||||
|
||||
char pad3_ [64];
|
||||
};
|
||||
|
||||
struct collector
|
||||
{
|
||||
char pad0_ [64];
|
||||
|
||||
pcx_node* defer_list_head_;
|
||||
unsigned defer_list_size_;
|
||||
|
||||
char pad1_ [64];
|
||||
|
||||
struct state_part
|
||||
{
|
||||
unsigned is_current_ : 1;
|
||||
unsigned back_link_ : 1;
|
||||
unsigned pad_ : collector_bits * 2 - 2;
|
||||
unsigned inner_counter_ : word_bits - 2 * collector_bits;
|
||||
};
|
||||
|
||||
union state
|
||||
{
|
||||
long whole_;
|
||||
state_part part_;
|
||||
};
|
||||
|
||||
state state_;
|
||||
|
||||
char pad2_ [64];
|
||||
};
|
||||
|
||||
__declspec(selectany)
|
||||
master g_master;
|
||||
__declspec(selectany)
|
||||
collector g_collectors [collector_count];
|
||||
__declspec(selectany, thread)
|
||||
thread_int* g_thread_instance;
|
||||
|
||||
typedef void (__stdcall nt_tls_cb_t)(void*, unsigned long, void*);
|
||||
nt_tls_cb_t on_tls_callback;
|
||||
|
||||
#pragma data_seg(push, old_seg)
|
||||
#pragma data_seg(".CRT$XLB")
|
||||
__declspec(selectany, dllexport)
|
||||
nt_tls_cb_t* volatile p_thread_callback = on_tls_callback;
|
||||
#pragma data_seg(pop, old_seg)
|
||||
|
||||
inline void __stdcall on_tls_callback(void*, unsigned long reason, void*)
|
||||
{
|
||||
if (1 == reason)
|
||||
{
|
||||
init();
|
||||
thread_callback(true);
|
||||
}
|
||||
else if (0 == reason)
|
||||
{
|
||||
thread_callback(false);
|
||||
deinit();
|
||||
}
|
||||
if (2 == reason)
|
||||
{
|
||||
thread_callback(true);
|
||||
}
|
||||
else if (3 == reason)
|
||||
{
|
||||
thread_callback(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void init()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
master& m = g_master;
|
||||
m.garbage_threshold_ = 128;
|
||||
m.state_.part_.current_collector_ = 0;
|
||||
m.state_.part_.collector_tail_ = 0;
|
||||
m.state_.part_.outer_counter_ = 0;
|
||||
m.state_copy_.part_.current_collector_ = 0;
|
||||
m.state_copy_.part_.collector_tail_ = 0;
|
||||
m.state_copy_.part_.outer_counter_ = 0;
|
||||
for (unsigned i = 0; i != collector_count; ++i)
|
||||
{
|
||||
collector& c = g_collectors[i];
|
||||
c.defer_list_head_ = 0;
|
||||
c.defer_list_size_ = 0;
|
||||
c.state_.part_.is_current_ = 1;
|
||||
c.state_.part_.back_link_ = 1;
|
||||
c.state_.part_.inner_counter_ = 0;
|
||||
}
|
||||
g_collectors[0].state_.part_.back_link_ = 0;
|
||||
}
|
||||
|
||||
inline void deinit()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
pcx_thread::get().release_impl(g_master.state_.part_.current_collector_, is_current_inc);
|
||||
}
|
||||
|
||||
inline void thread_callback(bool init)
|
||||
{
|
||||
if (init)
|
||||
{
|
||||
g_thread_instance = RL_NEW pcx_thread ();
|
||||
pcx_thread::get().init();
|
||||
}
|
||||
else
|
||||
{
|
||||
pcx_thread::get().deinit();
|
||||
RL_DELETE(g_thread_instance);
|
||||
g_thread_instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline pcx_thread& pcx_thread::get()
|
||||
{
|
||||
return static_cast<pcx_thread&>(*pcx_int::g_thread_instance);
|
||||
}
|
||||
|
||||
inline unsigned pcx_thread::acquire_impl()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
long const prev =
|
||||
_InterlockedExchangeAdd(
|
||||
&master_->state_.whole_, counter_inc);
|
||||
master::state_part u = {prev};
|
||||
|
||||
#ifdef PCX_DEBUG
|
||||
std::ostringstream ss;
|
||||
ss << "[PCX] thread " << this << " acquire " << u.current_collector_ << "\n";
|
||||
OutputDebugStringA(ss.str().c_str());
|
||||
#endif
|
||||
|
||||
if (u.current_collector_ == flush_tail_
|
||||
&& local_collectors_[flush_tail_].defer_size_)
|
||||
{
|
||||
local_flush();
|
||||
}
|
||||
|
||||
return u.current_collector_;
|
||||
}
|
||||
|
||||
inline void pcx_thread::release_impl(unsigned index, unsigned count)
|
||||
{
|
||||
using namespace pcx_int;
|
||||
collector& c = collectors_[index];
|
||||
unsigned const prev =
|
||||
_InterlockedExchangeAdd(
|
||||
&c.state_.whole_, (unsigned)-(int)count);
|
||||
|
||||
#ifdef PCX_DEBUG
|
||||
std::ostringstream ss;
|
||||
ss << "[PCX] thread " << this << " release " << index << "\n";
|
||||
OutputDebugStringA(ss.str().c_str());
|
||||
#endif
|
||||
|
||||
if (0 == prev - count)
|
||||
{
|
||||
pcx_node* curr = c.defer_list_head_;
|
||||
while (curr)
|
||||
{
|
||||
pcx_node* next = curr->pcx_next_;
|
||||
curr->pcx_dtor_(curr);
|
||||
curr = next;
|
||||
}
|
||||
c.defer_list_head_ = 0;
|
||||
c.defer_list_size_ = 0;
|
||||
c.state_.part_.back_link_ = 1;
|
||||
c.state_.part_.is_current_ = 1;
|
||||
|
||||
long u;
|
||||
if (index != collector_count - 1)
|
||||
u = collector_count;
|
||||
else
|
||||
u = -(long)(collector_count * (collector_count - 1));
|
||||
_InterlockedExchangeAdd(&master_->state_.whole_, u);
|
||||
|
||||
release_impl((index + 1) % collector_count, back_link_inc);
|
||||
}
|
||||
}
|
||||
|
||||
inline void pcx_thread::flush_impl()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
_mm_mfence();
|
||||
master::state state = master_->state_;
|
||||
last_seen_collector_index_ = state.part_.current_collector_;
|
||||
collector& gc = collectors_[state.part_.current_collector_];
|
||||
local_collector& lc = local_collectors_[state.part_.current_collector_];
|
||||
lc.defer_head_->pcx_next_ = defer_tail_.pcx_next_;
|
||||
lc.defer_head_ = defer_tail_.pcx_next_;
|
||||
lc.defer_size_ += defer_size_;
|
||||
defer_head_ = &defer_tail_;
|
||||
defer_tail_.pcx_next_ = 0;
|
||||
defer_size_ = 0;
|
||||
if (master_->garbage_threshold_ < lc.defer_size_ || promote_)
|
||||
{
|
||||
master::state cmp;
|
||||
master::state val;
|
||||
do
|
||||
{
|
||||
cmp = master_->state_;
|
||||
if (cmp.part_.current_collector_ != last_seen_collector_index_)
|
||||
{
|
||||
promote_ = 0;
|
||||
return;
|
||||
}
|
||||
unsigned next_index = (last_seen_collector_index_ + 1) % collector_count;
|
||||
if (cmp.part_.collector_tail_ == next_index)
|
||||
return;
|
||||
val = cmp;
|
||||
val.part_.current_collector_ += 1;
|
||||
val.part_.outer_counter_ = 0;
|
||||
}
|
||||
while (cmp.whole_ != _InterlockedCompareExchange(
|
||||
(long*)&master_->state_.whole_, val.whole_, cmp.whole_));
|
||||
last_seen_collector_index_ = val.part_.current_collector_;
|
||||
promote_ = 0;
|
||||
_InterlockedIncrement((long*)&master_->state_copy_.whole_);
|
||||
_InterlockedExchangeAdd((long*)&gc.state_.whole_,
|
||||
cmp.part_.outer_counter_ * counter_inc - is_current_inc);
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(noinline)
|
||||
inline void pcx_thread::local_flush()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
if (flush_tail_ == master_->state_.part_.collector_tail_)
|
||||
return;
|
||||
|
||||
#ifdef PCX_DEBUG
|
||||
std::ostringstream ss;
|
||||
ss << "[PCX] thread " << this << " flush " << flush_tail_ << "\n";
|
||||
OutputDebugStringA(ss.str().c_str());
|
||||
#endif
|
||||
|
||||
local_collector& lc = local_collectors_[flush_tail_];
|
||||
pcx_node* curr = lc.defer_tail_.pcx_next_;
|
||||
while (curr)
|
||||
{
|
||||
#ifdef PCX_DEBUG
|
||||
std::ostringstream ss;
|
||||
ss << "[PCX] thread " << this << " destroy " << curr << "\n";
|
||||
OutputDebugStringA(ss.str().c_str());
|
||||
#endif
|
||||
|
||||
pcx_node* next = curr->pcx_next_;
|
||||
curr->pcx_dtor_(curr);
|
||||
curr = next;
|
||||
}
|
||||
lc.defer_head_ = &lc.defer_tail_;
|
||||
lc.defer_tail_.pcx_next_ = 0;
|
||||
lc.defer_size_ = 0;
|
||||
flush_tail_ = (flush_tail_ + 1) % collector_count;
|
||||
}
|
||||
|
||||
__declspec(noinline)
|
||||
inline void pcx_thread::quiescent_impl()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
if (defer_size_)
|
||||
flush_impl();
|
||||
release_impl(collector_index_, counter_inc);
|
||||
collector_index_ = acquire_impl();
|
||||
}
|
||||
|
||||
inline void pcx_thread::acquire()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
recursion_count_ += 1;
|
||||
if (1 != recursion_count_)
|
||||
return;
|
||||
if (is_acquired_)
|
||||
return;
|
||||
collector_index_ = acquire_impl();
|
||||
last_seen_collector_index_ = collector_index_;
|
||||
is_acquired_ = 1;
|
||||
}
|
||||
|
||||
inline void pcx_thread::release()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
recursion_count_ -= 1;
|
||||
if (0 == recursion_count_)
|
||||
{
|
||||
if (master_->state_copy_.part_.current_collector_ != collector_index_
|
||||
|| promote_)
|
||||
{
|
||||
if (defer_size_)
|
||||
flush_impl();
|
||||
release_impl(collector_index_, counter_inc);
|
||||
is_acquired_ = 0;
|
||||
}
|
||||
}
|
||||
if (flush_tail_ != last_seen_collector_index_)
|
||||
{
|
||||
local_flush();
|
||||
}
|
||||
}
|
||||
|
||||
inline void pcx_thread::quiescent()
|
||||
{
|
||||
if (master_->state_copy_.part_.current_collector_ != collector_index_
|
||||
|| promote_)
|
||||
{
|
||||
quiescent_impl();
|
||||
}
|
||||
if (flush_tail_ != last_seen_collector_index_)
|
||||
{
|
||||
local_flush();
|
||||
}
|
||||
}
|
||||
|
||||
inline void pcx_thread::defer(pcx_node* node, pcx_node::pcx_dtor_t dtor)
|
||||
{
|
||||
using namespace pcx_int;
|
||||
node->pcx_next_ = 0;
|
||||
node->pcx_dtor_ = dtor;
|
||||
defer_head_->pcx_next_ = node;
|
||||
defer_head_ = node;
|
||||
defer_size_ += 1;
|
||||
}
|
||||
|
||||
inline void pcx_thread::flush()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
if (recursion_count_)
|
||||
return;
|
||||
if (0 == is_acquired_)
|
||||
return;
|
||||
if (defer_size_)
|
||||
flush_impl();
|
||||
release_impl(collector_index_, counter_inc);
|
||||
is_acquired_ = 0;
|
||||
}
|
||||
|
||||
inline void pcx_thread::promote()
|
||||
{
|
||||
promote_ = 1;
|
||||
}
|
||||
|
||||
inline void pcx_thread::init()
|
||||
{
|
||||
using namespace pcx_int;
|
||||
master_ = &g_master;
|
||||
collectors_ = g_collectors;
|
||||
defer_head_ = &defer_tail_;
|
||||
defer_tail_.pcx_next_ = 0;
|
||||
for (unsigned i = 0; i != collector_count; ++i)
|
||||
{
|
||||
local_collectors_[i].defer_head_ = &local_collectors_[i].defer_tail_;
|
||||
}
|
||||
}
|
||||
|
||||
inline void pcx_thread::deinit()
|
||||
{
|
||||
flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// ws_deque.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#pragma warning (disable: 4201)
|
||||
|
||||
//#define RL_GC
|
||||
#define RL_MSVC_OUTPUT
|
||||
|
||||
#include "../../relacy/pch.hpp"
|
||||
#include "../../relacy/relacy_std.hpp"
|
||||
|
||||
Reference in New Issue
Block a user