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

Global Hazard Pointer Domain Manager. More...

#include <safe_hazard_pointer.h>

Collaboration diagram for kcenon::thread::safe_hazard_pointer_domain:
Collaboration graph

Public Types

using retire_callback = std::function<void(void*)>
 

Public Member Functions

safe_hazard_pointer_recordacquire ()
 Acquire a hazard pointer record for current thread.
 
void release (safe_hazard_pointer_record *record) noexcept
 Release a hazard pointer record.
 
void retire (void *p, retire_callback deleter)
 Retire a pointer for later reclamation.
 
void collect ()
 Collect reclaimable objects.
 
size_t retired_count () const noexcept
 Get current retired count.
 
size_t active_count () const noexcept
 Get active thread count.
 

Static Public Member Functions

static safe_hazard_pointer_domaininstance ()
 Get singleton instance.
 

Private Member Functions

 safe_hazard_pointer_domain ()=default
 
 ~safe_hazard_pointer_domain ()
 
 safe_hazard_pointer_domain (const safe_hazard_pointer_domain &)=delete
 
safe_hazard_pointer_domainoperator= (const safe_hazard_pointer_domain &)=delete
 
void collect_internal ()
 Collect reclaimable objects (internal, lock held)
 
size_t get_adaptive_threshold () const noexcept
 Get adaptive threshold based on active thread count.
 

Private Attributes

std::atomic< safe_hazard_pointer_record * > head_ {nullptr}
 
std::atomic< size_t > active_count_ {0}
 
std::atomic< size_t > retired_count_ {0}
 
std::mutex retire_mutex_
 
std::vector< std::pair< void *, retire_callback > > retired_list_
 

Detailed Description

Global Hazard Pointer Domain Manager.

Manages all thread-local hazard pointer records centrally. Provides safe memory reclamation with explicit memory ordering guarantees.

Thread-safe and lock-free for acquire/release operations.

Definition at line 114 of file safe_hazard_pointer.h.

Member Typedef Documentation

◆ retire_callback

Constructor & Destructor Documentation

◆ safe_hazard_pointer_domain() [1/2]

kcenon::thread::safe_hazard_pointer_domain::safe_hazard_pointer_domain ( )
privatedefault

◆ ~safe_hazard_pointer_domain()

kcenon::thread::safe_hazard_pointer_domain::~safe_hazard_pointer_domain ( )
inlineprivate
Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 257 of file safe_hazard_pointer.h.

257 {
258 // Delete all records
259 auto* p = head_.load(std::memory_order_relaxed);
260 while (p != nullptr) {
261 auto* next = p->next.load(std::memory_order_relaxed);
262 delete p;
263 p = next;
264 }
265
266 // Force delete remaining retired objects
267 for (auto& [ptr, deleter] : retired_list_) {
268 if (deleter && ptr) {
269 deleter(ptr);
270 }
271 }
272 }
std::atomic< safe_hazard_pointer_record * > head_
std::vector< std::pair< void *, retire_callback > > retired_list_

References head_, and retired_list_.

◆ safe_hazard_pointer_domain() [2/2]

kcenon::thread::safe_hazard_pointer_domain::safe_hazard_pointer_domain ( const safe_hazard_pointer_domain & )
privatedelete

Member Function Documentation

◆ acquire()

safe_hazard_pointer_record * kcenon::thread::safe_hazard_pointer_domain::acquire ( )
inline

Acquire a hazard pointer record for current thread.

Returns
Pointer to acquired record

Lock-free acquisition with proper memory ordering.

Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 132 of file safe_hazard_pointer.h.

132 {
133 // 1. Try to reuse an inactive record first
134 safe_hazard_pointer_record* p = head_.load(std::memory_order_acquire);
135 while (p != nullptr) {
136 bool expected = false;
137 if (p->active.compare_exchange_strong(
138 expected, true,
139 std::memory_order_acq_rel,
140 std::memory_order_relaxed)) {
141 // Clear hazard pointers immediately after acquiring to avoid
142 // stale pointers from previous use affecting collect()
143 p->clear(0);
144 p->clear(1);
145 active_count_.fetch_add(1, std::memory_order_relaxed);
146 return p;
147 }
148 p = p->next.load(std::memory_order_acquire);
149 }
150
151 // 2. Create new record (constructor already clears hazard pointers)
152 auto* new_record = new safe_hazard_pointer_record();
153 new_record->active.store(true, std::memory_order_relaxed);
154
155 // 3. Add to list (lock-free)
156 safe_hazard_pointer_record* old_head;
157 do {
158 old_head = head_.load(std::memory_order_relaxed);
159 new_record->next.store(old_head, std::memory_order_relaxed);
160 } while (!head_.compare_exchange_weak(
161 old_head, new_record,
162 std::memory_order_release,
163 std::memory_order_relaxed));
164
165 active_count_.fetch_add(1, std::memory_order_relaxed);
166 return new_record;
167 }

References kcenon::thread::safe_hazard_pointer_record::active, active_count_, kcenon::thread::safe_hazard_pointer_record::clear(), head_, and kcenon::thread::safe_hazard_pointer_record::next.

Here is the call graph for this function:

◆ active_count()

size_t kcenon::thread::safe_hazard_pointer_domain::active_count ( ) const
inlinenodiscardnoexcept

Get active thread count.

Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 250 of file safe_hazard_pointer.h.

250 {
251 return active_count_.load(std::memory_order_relaxed);
252 }

References active_count_.

◆ collect()

void kcenon::thread::safe_hazard_pointer_domain::collect ( )
inline

Collect reclaimable objects.

Scans all hazard pointers and deletes objects that are not protected.

Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 235 of file safe_hazard_pointer.h.

235 {
236 std::lock_guard<std::mutex> lock(retire_mutex_);
238 }
void collect_internal()
Collect reclaimable objects (internal, lock held)

References collect_internal(), and retire_mutex_.

Referenced by kcenon::thread::typed_safe_hazard_domain< T >::collect(), and retire().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ collect_internal()

void kcenon::thread::safe_hazard_pointer_domain::collect_internal ( )
inlineprivate

Collect reclaimable objects (internal, lock held)

Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 281 of file safe_hazard_pointer.h.

