Wildmeshing Toolkit
Loading...
Searching...
No Matches
enumerable_thread_specific.hpp
1#pragma once
2
3#include <algorithm>
4#include <atomic>
5#include <functional>
6#include <thread>
7#include <vector>
8
9namespace wmtk::threading {
10
11namespace detail {
12inline std::atomic<std::uint64_t>& ets_id_counter()
13{
14 static std::atomic<std::uint64_t> counter{1};
15 return counter;
16}
17} // namespace detail
18
19// ---------------------------------------------------------------------------
20// enumerable_thread_specific: replaces tbb::enumerable_thread_specific.
21// Only `.local()` (and construction with an optional initial value) is used.
22// Lock-free lookup: each thread owns a thread_local vector of slots.
23// ---------------------------------------------------------------------------
24template <typename T>
26{
27 struct Slot
28 {
29 std::uint64_t id;
30 std::unique_ptr<T> value;
31 };
32
33 static std::vector<Slot>& thread_slots()
34 {
35 static thread_local std::vector<Slot> slots;
36 return slots;
37 }
38
39 std::uint64_t m_id = detail::ets_id_counter().fetch_add(1, std::memory_order_relaxed);
40 std::function<T()> m_factory;
41
42public:
44 : m_factory([]() { return T(); })
45 {}
46
47 template <
48 typename U,
49 typename = std::enable_if_t<!std::is_same_v<std::decay_t<U>, enumerable_thread_specific>>>
50 explicit enumerable_thread_specific(U&& init)
51 : m_factory([captured = T(std::forward<U>(init))]() { return captured; })
52 {}
53
54 enumerable_thread_specific(const enumerable_thread_specific&) = delete;
55 enumerable_thread_specific& operator=(const enumerable_thread_specific&) = delete;
56 enumerable_thread_specific(enumerable_thread_specific&&) = delete;
57 enumerable_thread_specific& operator=(enumerable_thread_specific&&) = delete;
58
59 ~enumerable_thread_specific()
60 {
61 // Clear this (usually the main) thread's slot for this instance. Worker
62 // threads are ephemeral in this shim, so their slots die with them.
63 auto& slots = thread_slots();
64 slots.erase(
65 std::remove_if(
66 slots.begin(),
67 slots.end(),
68 [this](const Slot& s) { return s.id == m_id; }),
69 slots.end());
70 }
71
72 T& local()
73 {
74 auto& slots = thread_slots();
75 for (auto& s : slots) {
76 if (s.id == m_id) {
77 return *s.value;
78 }
79 }
80 // No slot for this thread yet, create one.
81 slots.push_back(Slot{m_id, std::make_unique<T>(m_factory())});
82 return *slots.back().value;
83 }
84};
85
86} // namespace wmtk::threading
Definition enumerable_thread_specific.hpp:26
Definition enumerable_thread_specific.hpp:28