Thread System 0.3.1
High-performance C++20 thread pool with work stealing and DAG scheduling
Loading...
Searching...
No Matches
kcenon::thread::detail::hazard_pointer_registry Class Reference

Global hazard pointer registry Manages all thread-local hazard lists. More...

#include <hazard_pointer.h>

Collaboration diagram for kcenon::thread::detail::hazard_pointer_registry:
Collaboration graph

Public Member Functions

thread_hazard_listget_thread_list ()
 Get or create thread-local hazard list.
 
void mark_inactive ()
 Mark current thread's list as inactive.
 
std::vector< void * > scan_hazard_pointers ()
 Scan all hazard pointers and collect protected pointers.
 
size_t get_active_thread_count () const
 Get total number of active threads.
 

Static Public Member Functions

static hazard_pointer_registryinstance ()
 

Private Member Functions

 hazard_pointer_registry ()=default
 

Private Attributes

std::atomic< thread_hazard_list * > head_ {nullptr}
 
std::atomic< size_t > thread_count_ {0}
 

Detailed Description

Global hazard pointer registry Manages all thread-local hazard lists.

Definition at line 58 of file hazard_pointer.h.

Constructor & Destructor Documentation

◆ hazard_pointer_registry()

kcenon::thread::detail::hazard_pointer_registry::hazard_pointer_registry ( )
privatedefault

Member Function Documentation

◆ get_active_thread_count()

size_t kcenon::thread::detail::hazard_pointer_registry::get_active_thread_count ( ) const

Get total number of active threads.

Definition at line 143 of file hazard_pointer.cpp.

143 {
144 return thread_count_.load(std::memory_order_relaxed);
145}

References thread_count_.

◆ get_thread_list()

thread_hazard_list * kcenon::thread::detail::hazard_pointer_registry::get_thread_list ( )

Get or create thread-local hazard list.

Definition at line 22 of file hazard_pointer.cpp.

22 {
23 // Thread-local storage for this thread's hazard list
24 static thread_local thread_hazard_list* thread_list = nullptr;
25
26 if (thread_list == nullptr) {
27 // Try to reuse an inactive list first to prevent unbounded memory growth
28 thread_hazard_list* curr = head_.load(std::memory_order_acquire);
29 while (curr) {
30 bool expected = false;
31 // Try to claim an inactive list
32 // Use acq_rel: acquire to see prior hazard pointer clears,
33 // release to publish active=true to scanning threads
34 if (curr->active.compare_exchange_strong(expected, true, std::memory_order_acq_rel,
35 std::memory_order_relaxed)) {
36 thread_list = curr;
37 thread_count_.fetch_add(1, std::memory_order_relaxed);
38 break;
39 }
40 curr = curr->next;
41 }
42
43 // If no inactive list found, allocate a new one
44 if (thread_list == nullptr) {
45 thread_list = new thread_hazard_list();
46
47 // Add to global linked list
48 // Use acq_rel on CAS: acquire to read current list, release to
49 // publish the new node so that scanning threads see it
50 thread_hazard_list* old_head = head_.load(std::memory_order_acquire);
51 do {
52 thread_list->next = old_head;
53 } while (!head_.compare_exchange_weak(old_head, thread_list, std::memory_order_acq_rel,
54 std::memory_order_relaxed));
55
56 thread_count_.fetch_add(1, std::memory_order_relaxed);
57 }
58
59 // Register thread cleanup
60 static thread_local struct thread_cleanup {
61 thread_hazard_list* list;
62
63 ~thread_cleanup() {
64 if (list) {
65 hazard_pointer_registry::instance().mark_inactive();
66 }
67 }
68 } cleanup{thread_list};
69 }
70
71 return thread_list;
72}
std::atomic< thread_hazard_list * > head_

References kcenon::thread::detail::thread_hazard_list::active, head_, kcenon::thread::detail::thread_hazard_list::next, and thread_count_.

Referenced by kcenon::thread::hazard_pointer::hazard_pointer(), and mark_inactive().

Here is the caller graph for this function:

◆ instance()

hazard_pointer_registry & kcenon::thread::detail::hazard_pointer_registry::instance ( )
static