281 {
282 if (retired_list_.empty()) {
283 return;
284 }
285
286 // Gather all currently protected pointers
287 // IMPORTANT: Check ALL records, not just active ones, to avoid race condition
288 // where a record is being reused while we're scanning.
289 // The hazard pointer value is set before active=true, so we must check
290 // the pointer value itself, not the active flag.
291 std::unordered_set<void*> hazards;
292 hazards.reserve(active_count_.load(std::memory_order_relaxed) *
294
295 safe_hazard_pointer_record* p = head_.load(std::memory_order_acquire);
296 while (p != nullptr) {
297 // Check all records regardless of active status to avoid race condition
298 for (size_t i = 0; i < safe_hazard_pointer_record::MAX_HAZARD_POINTERS; ++i) {
299 void* hp = p->get(i);
300 if (hp != nullptr) {
301 hazards.insert(hp);
302 }
303 }
304 p = p->next.load(std::memory_order_acquire);
305 }
306
307 // Delete objects not in hazard set
308 size_t reclaimed = 0;
309 auto it = retired_list_.begin();
310 while (it != retired_list_.end()) {
311 if (hazards.find(it->first) == hazards.end()) {
312 // Safe to delete
313 if (it->second && it->first) {
314 it->second(it->first);
315 }
316 it = retired_list_.erase(it);
317 ++reclaimed;
318 } else {
319 ++it;
320 }
321 }
322
323 retired_count_.fetch_sub(reclaimed, std::memory_order_relaxed);
324 }

References active_count_, kcenon::thread::safe_hazard_pointer_record::get(), head_, kcenon::thread::safe_hazard_pointer_record::MAX_HAZARD_POINTERS, kcenon::thread::safe_hazard_pointer_record::next, retired_count_, and retired_list_.

Referenced by collect().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_adaptive_threshold()

size_t kcenon::thread::safe_hazard_pointer_domain::get_adaptive_threshold ( ) const
inlinenodiscardprivatenoexcept

Get adaptive threshold based on active thread count.

Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 329 of file safe_hazard_pointer.h.

329 {
330 static constexpr size_t BASE_THRESHOLD = 64;
331 static constexpr size_t PER_THREAD_THRESHOLD = 16;
332 static constexpr size_t MAX_THRESHOLD = 512;
333
334 size_t active = active_count_.load(std::memory_order_relaxed);
335 return std::min(MAX_THRESHOLD, BASE_THRESHOLD + active * PER_THREAD_THRESHOLD);
336 }
@ active
Worker is actively processing jobs.

References kcenon::thread::active, and active_count_.

Referenced by retire().

Here is the caller graph for this function:

◆ instance()

◆ operator=()

◆ release()

void kcenon::thread::safe_hazard_pointer_domain::release ( safe_hazard_pointer_record * record)
inlinenoexcept

Release a hazard pointer record.

Parameters
recordRecord to release

Clears all hazard pointers and marks record as inactive.

Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 175 of file safe_hazard_pointer.h.

175 {
176 if (record == nullptr) {
177 return;
178 }
179
180 record->clear(0);
181 record->clear(1);
182 record->active.store(false, std::memory_order_release);
183 active_count_.fetch_sub(1, std::memory_order_relaxed);
184 }

References active_count_.

Referenced by kcenon::thread::safe_hazard_guard::operator=(), and kcenon::thread::safe_hazard_guard::~safe_hazard_guard().

Here is the caller graph for this function:

◆ retire()

void kcenon::thread::safe_hazard_pointer_domain::retire ( void * p,
retire_callback deleter )
inline

Retire a pointer for later reclamation.

Parameters
pPointer to retire
deleterDeletion callback

Thread-safe. Triggers collection when threshold is reached. Handles duplicate addresses by removing old entries (memory reuse scenario).

Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 194 of file safe_hazard_pointer.h.

194 {
195 if (p == nullptr) {
196 return;
197 }
198
199 bool should_collect = false;
200 {
201 std::lock_guard<std::mutex> lock(retire_mutex_);
202
203 // Remove any existing entry with the same address to handle memory reuse.
204 // This can happen when memory is freed and reallocated to the same address.
205 // In this case, the old entry's deleter must NOT be called since the memory
206 // is now occupied by a new valid object.
207 auto it = std::remove_if(retired_list_.begin(), retired_list_.end(),
208 [p](const auto& entry) { return entry.first == p; });
209 if (it != retired_list_.end()) {
210 size_t removed = std::distance(it, retired_list_.end());
211 retired_list_.erase(it, retired_list_.end());
212 retired_count_.fetch_sub(removed, std::memory_order_relaxed);
213 }
214
215 retired_list_.emplace_back(p, std::move(deleter));
216 retired_count_.fetch_add(1, std::memory_order_relaxed);
217
218 // Check threshold inside lock to avoid race
219 size_t threshold = get_adaptive_threshold();
220 should_collect = (retired_count_.load(std::memory_order_relaxed) >= threshold);
221 }
222
223 // Trigger collection after releasing lock to avoid deadlock
224 if (should_collect) {
225 collect();
226 }
227 }
void collect()
Collect reclaimable objects.
size_t get_adaptive_threshold() const noexcept
Get adaptive threshold based on active thread count.

References collect(), get_adaptive_threshold(), retire_mutex_, retired_count_, and retired_list_.

Referenced by kcenon::thread::detail::lockfree_job_queue::retire_node(), and kcenon::thread::safe_retire_hazard().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ retired_count()

size_t kcenon::thread::safe_hazard_pointer_domain::retired_count ( ) const
inlinenodiscardnoexcept

Get current retired count.

Examples
/home/runner/work/thread_system/thread_system/include/kcenon/thread/core/safe_hazard_pointer.h.

Definition at line 243 of file safe_hazard_pointer.h.

243 {
244 return retired_count_.load(std::memory_order_relaxed);
245 }

References retired_count_.

Member Data Documentation

◆ active_count_

std::atomic<size_t> kcenon::thread::safe_hazard_pointer_domain::active_count_ {0}
private

◆ head_

std::atomic<safe_hazard_pointer_record*> kcenon::thread::safe_hazard_pointer_domain::head_ {nullptr}
private

◆ retire_mutex_

std::mutex kcenon::thread::safe_hazard_pointer_domain::retire_mutex_
private

◆ retired_count_

std::atomic<size_t> kcenon::thread::safe_hazard_pointer_domain::retired_count_ {0}
private

◆ retired_list_

std::vector<std::pair<void*, retire_callback> > kcenon::thread::safe_hazard_pointer_domain::retired_list_
private

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