| Deutsch English Français Italiano |
|
<vcadq3$340q3$1@dont-email.me> View for Bookmarking (what is this?) Look up another Usenet article |
Path: ...!2.eu.feeder.erje.net!feeder.erje.net!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail
From: "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Newsgroups: comp.lang.c++
Subject: Ported my proxy collector...
Date: Mon, 16 Sep 2024 16:08:51 -0700
Organization: A noiseless patient Spider
Lines: 431
Message-ID: <vcadq3$340q3$1@dont-email.me>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Tue, 17 Sep 2024 01:08:52 +0200 (CEST)
Injection-Info: dont-email.me; posting-host="2d41bb0e6a21724e79465d793e720c2a";
logging-data="3277635"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+baTLE9flW4Fn3jFzfT723iuC6/v7w56k="
User-Agent: Mozilla Thunderbird
Cancel-Lock: sha1:aJlqeESUdBmiAaTmDTVOwcPEj6c=
Content-Language: en-US
Bytes: 11033
This is a just quick port form Relacy to C++. The collector has the
ability to allow a lock-free stack, queues, ect... to be used without
any DWCAS on a pop operation. Also, it takes care of memory management
issues with the nodes...
Well here is my crude test code. Keep in mind that the
g_debug_alloc/free global's are only there for debugging purposes at
this early stage. They can be removed. This is akin to a poor mans RCU,
in a sense... Well, can you compile and run it when you get some really
free time to burn?
Thanks everybody. :^)
_________________________________________
#include <cassert>
#include <iostream>
#include <atomic>
#include <thread>
#define CT_READERS 10
#define CT_WRITERS 10
#define CT_THREADS (CT_WRITERS + CT_READERS)
#define CT_ITERS 10000000
#define CT_COLLECT 10240
// These are for debug and some statistics only...
static std::atomic<unsigned long> g_debug_alloc(0);
static std::atomic<unsigned long> g_debug_free(0);
// Chris M. Thomassons single target proxy collector:
// https://pastebin.com/raw/f71480694
namespace ct_proxy
{
struct node
{
std::atomic<node*> m_next;
node* m_defer_next;
node()
{
// debug only!
g_debug_alloc.fetch_add(1);
}
~node()
{
// debug only!
g_debug_free.fetch_add(1);
}
};
class proxy
{
static void prv_destroy(node* n)
{
while (n)
{
node* next = n->m_defer_next;
delete n;
n = next;
}
}
public:
class collector
{
friend class proxy;
private:
std::atomic<node*> m_defer;
std::atomic<unsigned int> m_count;
public:
collector()
: m_defer(nullptr),
m_count(0)
{
}
~collector()
{
prv_destroy(m_defer.load(std::memory_order_relaxed));
}
};
private:
std::atomic<unsigned int> m_current;
std::atomic<bool> m_quiesce;
node* m_defer;
collector m_collectors[2];
public:
proxy()
: m_current(0),
m_quiesce(false),
m_defer(nullptr)
{
}
~proxy()
{
prv_destroy(m_defer);
}
private:
void prv_quiesce_begin()
{
// Try to begin the quiescence process.
if (! m_quiesce.exchange(true, std::memory_order_acquire))
{
// advance the current collector and grab the old one.
unsigned int old =
m_current.load(std::memory_order_relaxed) & 0xFU;
old = m_current.exchange((old + 1) & 1,
std::memory_order_acq_rel);
collector& c = m_collectors[old & 0xFU];
// decode reference count.
unsigned int refs = old & 0xFFFFFFF0U;
// verify reference count and previous collector index.
assert(! (refs & 0x10U) && (old & 0xFU) == (&c -
m_collectors));
// increment and generate an odd reference count.
if (c.m_count.fetch_add(refs + 0x10U,
std::memory_order_release) == -(int)refs)
{
// odd reference count and drop-to-zero condition
detected!
prv_quiesce_complete(c);
}
}
}
void prv_quiesce_complete(collector& c)
{
// the collector `c' is now in a quiescent state! :^)
std::atomic_thread_fence(std::memory_order_acquire);
// maintain the back link and obtain "fresh" objects from
// this collection.
node* n = m_defer;
m_defer = c.m_defer.load(std::memory_order_relaxed);
c.m_defer.store(0, std::memory_order_relaxed);
// verify and reset the reference count.
assert(c.m_count.load(std::memory_order_relaxed) == 0x10U);
c.m_count.store(0, std::memory_order_relaxed);
// release the quiesce lock.
m_quiesce.store(false, std::memory_order_release);
// destroy nodes.
prv_destroy(n);
}
========== REMAINDER OF ARTICLE TRUNCATED ==========