C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Suggestion: non-static member variables for static-duration-only classes

From: Walt Karas <wkaras_at_[hidden]>
Date: Sun, 5 Oct 2025 17:06:25 +0000 (UTC)
Response: In your approach, a count for one counter is a count for all counters: https://gcc.godbolt.org/z/czovra5cd

On Sunday, October 5, 2025 at 08:26:16 AM EDT, Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]> wrote:

>
Actually just use the lockfree queue from Boost. I did this on my phone so I can't compile it, so maybe it doesn't compile successfully, but anyway:

#include <cassert>
#include <array>
#include <atomic>
#include <optional>
#include <set>
#include <stdexcept>
#include <thread>
#include <utility>
#include <vector>
#include <mutex>

#include <boost/lockfree/queue.hpp>

template<typename T, unsigned max_threads>
class ThreadLocal final {
static inline std::array<std::optional<T>, max_threads> storage{};
static inline std::atomic<unsigned> nextIndex{0u};
static inline boost::lockfree::queue<unsigned> freeList{max_threads};

static unsigned acquireSlot(void) noexcept(false)
{
unsigned index;
if ( freeList.pop(index) ) return index;

index = nextIndex.fetch_add(1u, std::memory_order_relaxed);
if ( index >= max_threads )
throw std::runtime_error("Exceeded max_threads limit!");
return index;
}

static void releaseSlot(unsigned const index) noexcept
{
storage[index].reset();
freeList.push(index);
}

template<typename... Params>
unsigned registerThread(Params&&... args) const noexcept(false)
{
static thread_local unsigned const myIndex = acquireSlot();

struct Reclaimer {
unsigned idx;
~Reclaimer(void) noexcept { releaseSlot(idx); }
};

static thread_local Reclaimer const reclaimer{myIndex};

if ( false == storage[myIndex].has_value() )
storage[myIndex].emplace( std::forward<Params>(args)... );
return myIndex;
}

public:
template<typename... Params>
T &operator()(Params&&... args) noexcept(false)
{
static thread_local unsigned const slot =
registerThread(std::forward<Params>(args)...);
return *storage[slot];
}

template<typename... Params>
T const &operator()(Params&&... args) const noexcept(false)
{
return const_cast<ThreadLocal*>(this)
->operator()( std::forward<Params>(args)... );
}
};

// ===================
// Example Usage
// ===================

class Counter {
std::atomic<unsigned> total{0u};
std::atomic<unsigned> num_threads{0u};
std::mutex mtx;

struct PerThread {
std::atomic<unsigned> count{0u};
Counter &parent;

explicit PerThread(Counter *const c) noexcept(false) : parent(*c)
{
std::lock_guard lock{parent.mtx};
parent.pt_active.insert(this);
parent.num_threads.fetch_add(1u, std::memory_order_relaxed);
}

~PerThread(void) noexcept // we want std::terminate called
{
{
std::lock_guard lock{parent.mtx};
parent.pt_active.erase(this);
}

if ( unsigned const leftover =
count.load(std::memory_order_relaxed) )
parent.total.fetch_add(leftover, std::memory_order_relaxed);

parent.num_threads.fetch_sub(1u, std::memory_order_relaxed);
}
};

std::set<PerThread*> pt_active;
ThreadLocal<PerThread, 32u> pt;

public:
void incr(void) noexcept
{
pt(this).count.fetch_add(1u, std::memory_order_relaxed);
}

unsigned collect(void) noexcept(false)
{
unsigned sum = 0u;

{
std::lock_guard lock{mtx};
for ( PerThread *const p : pt_active )
sum += p->count.exchange(0u, std::memory_order_relaxed);
}

if ( sum ) total.fetch_add(sum, std::memory_order_relaxed);
return sum;
}
};

#include <iostream>

auto main(void) -> int
{
Counter counter;

constexpr unsigned numThreads = 6u;
constexpr unsigned iterations = 200u;

constexpr auto work = [&](unsigned)
{
for ( unsigned i = 0; i < iterations; ++i ) counter.incr();
};

std::vector<std::thread> threads;
threads.reserve(numThreads);

for ( unsigned i = 0; i < numThreads; ++i ) threads.emplace_back(work, i);

for (auto &t : threads) t.join();

std::cout << "Collected total = " << counter.collect() << std::endl;
}



-- 
Std-Proposals mailing list
Std-Proposals_at_[hidden]
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2025-10-05 17:06:36