◆ mark_inactive()

void kcenon::thread::detail::hazard_pointer_registry::mark_inactive ( )

Mark current thread's list as inactive.

Definition at line 75 of file hazard_pointer.cpp.

75 {
76 static thread_local thread_hazard_list* thread_list = get_thread_list();
77
78 if (thread_list) {
79 // Clear all hazard pointers
80 for (auto& h : thread_list->hazards) {
81 h.store(nullptr, std::memory_order_release);
82 }
83
84 // Mark as inactive (using release to ensure visibility)
85 thread_list->active.store(false, std::memory_order_release);
86 thread_count_.fetch_sub(1, std::memory_order_relaxed);
87 }
88}
thread_hazard_list * get_thread_list()
Get or create thread-local hazard list.

References kcenon::thread::detail::thread_hazard_list::active, get_thread_list(), kcenon::thread::detail::thread_hazard_list::hazards, and thread_count_.

Here is the call graph for this function:

◆ scan_hazard_pointers()

std::vector< void * > kcenon::thread::detail::hazard_pointer_registry::scan_hazard_pointers ( )

Scan all hazard pointers and collect protected pointers.

Definition at line 91 of file hazard_pointer.cpp.

91 {
92 std::vector<void*> protected_ptrs;
93 protected_ptrs.reserve(256); // Pre-allocate reasonable size
94
95 // Marker value for owned but not protecting slots
96 const void* SLOT_OWNED_MARKER = reinterpret_cast<void*>(0x1);
97
98 // Periodically clean up inactive thread lists
99 // Use scan counter to avoid overhead on every scan
100 static thread_local size_t scan_counter = 0;
101 static constexpr size_t CLEANUP_INTERVAL = 100; // Clean every 100 scans
102 bool should_cleanup = (++scan_counter % CLEANUP_INTERVAL == 0);
103
104 // Traverse all thread lists
105 thread_hazard_list* curr = head_.load(std::memory_order_acquire);
106 size_t inactive_count = 0;
107
108 while (curr) {
109 // IMPORTANT: Scan ALL records regardless of active status.
110 // A thread may be in the process of setting a hazard pointer before
111 // setting active=true (race window in get_thread_list). If we skip
112 // inactive records, we might miss a valid hazard pointer and
113 // prematurely reclaim a protected node.
114 // This matches the approach used in safe_hazard_pointer.h.
115 for (auto& hazard : curr->hazards) {
116 void* ptr = hazard.load(std::memory_order_acquire);
117 // Only add if it's a real pointer (not nullptr or SLOT_OWNED_MARKER)
118 if (ptr != nullptr && ptr != SLOT_OWNED_MARKER) {
119 protected_ptrs.push_back(ptr);
120 }
121 }
122
123 if (!curr->active.load(std::memory_order_acquire)) {
124 ++inactive_count;
125 }
126
127 curr = curr->next;
128 }
129
130 // Sort for efficient binary search in scan_and_reclaim
131 // O(N log N) but enables O(log N) lookups later
132 std::sort(protected_ptrs.begin(), protected_ptrs.end());
133
134 // Remove duplicates to minimize search space
135 // Multiple threads may protect the same pointer
136 protected_ptrs.erase(std::unique(protected_ptrs.begin(), protected_ptrs.end()),
137 protected_ptrs.end());
138
139 return protected_ptrs;
140}

References kcenon::thread::detail::thread_hazard_list::active, kcenon::thread::detail::thread_hazard_list::hazards, head_, and kcenon::thread::detail::thread_hazard_list::next.

Member Data Documentation

◆ head_

std::atomic<thread_hazard_list*> kcenon::thread::detail::hazard_pointer_registry::head_ {nullptr}
private

Definition at line 77 of file hazard_pointer.h.

77{nullptr};

Referenced by get_thread_list(), and scan_hazard_pointers().

◆ thread_count_

std::atomic<size_t> kcenon::thread::detail::hazard_pointer_registry::thread_count_ {0}
private

Definition at line 78 of file hazard_pointer.h.

78{0};

Referenced by get_active_thread_count(), get_thread_list(), and mark_inactive().


The documentation for this class was generated from the following files: