#ifndef __CDS_CONTAINER_ELLEN_BINTREE_MAP_DHP_H
#define __CDS_CONTAINER_ELLEN_BINTREE_MAP_DHP_H
-#include <cds/gc/ptb.h>
+#include <cds/gc/dhp.h>
#include <cds/container/impl/ellen_bintree_map.h>
#endif // #ifndef __CDS_CONTAINER_ELLEN_BINTREE_MAP_DHP_H
#ifndef __CDS_CONTAINER_ELLEN_BINTREE_SET_DHP_H
#define __CDS_CONTAINER_ELLEN_BINTREE_SET_DHP_H
-#include <cds/gc/ptb.h>
+#include <cds/gc/dhp.h>
#include <cds/container/impl/ellen_bintree_set.h>
#endif // #ifndef __CDS_CONTAINER_ELLEN_BINTREE_SET_DHP_H
//$$CDS-header$$
-#ifndef __CDS_GC_DHP_H
-#define __CDS_GC_DHP_H
+#ifndef __CDS_GC_PTB_H
+#define __CDS_GC_PTB_H
-#include <cds/gc/ptb.h>
+#include <cds/gc/dhp_decl.h>
+#include <cds/gc/dhp_impl.h>
+#include <cds/details/lib.h>
-//@cond
-namespace cds { namespace gc {
- typedef PTB DHP;
-}} // namespace cds::gc
-//@endcond
-
-#endif // #ifndef __CDS_GC_DHP_H
+#endif // #ifndef __CDS_GC_PTB_H
--- /dev/null
+//$$CDS-header$$
+
+#ifndef __CDS_GC_PTB_DECL_H
+#define __CDS_GC_PTB_DECL_H
+
+#include <cds/gc/ptb/ptb.h>
+#include <cds/details/marked_ptr.h>
+#include <cds/details/static_functor.h>
+
+namespace cds { namespace gc {
+
+ /// Pass-the-Buck garbage collector
+ /** @ingroup cds_garbage_collector
+ @headerfile cds/gc/dhp.h
+ This class is a wrapper for Pass-the-Buck garbage collector internal implementation.
+
+ Sources:
+ - [2002] M. Herlihy, V. Luchangco, and M. Moir. The repeat offender problem: A mechanism for supporting
+ dynamic-sized lockfree data structures. Technical Report TR-2002-112, Sun Microsystems Laboratories, 2002
+ - [2002] M. Herlihy, V. Luchangco, P. Martin, and M. Moir. Dynamic-sized Lockfree Data Structures.
+ Technical Report TR-2002-110, Sun Microsystems Laboratories, 2002
+ - [2005] M. Herlihy, V. Luchangco, P. Martin, and M. Moir. Nonblocking Memory Management Support
+ for Dynamic_Sized Data Structures. ACM Transactions on Computer Systems, Vol.23, No.2, May 2005
+
+ See \ref cds_how_to_use "How to use" section for details of garbage collector applying.
+ */
+ class PTB
+ {
+ public:
+ /// Native guarded pointer type
+ typedef void * guarded_pointer;
+
+ /// Atomic reference
+ /**
+ @headerfile cds/gc/dhp.h
+ */
+ template <typename T> using atomic_ref = atomics::atomic<T *>;
+
+ /// Atomic type
+ /**
+ @headerfile cds/gc/dhp.h
+ */
+ template <typename T> using atomic_type = atomics::atomic<T>;
+
+ /// Atomic marked pointer
+ /**
+ @headerfile cds/gc/dhp.h
+ */
+ template <typename MarkedPtr> using atomic_marked_ptr = atomics::atomic<MarkedPtr>;
+
+ /// Thread GC implementation for internal usage
+ typedef ptb::ThreadGC thread_gc_impl;
+
+ /// Wrapper for ptb::ThreadGC class
+ /**
+ @headerfile cds/gc/dhp.h
+ This class performs automatically attaching/detaching Pass-the-Buck GC
+ for the current thread.
+ */
+ class thread_gc: public thread_gc_impl
+ {
+ //@cond
+ bool m_bPersistent;
+ //@endcond
+ public:
+ /// Constructor
+ /**
+ The constructor attaches the current thread to the Pass-the-Buck GC
+ if it is not yet attached.
+ The \p bPersistent parameter specifies attachment persistence:
+ - \p true - the class destructor will not detach the thread from Pass-the-Buck GC.
+ - \p false (default) - the class destructor will detach the thread from Pass-the-Buck GC.
+ */
+ thread_gc(
+ bool bPersistent = false
+ ) ; // inline in ptb_impl.h
+
+ /// Destructor
+ /**
+ If the object has been created in persistent mode, the destructor does nothing.
+ Otherwise it detaches the current thread from Pass-the-Buck GC.
+ */
+ ~thread_gc() ; // inline in ptb_impl.h
+ };
+
+
+ /// Pass-the-Buck guard
+ /**
+ @headerfile cds/gc/dhp.h
+ This class is a wrapper for ptb::Guard.
+ */
+ class Guard: public ptb::Guard
+ {
+ //@cond
+ typedef ptb::Guard base_class;
+ //@endcond
+
+ public:
+ //@cond
+ Guard() ; // inline in ptb_impl.h
+ //@endcond
+
+ /// Protects a pointer of type <tt> atomic<T*> </tt>
+ /**
+ Return the value of \p toGuard
+
+ The function tries to load \p toGuard and to store it
+ to the HP slot repeatedly until the guard's value equals \p toGuard
+ */
+ template <typename T>
+ T protect( atomics::atomic<T> const& toGuard )
+ {
+ T pCur = toGuard.load(atomics::memory_order_relaxed);
+ T pRet;
+ do {
+ pRet = assign( pCur );
+ pCur = toGuard.load(atomics::memory_order_acquire);
+ } while ( pRet != pCur );
+ return pCur;
+ }
+
+ /// Protects a converted pointer of type <tt> atomic<T*> </tt>
+ /**
+ Return the value of \p toGuard
+
+ The function tries to load \p toGuard and to store result of \p f functor
+ to the HP slot repeatedly until the guard's value equals \p toGuard.
+
+ The function is useful for intrusive containers when \p toGuard is a node pointer
+ that should be converted to a pointer to the value type before guarding.
+ The parameter \p f of type Func is a functor that makes this conversion:
+ \code
+ struct functor {
+ value_type * operator()( T * p );
+ };
+ \endcode
+ Really, the result of <tt> f( toGuard.load() ) </tt> is assigned to the hazard pointer.
+ */
+ template <typename T, class Func>
+ T protect( atomics::atomic<T> const& toGuard, Func f )
+ {
+ T pCur = toGuard.load(atomics::memory_order_relaxed);
+ T pRet;
+ do {
+ pRet = pCur;
+ assign( f( pCur ) );
+ pCur = toGuard.load(atomics::memory_order_acquire);
+ } while ( pRet != pCur );
+ return pCur;
+ }
+
+ /// Store \p p to the guard
+ /**
+ The function equals to a simple assignment, no loop is performed.
+ Can be used for a pointer that cannot be changed concurrently.
+ */
+ template <typename T>
+ T * assign( T * p )
+ {
+ return base_class::operator =(p);
+ }
+
+ //@cond
+ std::nullptr_t assign( std::nullptr_t )
+ {
+ return base_class::operator =(nullptr);
+ }
+ //@endcond
+
+ /// Store marked pointer \p p to the guard
+ /**
+ The function equals to a simple assignment of <tt>p.ptr()</tt>, no loop is performed.
+ Can be used for a marked pointer that cannot be changed concurrently.
+ */
+ template <typename T, int BITMASK>
+ T * assign( cds::details::marked_ptr<T, BITMASK> p )
+ {
+ return base_class::operator =( p.ptr() );
+ }
+
+ /// Copy from \p src guard to \p this guard
+ void copy( Guard const& src )
+ {
+ assign( src.get_native() );
+ }
+
+ /// Clear value of the guard
+ void clear()
+ {
+ base_class::clear();
+ }
+
+ /// Get the value currently protected (relaxed read)
+ template <typename T>
+ T * get() const
+ {
+ return reinterpret_cast<T *>( get_native() );
+ }
+
+ /// Get native guarded pointer stored
+ guarded_pointer get_native() const
+ {
+ return base_class::get_guard()->pPost.load(atomics::memory_order_relaxed);
+ }
+
+ };
+
+ /// Array of Pass-the-Buck guards
+ /**
+ @headerfile cds/gc/dhp.h
+ This class is a wrapper for ptb::GuardArray template.
+ Template parameter \p Count defines the size of PTB array.
+ */
+ template <size_t Count>
+ class GuardArray: public ptb::GuardArray<Count>
+ {
+ //@cond
+ typedef ptb::GuardArray<Count> base_class;
+ //@endcond
+ public:
+ /// Rebind array for other size \p COUNT2
+ template <size_t OtherCount>
+ struct rebind {
+ typedef GuardArray<OtherCount> other ; ///< rebinding result
+ };
+
+ public:
+ //@cond
+ GuardArray() ; // inline in ptb_impl.h
+ //@endcond
+
+ /// Protects a pointer of type \p atomic<T*>
+ /**
+ Return the value of \p toGuard
+
+ The function tries to load \p toGuard and to store it
+ to the slot \p nIndex repeatedly until the guard's value equals \p toGuard
+ */
+ template <typename T>
+ T protect(size_t nIndex, atomics::atomic<T> const& toGuard )
+ {
+ T pRet;
+ do {
+ pRet = assign( nIndex, toGuard.load(atomics::memory_order_relaxed) );
+ } while ( pRet != toGuard.load(atomics::memory_order_acquire));
+
+ return pRet;
+ }
+
+ /// Protects a pointer of type \p atomic<T*>
+ /**
+ Return the value of \p toGuard
+
+ The function tries to load \p toGuard and to store it
+ to the slot \p nIndex repeatedly until the guard's value equals \p toGuard
+
+ The function is useful for intrusive containers when \p toGuard is a node pointer
+ that should be converted to a pointer to the value type before guarding.
+ The parameter \p f of type Func is a functor that makes this conversion:
+ \code
+ struct functor {
+ value_type * operator()( T * p );
+ };
+ \endcode
+ Really, the result of <tt> f( toGuard.load() ) </tt> is assigned to the hazard pointer.
+ */
+ template <typename T, class Func>
+ T protect(size_t nIndex, atomics::atomic<T> const& toGuard, Func f )
+ {
+ T pRet;
+ do {
+ assign( nIndex, f( pRet = toGuard.load(atomics::memory_order_relaxed) ));
+ } while ( pRet != toGuard.load(atomics::memory_order_acquire));
+
+ return pRet;
+ }
+
+ /// Store \p to the slot \p nIndex
+ /**
+ The function equals to a simple assignment, no loop is performed.
+ */
+ template <typename T>
+ T * assign( size_t nIndex, T * p )
+ {
+ base_class::set(nIndex, p);
+ return p;
+ }
+
+ /// Store marked pointer \p p to the guard
+ /**
+ The function equals to a simple assignment of <tt>p.ptr()</tt>, no loop is performed.
+ Can be used for a marked pointer that cannot be changed concurrently.
+ */
+ template <typename T, int Bitmask>
+ T * assign( size_t nIndex, cds::details::marked_ptr<T, Bitmask> p )
+ {
+ return assign( nIndex, p.ptr() );
+ }
+
+ /// Copy guarded value from \p src guard to slot at index \p nIndex
+ void copy( size_t nIndex, Guard const& src )
+ {
+ assign( nIndex, src.get_native() );
+ }
+
+ /// Copy guarded value from slot \p nSrcIndex to slot at index \p nDestIndex
+ void copy( size_t nDestIndex, size_t nSrcIndex )
+ {
+ assign( nDestIndex, get_native( nSrcIndex ));
+ }
+
+ /// Clear value of the slot \p nIndex
+ void clear( size_t nIndex)
+ {
+ base_class::clear( nIndex );
+ }
+
+ /// Get current value of slot \p nIndex
+ template <typename T>
+ T * get( size_t nIndex) const
+ {
+ return reinterpret_cast<T *>( get_native( nIndex ) );
+ }
+
+ /// Get native guarded pointer stored
+ guarded_pointer get_native( size_t nIndex ) const
+ {
+ return base_class::operator[](nIndex).get_guard()->pPost.load(atomics::memory_order_relaxed);
+ }
+
+ /// Capacity of the guard array
+ static CDS_CONSTEXPR size_t capacity()
+ {
+ return Count;
+ }
+ };
+
+ public:
+ /// Initializes ptb::GarbageCollector singleton
+ /**
+ The constructor calls GarbageCollector::Construct with passed parameters.
+ See ptb::GarbageCollector::Construct for explanation of parameters meaning.
+ */
+ PTB(
+ size_t nLiberateThreshold = 1024
+ , size_t nInitialThreadGuardCount = 8
+ )
+ {
+ ptb::GarbageCollector::Construct(
+ nLiberateThreshold,
+ nInitialThreadGuardCount
+ );
+ }
+
+ /// Terminates ptb::GarbageCollector singleton
+ /**
+ The destructor calls \code ptb::GarbageCollector::Destruct() \endcode
+ */
+ ~PTB()
+ {
+ ptb::GarbageCollector::Destruct();
+ }
+
+ /// Checks if count of hazard pointer is no less than \p nCountNeeded
+ /**
+ The function always returns \p true since the guard count is unlimited for
+ PTB garbage collector.
+ */
+ static bool check_available_guards( size_t nCountNeeded, bool /*bRaiseException*/ = true )
+ {
+ CDS_UNUSED( nCountNeeded );
+ return true;
+ }
+
+ /// Retire pointer \p p with function \p pFunc
+ /**
+ The function places pointer \p p to array of pointers ready for removing.
+ (so called retired pointer array). The pointer can be safely removed when no guarded pointer points to it.
+ Deleting the pointer is the function \p pFunc call.
+ */
+ template <typename T>
+ static void retire( T * p, void (* pFunc)(T *) )
+ {
+ ptb::GarbageCollector::instance().retirePtr( p, pFunc );
+ }
+
+ /// Retire pointer \p p with functor of type \p Disposer
+ /**
+ The function places pointer \p p to array of pointers ready for removing.
+ (so called retired pointer array). The pointer can be safely removed when no guarded pointer points to it.
+
+ See gc::HP::retire for \p Disposer requirements.
+ */
+ template <class Disposer, typename T>
+ static void retire( T * p )
+ {
+ retire( p, cds::details::static_functor<Disposer, T>::call );
+ }
+
+ /// Checks if Pass-the-Buck GC is constructed and may be used
+ static bool isUsed()
+ {
+ return ptb::GarbageCollector::isUsed();
+ }
+
+ /// Forced GC cycle call for current thread
+ /**
+ Usually, this function should not be called directly.
+ */
+ static void scan() ; // inline in ptb_impl.h
+
+ /// Synonym for \ref scan()
+ static void force_dispose()
+ {
+ scan();
+ }
+ };
+
+}} // namespace cds::gc
+
+#endif // #ifndef __CDS_GC_PTB_DECL_H
--- /dev/null
+//$$CDS-header$$
+
+#ifndef __CDS_GC_PTB_IMPL_H
+#define __CDS_GC_PTB_IMPL_H
+
+#include <cds/threading/model.h>
+
+//@cond
+namespace cds { namespace gc {
+
+ inline PTB::thread_gc::thread_gc(
+ bool bPersistent
+ )
+ : m_bPersistent( bPersistent )
+ {
+ if ( !cds::threading::Manager::isThreadAttached() )
+ cds::threading::Manager::attachThread();
+ }
+
+ inline PTB::thread_gc::~thread_gc()
+ {
+ if ( !m_bPersistent )
+ cds::threading::Manager::detachThread();
+ }
+
+ inline PTB::Guard::Guard()
+ : Guard::base_class( cds::threading::getGC<PTB>() )
+ {}
+
+ template <size_t COUNT>
+ inline PTB::GuardArray<COUNT>::GuardArray()
+ : GuardArray::base_class( cds::threading::getGC<PTB>() )
+ {}
+
+ inline void PTB::scan()
+ {
+ cds::threading::getGC<PTB>().scan();
+ }
+
+}} // namespace cds::gc
+//@endcond
+
+#endif // #ifndef __CDS_GC_PTB_IMPL_H
--- /dev/null
+//$$CDS-header$$
+
+#ifndef __CDS_GC_HZP_DETAILS_HP_ALLOC_H
+#define __CDS_GC_HZP_DETAILS_HP_ALLOC_H
+
+#include <cds/cxx11_atomic.h>
+#include <cds/details/allocator.h>
+#include <cds/gc/hp/details/hp_fwd.h>
+#include <cds/gc/hp/details/hp_type.h>
+
+//@cond
+namespace cds {
+ namespace gc { namespace hzp {
+ /// Hazard Pointer schema implementation details
+ namespace details {
+
+ /// Hazard pointer guard
+ /**
+ It is unsafe to use this class directly.
+ Instead, the AutoHPGuard class should be used.
+
+ Template parameter:
+ \li HazardPointer - type of hazard pointer. It is \ref hazard_pointer for Michael's Hazard Pointer reclamation schema
+ */
+ template <typename HazardPointer>
+ class HPGuardT: protected atomics::atomic<HazardPointer>
+ {
+ public:
+ typedef HazardPointer hazard_ptr ; ///< Hazard pointer type
+ private:
+ //@cond
+ typedef atomics::atomic<hazard_ptr> base_class;
+ //@endcond
+
+ protected:
+ //@cond
+ template <typename OtherHazardPointer, class Allocator> friend class HPAllocator;
+ //@endcond
+
+ public:
+ HPGuardT() CDS_NOEXCEPT
+ : base_class( nullptr )
+ {}
+ ~HPGuardT() CDS_NOEXCEPT
+ {}
+
+ /// Sets HP value. Guards pointer \p p from reclamation.
+ /**
+ Storing has release semantics.
+ */
+ template <typename T>
+ T * operator =( T * p ) CDS_NOEXCEPT
+ {
+ // We use atomic store with explicit memory order because other threads may read this hazard pointer concurrently
+ base_class::store( reinterpret_cast<hazard_ptr>(p), atomics::memory_order_release );
+ return p;
+ }
+
+ //@cond
+ std::nullptr_t operator=( std::nullptr_t ) CDS_NOEXCEPT
+ {
+ clear();
+ return nullptr;
+ }
+ //@endcond
+
+ /// Returns current value of hazard pointer
+ /**
+ Loading has acquire semantics
+ */
+ operator hazard_ptr() const CDS_NOEXCEPT
+ {
+ return get();
+ }
+
+ /// Returns current value of hazard pointer
+ /**
+ Loading has acquire semantics
+ */
+ hazard_ptr get() const CDS_NOEXCEPT
+ {
+ return base_class::load( atomics::memory_order_acquire );
+ }
+
+ /// Clears HP
+ /**
+ Clearing has relaxed semantics.
+ */
+ void clear() CDS_NOEXCEPT
+ {
+ // memory order is not necessary here
+ base_class::store( nullptr, atomics::memory_order_relaxed );
+ //CDS_COMPILER_RW_BARRIER;
+ }
+ };
+
+ /// Specialization of HPGuardT for hazard_pointer type
+ typedef HPGuardT<hazard_pointer> HPGuard;
+
+ /// Array of hazard pointers.
+ /**
+ Array of hazard-pointer. Placing a pointer into this array guards the pointer against reclamation.
+ Template parameter \p Count defines the size of hazard pointer array. \p Count parameter should not exceed
+ GarbageCollector::getHazardPointerCount().
+
+ It is unsafe to use this class directly. Instead, the AutoHPArray should be used.
+
+ While creating the object of HPArray class an array of size \p Count of hazard pointers is reserved by
+ the HP Manager of current thread. The object's destructor cleans all of reserved hazard pointer and
+ returns reserved HP to the HP pool of ThreadGC.
+
+ Usually, it is not necessary to create an object of this class. The object of class ThreadGC contains
+ the HPArray object and implements interface for HP setting and freeing.
+
+ Template parameter:
+ \li HazardPointer - type of hazard pointer. It is hazard_pointer usually
+ \li Count - capacity of array
+
+ */
+ template <typename HazardPointer, size_t Count>
+ class HPArrayT
+ {
+ public:
+ typedef HazardPointer hazard_ptr_type ; ///< Hazard pointer type
+ typedef HPGuardT<hazard_ptr_type> atomic_hazard_ptr ; ///< Element type of the array
+ static const size_t c_nCapacity = Count ; ///< Capacity of the array
+
+ private:
+ //@cond
+ atomic_hazard_ptr * m_arr ; ///< Hazard pointer array of size = \p Count
+ template <typename OtherHazardPointer, class Allocator> friend class HPAllocator;
+ //@endcond
+
+ public:
+ /// Constructs uninitialized array.
+ HPArrayT() CDS_NOEXCEPT
+ {}
+
+ /// Destructs object
+ ~HPArrayT() CDS_NOEXCEPT
+ {}
+
+ /// Returns max count of hazard pointer for this array
+ CDS_CONSTEXPR size_t capacity() const
+ {
+ return c_nCapacity;
+ }
+
+ /// Set hazard pointer \p nIndex. 0 <= \p nIndex < \p Count
+ void set( size_t nIndex, hazard_ptr_type hzPtr ) CDS_NOEXCEPT
+ {
+ assert( nIndex < capacity() );
+ m_arr[nIndex] = hzPtr;
+ }
+
+ /// Returns reference to hazard pointer of index \p nIndex (0 <= \p nIndex < \p Count)
+ atomic_hazard_ptr& operator []( size_t nIndex ) CDS_NOEXCEPT
+ {
+ assert( nIndex < capacity() );
+ return m_arr[nIndex];
+ }
+
+ /// Returns reference to hazard pointer of index \p nIndex (0 <= \p nIndex < \p Count) [const version]
+ atomic_hazard_ptr& operator []( size_t nIndex ) const CDS_NOEXCEPT
+ {
+ assert( nIndex < capacity() );
+ return m_arr[nIndex];
+ }
+
+ /// Clears (sets to \p nullptr) hazard pointer \p nIndex
+ void clear( size_t nIndex ) CDS_NOEXCEPT
+ {
+ assert( nIndex < capacity() );
+ m_arr[ nIndex ].clear();
+ }
+ };
+
+ /// Specialization of HPArrayT class for hazard_pointer type
+ template <size_t Count> using HPArray = HPArrayT<hazard_pointer, Count >;
+
+ /// Allocator of hazard pointers for the thread
+ /**
+ The hazard pointer array is the free-list of unused hazard pointer for the thread.
+ The array is managed as a stack.
+ The max size (capacity) of array is defined at ctor time and cannot be changed during object's lifetime
+
+ Each allocator object is thread-private.
+
+ Template parameters:
+ \li HazardPointer - type of hazard pointer (hazard_pointer usually)
+ \li Allocator - memory allocator class, default is \ref CDS_DEFAULT_ALLOCATOR
+
+ This helper class should not be used directly.
+ */
+ template < typename HazardPointer, class Allocator = CDS_DEFAULT_ALLOCATOR >
+ class HPAllocator
+ {
+ public:
+ typedef HazardPointer hazard_ptr_type ; ///< type of hazard pointer
+ typedef HPGuardT<hazard_ptr_type> atomic_hazard_ptr ; ///< Atomic hazard pointer type
+ typedef Allocator allocator_type ; ///< allocator type
+
+ private:
+ //@cond
+ typedef cds::details::Allocator< atomic_hazard_ptr, allocator_type > allocator_impl;
+
+ atomic_hazard_ptr * m_arrHazardPtr ; ///< Array of hazard pointers
+ size_t m_nTop ; ///< The top of stack
+ const size_t m_nCapacity ; ///< Array capacity
+
+ //@endcond
+
+ public:
+ /// Default ctor
+ explicit HPAllocator(
+ size_t nCapacity ///< max count of hazard pointer per thread
+ )
+ : m_arrHazardPtr( alloc_array( nCapacity ) )
+ , m_nCapacity( nCapacity )
+ {
+ make_free();
+ }
+
+ /// Dtor
+ ~HPAllocator()
+ {
+ allocator_impl().Delete( m_arrHazardPtr, capacity() );
+ }
+
+ /// Get capacity of array
+ size_t capacity() const CDS_NOEXCEPT
+ {
+ return m_nCapacity;
+ }
+
+ /// Get size of array. The size is equal to the capacity of array
+ size_t size() const CDS_NOEXCEPT
+ {
+ return capacity();
+ }
+
+ /// Checks if all items are allocated
+ bool isFull() const CDS_NOEXCEPT
+ {
+ return m_nTop == 0;
+ }
+
+ /// Allocates hazard pointer
+ atomic_hazard_ptr& alloc() CDS_NOEXCEPT
+ {
+ assert( m_nTop > 0 );
+ --m_nTop;
+ return m_arrHazardPtr[m_nTop];
+ }
+
+ /// Frees previously allocated hazard pointer
+ void free( atomic_hazard_ptr& hp ) CDS_NOEXCEPT
+ {
+ assert( m_nTop < capacity() );
+ hp.clear();
+ ++m_nTop;
+ CDS_COMPILER_RW_BARRIER ; // ???
+ }
+
+ /// Allocates hazard pointers array
+ /**
+ Allocates \p Count hazard pointers from array \p m_arrHazardPtr
+ Returns initialized object \p arr
+ */
+ template <size_t Count>
+ void alloc( HPArrayT<hazard_ptr_type, Count>& arr ) CDS_NOEXCEPT
+ {
+ assert( m_nTop >= Count );
+ m_nTop -= Count;
+ arr.m_arr = m_arrHazardPtr + m_nTop;
+ }
+
+ /// Frees hazard pointer array
+ /**
+ Frees the array of hazard pointers allocated by previous call \p this->alloc.
+ */
+ template <size_t Count>
+ void free( const HPArrayT<hazard_ptr_type, Count>& arr ) CDS_NOEXCEPT
+ {
+ assert( m_nTop + Count <= capacity());
+ for ( size_t i = m_nTop; i < m_nTop + Count; ++i )
+ m_arrHazardPtr[ i ].clear();
+ m_nTop += Count;
+ }
+
+ /// Makes all HP free
+ void clear() CDS_NOEXCEPT
+ {
+ make_free();
+ }
+
+ /// Returns to i-th hazard pointer
+ atomic_hazard_ptr& operator []( size_t i ) CDS_NOEXCEPT
+ {
+ assert( i < capacity() );
+ return m_arrHazardPtr[i];
+ }
+
+ private:
+ //@cond
+ void make_free() CDS_NOEXCEPT
+ {
+ for ( size_t i = 0; i < capacity(); ++i )
+ m_arrHazardPtr[ i ].clear();
+ m_nTop = capacity();
+ }
+
+ atomic_hazard_ptr * alloc_array( size_t nCapacity )
+ {
+ return allocator_impl().NewArray( nCapacity );
+ }
+ //@endcond
+ };
+
+ }}} // namespace gc::hzp::details
+} // namespace cds
+//@endcond
+
+#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_ALLOC_H
--- /dev/null
+//$$CDS-header$$
+
+#ifndef __CDS_GC_HZP_DETAILS_HP_FWD_H
+#define __CDS_GC_HZP_DETAILS_HP_FWD_H
+
+namespace cds {
+ namespace gc { namespace hzp {
+
+ // forward declarations
+ class GarbageCollector;
+ class ThreadGC;
+ } }
+}
+
+#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_FWD_H
--- /dev/null
+//$$CDS-header$$
+
+#ifndef __CDS_GC_HZP_DETAILS_HP_INLINE_H
+#define __CDS_GC_HZP_DETAILS_HP_INLINE_H
+
+namespace cds {
+ namespace gc{ namespace hzp { namespace details {
+
+ /************************************************************************/
+ /* INLINES */
+ /************************************************************************/
+ inline retired_vector::retired_vector( const cds::gc::hzp::GarbageCollector& HzpMgr )
+ : m_arr( HzpMgr.getMaxRetiredPtrCount() ),
+ m_nSize(0)
+ {}
+
+ inline HPRec::HPRec( const cds::gc::hzp::GarbageCollector& HzpMgr )
+ : m_hzp( HzpMgr.getHazardPointerCount() ),
+ m_arrRetired( HzpMgr )
+ {}
+
+ } } } // namespace gc::hzp::details
+} // namespace cds
+
+
+#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_INLINE_H
--- /dev/null
+//$$CDS-header$$
+
+#ifndef __CDS_GC_HZP_DETAILS_HP_RETIRED_H
+#define __CDS_GC_HZP_DETAILS_HP_RETIRED_H
+
+#include <cds/gc/hp/details/hp_fwd.h>
+#include <cds/gc/hp/details/hp_type.h>
+
+#include <cds/details/bounded_array.h>
+
+namespace cds {
+ namespace gc{ namespace hzp { namespace details {
+
+ /// Retired pointer
+ typedef cds::gc::details::retired_ptr retired_ptr;
+
+ /// Array of retired pointers
+ /**
+ The vector of retired pointer ready to delete.
+
+ The Hazard Pointer schema is build on thread-static arrays. For each HP-enabled thread the HP manager allocates
+ array of retired pointers. The array belongs to the thread: owner thread writes to the array, other threads
+ just read it.
+ */
+ class retired_vector {
+ /// Underlying vector implementation
+ typedef cds::details::bounded_array<retired_ptr> retired_vector_impl;
+
+ retired_vector_impl m_arr ; ///< the array of retired pointers
+ size_t m_nSize ; ///< Current size of \p m_arr
+
+ public:
+ /// Iterator
+ typedef retired_vector_impl::iterator iterator;
+
+ /// Constructor
+ retired_vector( const cds::gc::hzp::GarbageCollector& HzpMgr ) ; // inline
+ ~retired_vector()
+ {}
+
+ /// Vector capacity.
+ /**
+ The capacity is constant for any thread. It is defined by cds::gc::hzp::GarbageCollector.
+ */
+ size_t capacity() const { return m_arr.capacity(); }
+
+ /// Current vector size (count of retired pointers in the vector)
+ size_t size() const { return m_nSize; }
+
+ /// Set vector size. Uses internally
+ void size( size_t nSize )
+ {
+ assert( nSize <= capacity() );
+ m_nSize = nSize;
+ }
+
+ /// Pushes retired pointer to the vector
+ void push( const retired_ptr& p )
+ {
+ assert( m_nSize < capacity() );
+ m_arr[ m_nSize ] = p;
+ ++m_nSize;
+ }
+
+ /// Checks if the vector is full (size() == capacity() )
+ bool isFull() const
+ {
+ return m_nSize >= capacity();
+ }
+
+ /// Begin iterator
+ iterator begin() { return m_arr.begin(); }
+ /// End iterator
+ iterator end() { return m_arr.begin() + m_nSize ; }
+
+ /// Clears the vector. After clearing, size() == 0
+ void clear()
+ {
+ m_nSize = 0;
+ }
+ };
+
+ } } } // namespace gc::hzp::details
+} // namespace cds
+
+#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_RETIRED_H
--- /dev/null
+//$$CDS-header$$
+
+#ifndef __CDS_GC_HZP_DETAILS_HP_TYPE_H
+#define __CDS_GC_HZP_DETAILS_HP_TYPE_H
+
+#include <cds/gc/details/retired_ptr.h>
+
+namespace cds {
+ namespace gc {
+ namespace hzp {
+
+ /// Hazard pointer
+ typedef void * hazard_pointer;
+
+ /// Pointer to function to free (destruct and deallocate) retired pointer of specific type
+ typedef cds::gc::details::free_retired_ptr_func free_retired_ptr_func;
+ }
+ }
+}
+
+#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_TYPE_H
+
+
--- /dev/null
+//$$CDS-header$$
+
+#ifndef __CDS_GC_HZP_HZP_H
+#define __CDS_GC_HZP_HZP_H
+
+#include <vector>
+#include <cds/cxx11_atomic.h>
+#include <cds/os/thread.h>
+#include <cds/gc/hp/details/hp_fwd.h>
+#include <cds/gc/hp/details/hp_alloc.h>
+#include <cds/gc/hp/details/hp_retired.h>
+
+#if CDS_COMPILER == CDS_COMPILER_MSVC
+# pragma warning(push)
+ // warning C4251: 'cds::gc::hzp::GarbageCollector::m_pListHead' : class 'cds::cxx11_atomic::atomic<T>'
+ // needs to have dll-interface to be used by clients of class 'cds::gc::hzp::GarbageCollector'
+# pragma warning(disable: 4251)
+#endif
+
+/*
+ Editions:
+ 2007.12.24 khizmax Add statistics and CDS_GATHER_HAZARDPTR_STAT macro
+ 2008.03.06 khizmax Refactoring: implementation of HazardPtrMgr is moved to hazardptr.cpp
+ 2008.03.08 khizmax Remove HazardPtrMgr singleton. Now you must initialize/destroy HazardPtrMgr calling
+ HazardPtrMgr::Construct / HazardPtrMgr::Destruct before use (usually in main() function).
+ 2008.12.06 khizmax Refactoring. Changes class name, namespace hierarchy, all helper defs have been moved to details namespace
+ 2010.01.27 khizmax Introducing memory order constraint
+*/
+
+namespace cds {
+ /**
+ @page cds_garbage_collectors_comparison GC comparison
+ @ingroup cds_garbage_collector
+
+ <table>
+ <tr>
+ <th>Feature</th>
+ <th>%cds::gc::HP</th>
+ <th>%cds::gc::PTB</th>
+ </tr>
+ <tr>
+ <td>Implementation quality</td>
+ <td>stable</td>
+ <td>stable</td>
+ </tr>
+ <tr>
+ <td>Performance rank (1 - slowest, 5 - fastest)</td>
+ <td>5</td>
+ <td>4</td>
+ </tr>
+ <tr>
+ <td>Max number of guarded (hazard) pointers per thread</td>
+ <td>limited (specifies in GC object ctor)</td>
+ <td>unlimited (dynamically allocated when needed)</td>
+ </tr>
+ <tr>
+ <td>Max number of retired pointers<sup>1</sup></td>
+ <td>bounded</td>
+ <td>bounded</td>
+ </tr>
+ <tr>
+ <td>Array of retired pointers</td>
+ <td>preallocated for each thread, limited in size</td>
+ <td>global for the entire process, unlimited (dynamically allocated when needed)</td>
+ </tr>
+ <tr>
+ <td>Support direct pointer to item of lock-free container (useful for iterators)</td>
+ <td>not supported</td>
+ <td>not supported</td>
+ </tr>
+ </table>
+
+ <sup>1</sup>Unbounded count of retired pointer means a possibility of memory exhaustion.
+ */
+
+ /// Different safe memory reclamation schemas (garbage collectors)
+ /** @ingroup cds_garbage_collector
+
+ This namespace specifies different safe memory reclamation (SMR) algorithms.
+ See \ref cds_garbage_collector "Garbage collectors"
+ */
+ namespace gc {
+
+ /// Michael's Hazard Pointers reclamation schema
+ /**
+ \par Sources:
+ - [2002] Maged M.Michael "Safe memory reclamation for dynamic lock-freeobjects using atomic reads and writes"
+ - [2003] Maged M.Michael "Hazard Pointers: Safe memory reclamation for lock-free objects"
+ - [2004] Andrei Alexandrescy, Maged Michael "Lock-free Data Structures with Hazard Pointers"
+
+
+ The cds::gc::hzp namespace and its members are internal representation of Hazard Pointer GC and should not be used directly.
+ Use cds::gc::HP class in your code.
+
+ Hazard Pointer garbage collector is a singleton. The main user-level part of Hazard Pointer schema is
+ GC class and its nested classes. Before use any HP-related class you must initialize HP garbage collector
+ by contructing cds::gc::HP object in beginning of your main().
+ See cds::gc::HP class for explanation.
+ */
+ namespace hzp {
+
+ namespace details {
+ /// Hazard pointer record of the thread
+ /**
+ The structure of type "single writer - multiple reader": only the owner thread may write to this structure
+ other threads have read-only access.
+ */
+ struct HPRec {
+ HPAllocator<hazard_pointer> m_hzp ; ///< array of hazard pointers. Implicit \ref CDS_DEFAULT_ALLOCATOR dependency
+ retired_vector m_arrRetired ; ///< Retired pointer array
+
+ /// Ctor
+ HPRec( const cds::gc::hzp::GarbageCollector& HzpMgr ) ; // inline
+ ~HPRec()
+ {}
+
+ /// Clears all hazard pointers
+ void clear()
+ {
+ m_hzp.clear();
+ }
+ };
+ } // namespace details
+
+ /// GarbageCollector::Scan phase strategy
+ /**
+ See GarbageCollector::Scan for explanation
+ */
+ enum scan_type {
+ classic, ///< classic scan as described in Michael's works (see GarbageCollector::classic_scan)
+ inplace ///< inplace scan without allocation (see GarbageCollector::inplace_scan)
+ };
+
+ /// Hazard Pointer singleton
+ /**
+ Safe memory reclamation schema by Michael "Hazard Pointers"
+
+ \par Sources:
+ \li [2002] Maged M.Michael "Safe memory reclamation for dynamic lock-freeobjects using atomic reads and writes"
+ \li [2003] Maged M.Michael "Hazard Pointers: Safe memory reclamation for lock-free objects"
+ \li [2004] Andrei Alexandrescy, Maged Michael "Lock-free Data Structures with Hazard Pointers"
+
+ */
+ class CDS_EXPORT_API GarbageCollector
+ {
+ public:
+ typedef cds::atomicity::event_counter event_counter ; ///< event counter type
+
+ /// Internal GC statistics
+ struct InternalState {
+ size_t nHPCount ; ///< HP count per thread (const)
+ size_t nMaxThreadCount ; ///< Max thread count (const)
+ size_t nMaxRetiredPtrCount ; ///< Max retired pointer count per thread (const)
+ size_t nHPRecSize ; ///< Size of HP record, bytes (const)
+
+ size_t nHPRecAllocated ; ///< Count of HP record allocations
+ size_t nHPRecUsed ; ///< Count of HP record used
+ size_t nTotalRetiredPtrCount ; ///< Current total count of retired pointers
+ size_t nRetiredPtrInFreeHPRecs ; ///< Count of retired pointer in free (unused) HP records
+
+ event_counter::value_type evcAllocHPRec ; ///< Count of HPRec allocations
+ event_counter::value_type evcRetireHPRec ; ///< Count of HPRec retire events
+ event_counter::value_type evcAllocNewHPRec; ///< Count of new HPRec allocations from heap
+ event_counter::value_type evcDeleteHPRec ; ///< Count of HPRec deletions
+
+ event_counter::value_type evcScanCall ; ///< Count of Scan calling
+ event_counter::value_type evcHelpScanCall ; ///< Count of HelpScan calling
+ event_counter::value_type evcScanFromHelpScan;///< Count of Scan calls from HelpScan
+
+ event_counter::value_type evcDeletedNode ; ///< Count of deleting of retired objects
+ event_counter::value_type evcDeferredNode ; ///< Count of objects that cannot be deleted in Scan phase because of a hazard_pointer guards it
+ };
+
+ /// No GarbageCollector object is created
+ CDS_DECLARE_EXCEPTION( HZPManagerEmpty, "Global Hazard Pointer GarbageCollector is NULL" );
+
+ /// Not enough required Hazard Pointer count
+ CDS_DECLARE_EXCEPTION( HZPTooMany, "Not enough required Hazard Pointer count" );
+
+ private:
+ /// Internal GC statistics
+ struct Statistics {
+ event_counter m_AllocHPRec ; ///< Count of HPRec allocations
+ event_counter m_RetireHPRec ; ///< Count of HPRec retire events
+ event_counter m_AllocNewHPRec ; ///< Count of new HPRec allocations from heap
+ event_counter m_DeleteHPRec ; ///< Count of HPRec deletions
+
+ event_counter m_ScanCallCount ; ///< Count of Scan calling
+ event_counter m_HelpScanCallCount ; ///< Count of HelpScan calling
+ event_counter m_CallScanFromHelpScan ; ///< Count of Scan calls from HelpScan
+
+ event_counter m_DeletedNode ; ///< Count of retired objects deleting
+ event_counter m_DeferredNode ; ///< Count of objects that cannot be deleted in Scan phase because of a hazard_pointer guards it
+ };
+
+ /// Internal list of cds::gc::hzp::details::HPRec
+ struct hplist_node: public details::HPRec
+ {
+ hplist_node * m_pNextNode ; ///< next hazard ptr record in list
+ atomics::atomic<OS::ThreadId> m_idOwner ; ///< Owner thread id; 0 - the record is free (not owned)
+ atomics::atomic<bool> m_bFree ; ///< true if record if free (not owned)
+
+ //@cond
+ hplist_node( const GarbageCollector& HzpMgr )
+ : HPRec( HzpMgr ),
+ m_pNextNode( nullptr ),
+ m_idOwner( OS::c_NullThreadId ),
+ m_bFree( true )
+ {}
+
+ ~hplist_node()
+ {
+ assert( m_idOwner.load( atomics::memory_order_relaxed ) == OS::c_NullThreadId );
+ assert( m_bFree.load(atomics::memory_order_relaxed) );
+ }
+ //@endcond
+ };
+
+ atomics::atomic<hplist_node *> m_pListHead ; ///< Head of GC list
+
+ static GarbageCollector * m_pHZPManager ; ///< GC instance pointer
+
+ Statistics m_Stat ; ///< Internal statistics
+ bool m_bStatEnabled ; ///< true - statistics enabled
+
+ const size_t m_nHazardPointerCount ; ///< max count of thread's hazard pointer
+ const size_t m_nMaxThreadCount ; ///< max count of thread
+ const size_t m_nMaxRetiredPtrCount ; ///< max count of retired ptr per thread
+ scan_type m_nScanType ; ///< scan type (see \ref scan_type enum)
+
+
+ private:
+ /// Ctor
+ GarbageCollector(
+ size_t nHazardPtrCount = 0, ///< Hazard pointer count per thread
+ size_t nMaxThreadCount = 0, ///< Max count of thread
+ size_t nMaxRetiredPtrCount = 0, ///< Capacity of the array of retired objects
+ scan_type nScanType = inplace ///< Scan type (see \ref scan_type enum)
+ );
+
+ /// Dtor
+ ~GarbageCollector();
+
+ /// Allocate new HP record
+ hplist_node * NewHPRec();
+
+ /// Permanently deletes HPrecord \p pNode
+ /**
+ Caveat: for performance reason this function is defined as inline and cannot be called directly
+ */
+ void DeleteHPRec( hplist_node * pNode );
+
+ /// Permanently deletes retired pointer \p p
+ /**
+ Caveat: for performance reason this function is defined as inline and cannot be called directly
+ */
+ void DeletePtr( details::retired_ptr& p );
+
+ //@cond
+ void detachAllThread();
+ //@endcond
+
+ public:
+ /// Creates GarbageCollector singleton
+ /**
+ GC is the singleton. If GC instance is not exist then the function creates the instance.
+ Otherwise it does nothing.
+
+ The Michael's HP reclamation schema depends of three parameters:
+
+ \p nHazardPtrCount - HP pointer count per thread. Usually it is small number (2-4) depending from
+ the data structure algorithms. By default, if \p nHazardPtrCount = 0,
+ the function uses maximum of HP count for CDS library.
+
+ \p nMaxThreadCount - max count of thread with using HP GC in your application. Default is 100.
+
+ \p nMaxRetiredPtrCount - capacity of array of retired pointers for each thread. Must be greater than
+ \p nHazardPtrCount * \p nMaxThreadCount.
+ Default is 2 * \p nHazardPtrCount * \p nMaxThreadCount.
+ */
+ static void CDS_STDCALL Construct(
+ size_t nHazardPtrCount = 0, ///< Hazard pointer count per thread
+ size_t nMaxThreadCount = 0, ///< Max count of simultaneous working thread in your application
+ size_t nMaxRetiredPtrCount = 0, ///< Capacity of the array of retired objects for the thread
+ scan_type nScanType = inplace ///< Scan type (see \ref scan_type enum)
+ );
+
+ /// Destroys global instance of GarbageCollector
+ /**
+ The parameter \p bDetachAll should be used carefully: if its value is \p true,
+ then the destroying GC automatically detaches all attached threads. This feature
+ can be useful when you have no control over the thread termination, for example,
+ when \p libcds is injected into existing external thread.
+ */
+ static void CDS_STDCALL Destruct(
+ bool bDetachAll = false ///< Detach all threads
+ );
+
+ /// Returns pointer to GarbageCollector instance
+ static GarbageCollector& instance()
+ {
+ if ( !m_pHZPManager )
+ throw HZPManagerEmpty();
+ return *m_pHZPManager;
+ }
+
+ /// Checks if global GC object is constructed and may be used
+ static bool isUsed()
+ {
+ return m_pHZPManager != nullptr;
+ }
+
+ /// Returns max Hazard Pointer count defined in construction time
+ size_t getHazardPointerCount() const { return m_nHazardPointerCount; }
+
+ /// Returns max thread count defined in construction time
+ size_t getMaxThreadCount() const { return m_nMaxThreadCount; }
+
+ /// Returns max size of retired objects array. It is defined in construction time
+ size_t getMaxRetiredPtrCount() const { return m_nMaxRetiredPtrCount; }
+
+ // Internal statistics
+
+ /// Get internal statistics
+ InternalState& getInternalState(InternalState& stat) const;
+
+ /// Checks if internal statistics enabled
+ bool isStatisticsEnabled() const { return m_bStatEnabled; }
+
+ /// Enables/disables internal statistics
+ bool enableStatistics( bool bEnable )
+ {
+ bool bEnabled = m_bStatEnabled;
+ m_bStatEnabled = bEnable;
+ return bEnabled;
+ }
+
+ /// Checks that required hazard pointer count \p nRequiredCount is less or equal then max hazard pointer count
+ /**
+ If \p nRequiredCount > getHazardPointerCount() then the exception HZPTooMany is thrown
+ */
+ static void checkHPCount( unsigned int nRequiredCount )
+ {
+ if ( instance().getHazardPointerCount() < nRequiredCount )
+ throw HZPTooMany();
+ }
+
+ /// Get current scan strategy
+ scan_type getScanType() const
+ {
+ return m_nScanType;
+ }
+
+ /// Set current scan strategy
+ /** @anchor hzp_gc_setScanType
+ Scan strategy changing is allowed on the fly.
+ */
+ void setScanType(
+ scan_type nScanType ///< new scan strategy
+ )
+ {
+ m_nScanType = nScanType;
+ }
+
+ public: // Internals for threads
+
+ /// Allocates Hazard Pointer GC record. For internal use only
+ details::HPRec * AllocateHPRec();
+
+ /// Free HP record. For internal use only
+ void RetireHPRec( details::HPRec * pRec );
+
+ /// The main garbage collecting function
+ /**
+ This function is called internally by ThreadGC object when upper bound of thread's list of reclaimed pointers
+ is reached.
+
+ There are the following scan algorithm:
+ - \ref hzp_gc_classic_scan "classic_scan" allocates memory for internal use
+ - \ref hzp_gc_inplace_scan "inplace_scan" does not allocate any memory
+
+ Use \ref hzp_gc_setScanType "setScanType" member function to setup appropriate scan algorithm.
+ */
+ void Scan( details::HPRec * pRec )
+ {
+ switch ( m_nScanType ) {
+ case inplace:
+ inplace_scan( pRec );
+ break;
+ default:
+ assert(false) ; // Forgotten something?..
+ case classic:
+ classic_scan( pRec );
+ break;
+ }
+ }
+
+ /// Helper scan routine
+ /**
+ The function guarantees that every node that is eligible for reuse is eventually freed, barring
+ thread failures. To do so, after executing Scan, a thread executes a HelpScan,
+ where it checks every HP record. If an HP record is inactive, the thread moves all "lost" reclaimed pointers
+ to thread's list of reclaimed pointers.
+
+ The function is called internally by Scan.
+ */
+ void HelpScan( details::HPRec * pThis );
+
+ protected:
+ /// Classic scan algorithm
+ /** @anchor hzp_gc_classic_scan
+ Classical scan algorithm as described in Michael's paper.
+
+ A scan includes four stages. The first stage involves scanning the array HP for non-null values.
+ Whenever a non-null value is encountered, it is inserted in a local list of currently protected pointer.
+ Only stage 1 accesses shared variables. The following stages operate only on private variables.
+
+ The second stage of a scan involves sorting local list of protected pointers to allow
+ binary search in the third stage.
+
+ The third stage of a scan involves checking each reclaimed node
+ against the pointers in local list of protected pointers. If the binary search yields
+ no match, the node is freed. Otherwise, it cannot be deleted now and must kept in thread's list
+ of reclaimed pointers.
+
+ The forth stage prepares new thread's private list of reclaimed pointers
+ that could not be freed during the current scan, where they remain until the next scan.
+
+ This algorithm allocates memory for internal HP array.
+
+ This function is called internally by ThreadGC object when upper bound of thread's list of reclaimed pointers
+ is reached.
+ */
+ void classic_scan( details::HPRec * pRec );
+
+ /// In-place scan algorithm
+ /** @anchor hzp_gc_inplace_scan
+ Unlike the \ref hzp_gc_classic_scan "classic_scan" algorithm, \p inplace_scan does not allocate any memory.
+ All operations are performed in-place.
+ */
+ void inplace_scan( details::HPRec * pRec );
+ };
+
+ /// Thread's hazard pointer manager
+ /**
+ To use Hazard Pointer reclamation schema each thread object must be linked with the object of ThreadGC class
+ that interacts with GarbageCollector global object. The linkage is performed by calling \ref cds_threading "cds::threading::Manager::attachThread()"
+ on the start of each thread that uses HP GC. Before terminating the thread linked to HP GC it is necessary to call
+ \ref cds_threading "cds::threading::Manager::detachThread()".
+ */
+ class ThreadGC
+ {
+ GarbageCollector& m_HzpManager ; ///< Hazard Pointer GC singleton
+ details::HPRec * m_pHzpRec ; ///< Pointer to thread's HZP record
+
+ public:
+ /// Default constructor
+ ThreadGC()
+ : m_HzpManager( GarbageCollector::instance() ),
+ m_pHzpRec( nullptr )
+ {}
+
+ /// The object is not copy-constructible
+ ThreadGC( ThreadGC const& ) = delete;
+
+ ~ThreadGC()
+ {
+ fini();
+ }
+
+ /// Checks if thread GC is initialized
+ bool isInitialized() const { return m_pHzpRec != nullptr; }
+
+ /// Initialization. Repeat call is available
+ void init()
+ {
+ if ( !m_pHzpRec )
+ m_pHzpRec = m_HzpManager.AllocateHPRec();
+ }
+
+ /// Finalization. Repeat call is available
+ void fini()
+ {
+ if ( m_pHzpRec ) {
+ details::HPRec * pRec = m_pHzpRec;
+ m_pHzpRec = nullptr;
+ m_HzpManager.RetireHPRec( pRec );
+ }
+ }
+
+ /// Initializes HP guard \p guard
+ details::HPGuard& allocGuard()
+ {
+ assert( m_pHzpRec );
+ return m_pHzpRec->m_hzp.alloc();
+ }
+
+ /// Frees HP guard \p guard
+ void freeGuard( details::HPGuard& guard )
+ {
+ assert( m_pHzpRec );
+ m_pHzpRec->m_hzp.free( guard );
+ }
+
+ /// Initializes HP guard array \p arr
+ template <size_t Count>
+ void allocGuard( details::HPArray<Count>& arr )
+ {
+ assert( m_pHzpRec );
+ m_pHzpRec->m_hzp.alloc( arr );
+ }
+
+ /// Frees HP guard array \p arr
+ template <size_t Count>
+ void freeGuard( details::HPArray<Count>& arr )
+ {
+ assert( m_pHzpRec );
+ m_pHzpRec->m_hzp.free( arr );
+ }
+
+ /// Places retired pointer \p and its deleter \p pFunc into thread's array of retired pointer for deferred reclamation
+ template <typename T>
+ void retirePtr( T * p, void (* pFunc)(T *) )
+ {
+ retirePtr( details::retired_ptr( reinterpret_cast<void *>( p ), reinterpret_cast<free_retired_ptr_func>( pFunc ) ) );
+ }
+
+ /// Places retired pointer \p into thread's array of retired pointer for deferred reclamation
+ void retirePtr( const details::retired_ptr& p )
+ {
+ m_pHzpRec->m_arrRetired.push( p );
+
+ if ( m_pHzpRec->m_arrRetired.isFull() ) {
+ // Max of retired pointer count is reached. Do scan
+ scan();
+ }
+ }
+
+ //@cond
+ void scan()
+ {
+ m_HzpManager.Scan( m_pHzpRec );
+ m_HzpManager.HelpScan( m_pHzpRec );
+ }
+ //@endcond
+ };
+
+ /// Auto HPGuard.
+ /**
+ This class encapsulates Hazard Pointer guard to protect a pointer against deletion .
+ It allocates one HP from thread's HP array in constructor and free the HP allocated in destruction time.
+ */
+ class AutoHPGuard
+ {
+ //@cond
+ details::HPGuard& m_hp ; ///< Hazard pointer guarded
+ ThreadGC& m_gc ; ///< Thread GC
+ //@endcond
+
+ public:
+ typedef details::HPGuard::hazard_ptr hazard_ptr ; ///< Hazard pointer type
+ public:
+ /// Allocates HP guard from \p gc
+ AutoHPGuard( ThreadGC& gc )
+ : m_hp( gc.allocGuard() )
+ , m_gc( gc )
+ {}
+
+ /// Allocates HP guard from \p gc and protects the pointer \p p of type \p T
+ template <typename T>
+ AutoHPGuard( ThreadGC& gc, T * p )
+ : m_hp( gc.allocGuard() )
+ , m_gc( gc )
+ {
+ m_hp = p;
+ }
+
+ /// Frees HP guard. The pointer guarded may be deleted after this.
+ ~AutoHPGuard()
+ {
+ m_gc.freeGuard( m_hp );
+ }
+
+ /// Returns thread GC
+ ThreadGC& getGC() const
+ {
+ return m_gc;
+ }
+
+ /// Protects the pointer \p p against reclamation (guards the pointer).
+ template <typename T>
+ T * operator =( T * p )
+ {
+ return m_hp = p;
+ }
+
+ //@cond
+ std::nullptr_t operator =(std::nullptr_t)
+ {
+ return m_hp = nullptr;
+ }
+
+ hazard_ptr get() const
+ {
+ return m_hp;
+ }
+ //@endcond
+ };
+
+ /// Auto-managed array of hazard pointers
+ /**
+ This class is wrapper around cds::gc::hzp::details::HPArray class.
+ \p Count is the size of HP array
+ */
+ template <size_t Count>
+ class AutoHPArray: public details::HPArray<Count>
+ {
+ ThreadGC& m_mgr ; ///< Thread GC
+
+ public:
+ /// Rebind array for other size \p COUNT2
+ template <size_t Count2>
+ struct rebind {
+ typedef AutoHPArray<Count2> other ; ///< rebinding result
+ };
+
+ public:
+ /// Allocates array of HP guard from \p mgr
+ AutoHPArray( ThreadGC& mgr )
+ : m_mgr( mgr )
+ {
+ mgr.allocGuard( *this );
+ }
+
+ /// Frees array of HP guard
+ ~AutoHPArray()
+ {
+ m_mgr.freeGuard( *this );
+ }
+
+ /// Returns thread GC
+ ThreadGC& getGC() const { return m_mgr; }
+ };
+
+ } // namespace hzp
+}} // namespace cds::gc
+
+// Inlines
+#include <cds/gc/hp/details/hp_inline.h>
+
+#if CDS_COMPILER == CDS_COMPILER_MSVC
+# pragma warning(pop)
+#endif
+
+#endif // #ifndef __CDS_GC_HZP_HZP_H
#define __CDS_GC_HP_DECL_H
#include <stdexcept> // overflow_error
-#include <cds/gc/hzp/hzp.h>
+#include <cds/gc/hp/hp.h>
#include <cds/details/marked_ptr.h>
namespace cds { namespace gc {
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_HZP_DETAILS_HP_ALLOC_H
-#define __CDS_GC_HZP_DETAILS_HP_ALLOC_H
-
-#include <cds/cxx11_atomic.h>
-#include <cds/details/allocator.h>
-#include <cds/gc/hzp/details/hp_fwd.h>
-#include <cds/gc/hzp/details/hp_type.h>
-
-//@cond
-namespace cds {
- namespace gc { namespace hzp {
- /// Hazard Pointer schema implementation details
- namespace details {
-
- /// Hazard pointer guard
- /**
- It is unsafe to use this class directly.
- Instead, the AutoHPGuard class should be used.
-
- Template parameter:
- \li HazardPointer - type of hazard pointer. It is \ref hazard_pointer for Michael's Hazard Pointer reclamation schema
- */
- template <typename HazardPointer>
- class HPGuardT: protected atomics::atomic<HazardPointer>
- {
- public:
- typedef HazardPointer hazard_ptr ; ///< Hazard pointer type
- private:
- //@cond
- typedef atomics::atomic<hazard_ptr> base_class;
- //@endcond
-
- protected:
- //@cond
- template <typename OtherHazardPointer, class Allocator> friend class HPAllocator;
- //@endcond
-
- public:
- HPGuardT() CDS_NOEXCEPT
- : base_class( nullptr )
- {}
- ~HPGuardT() CDS_NOEXCEPT
- {}
-
- /// Sets HP value. Guards pointer \p p from reclamation.
- /**
- Storing has release semantics.
- */
- template <typename T>
- T * operator =( T * p ) CDS_NOEXCEPT
- {
- // We use atomic store with explicit memory order because other threads may read this hazard pointer concurrently
- base_class::store( reinterpret_cast<hazard_ptr>(p), atomics::memory_order_release );
- return p;
- }
-
- //@cond
- std::nullptr_t operator=( std::nullptr_t ) CDS_NOEXCEPT
- {
- clear();
- return nullptr;
- }
- //@endcond
-
- /// Returns current value of hazard pointer
- /**
- Loading has acquire semantics
- */
- operator hazard_ptr() const CDS_NOEXCEPT
- {
- return get();
- }
-
- /// Returns current value of hazard pointer
- /**
- Loading has acquire semantics
- */
- hazard_ptr get() const CDS_NOEXCEPT
- {
- return base_class::load( atomics::memory_order_acquire );
- }
-
- /// Clears HP
- /**
- Clearing has relaxed semantics.
- */
- void clear() CDS_NOEXCEPT
- {
- // memory order is not necessary here
- base_class::store( nullptr, atomics::memory_order_relaxed );
- //CDS_COMPILER_RW_BARRIER;
- }
- };
-
- /// Specialization of HPGuardT for hazard_pointer type
- typedef HPGuardT<hazard_pointer> HPGuard;
-
- /// Array of hazard pointers.
- /**
- Array of hazard-pointer. Placing a pointer into this array guards the pointer against reclamation.
- Template parameter \p Count defines the size of hazard pointer array. \p Count parameter should not exceed
- GarbageCollector::getHazardPointerCount().
-
- It is unsafe to use this class directly. Instead, the AutoHPArray should be used.
-
- While creating the object of HPArray class an array of size \p Count of hazard pointers is reserved by
- the HP Manager of current thread. The object's destructor cleans all of reserved hazard pointer and
- returns reserved HP to the HP pool of ThreadGC.
-
- Usually, it is not necessary to create an object of this class. The object of class ThreadGC contains
- the HPArray object and implements interface for HP setting and freeing.
-
- Template parameter:
- \li HazardPointer - type of hazard pointer. It is hazard_pointer usually
- \li Count - capacity of array
-
- */
- template <typename HazardPointer, size_t Count>
- class HPArrayT
- {
- public:
- typedef HazardPointer hazard_ptr_type ; ///< Hazard pointer type
- typedef HPGuardT<hazard_ptr_type> atomic_hazard_ptr ; ///< Element type of the array
- static const size_t c_nCapacity = Count ; ///< Capacity of the array
-
- private:
- //@cond
- atomic_hazard_ptr * m_arr ; ///< Hazard pointer array of size = \p Count
- template <typename OtherHazardPointer, class Allocator> friend class HPAllocator;
- //@endcond
-
- public:
- /// Constructs uninitialized array.
- HPArrayT() CDS_NOEXCEPT
- {}
-
- /// Destructs object
- ~HPArrayT() CDS_NOEXCEPT
- {}
-
- /// Returns max count of hazard pointer for this array
- CDS_CONSTEXPR size_t capacity() const
- {
- return c_nCapacity;
- }
-
- /// Set hazard pointer \p nIndex. 0 <= \p nIndex < \p Count
- void set( size_t nIndex, hazard_ptr_type hzPtr ) CDS_NOEXCEPT
- {
- assert( nIndex < capacity() );
- m_arr[nIndex] = hzPtr;
- }
-
- /// Returns reference to hazard pointer of index \p nIndex (0 <= \p nIndex < \p Count)
- atomic_hazard_ptr& operator []( size_t nIndex ) CDS_NOEXCEPT
- {
- assert( nIndex < capacity() );
- return m_arr[nIndex];
- }
-
- /// Returns reference to hazard pointer of index \p nIndex (0 <= \p nIndex < \p Count) [const version]
- atomic_hazard_ptr& operator []( size_t nIndex ) const CDS_NOEXCEPT
- {
- assert( nIndex < capacity() );
- return m_arr[nIndex];
- }
-
- /// Clears (sets to \p nullptr) hazard pointer \p nIndex
- void clear( size_t nIndex ) CDS_NOEXCEPT
- {
- assert( nIndex < capacity() );
- m_arr[ nIndex ].clear();
- }
- };
-
- /// Specialization of HPArrayT class for hazard_pointer type
- template <size_t Count> using HPArray = HPArrayT<hazard_pointer, Count >;
-
- /// Allocator of hazard pointers for the thread
- /**
- The hazard pointer array is the free-list of unused hazard pointer for the thread.
- The array is managed as a stack.
- The max size (capacity) of array is defined at ctor time and cannot be changed during object's lifetime
-
- Each allocator object is thread-private.
-
- Template parameters:
- \li HazardPointer - type of hazard pointer (hazard_pointer usually)
- \li Allocator - memory allocator class, default is \ref CDS_DEFAULT_ALLOCATOR
-
- This helper class should not be used directly.
- */
- template < typename HazardPointer, class Allocator = CDS_DEFAULT_ALLOCATOR >
- class HPAllocator
- {
- public:
- typedef HazardPointer hazard_ptr_type ; ///< type of hazard pointer
- typedef HPGuardT<hazard_ptr_type> atomic_hazard_ptr ; ///< Atomic hazard pointer type
- typedef Allocator allocator_type ; ///< allocator type
-
- private:
- //@cond
- typedef cds::details::Allocator< atomic_hazard_ptr, allocator_type > allocator_impl;
-
- atomic_hazard_ptr * m_arrHazardPtr ; ///< Array of hazard pointers
- size_t m_nTop ; ///< The top of stack
- const size_t m_nCapacity ; ///< Array capacity
-
- //@endcond
-
- public:
- /// Default ctor
- explicit HPAllocator(
- size_t nCapacity ///< max count of hazard pointer per thread
- )
- : m_arrHazardPtr( alloc_array( nCapacity ) )
- , m_nCapacity( nCapacity )
- {
- make_free();
- }
-
- /// Dtor
- ~HPAllocator()
- {
- allocator_impl().Delete( m_arrHazardPtr, capacity() );
- }
-
- /// Get capacity of array
- size_t capacity() const CDS_NOEXCEPT
- {
- return m_nCapacity;
- }
-
- /// Get size of array. The size is equal to the capacity of array
- size_t size() const CDS_NOEXCEPT
- {
- return capacity();
- }
-
- /// Checks if all items are allocated
- bool isFull() const CDS_NOEXCEPT
- {
- return m_nTop == 0;
- }
-
- /// Allocates hazard pointer
- atomic_hazard_ptr& alloc() CDS_NOEXCEPT
- {
- assert( m_nTop > 0 );
- --m_nTop;
- return m_arrHazardPtr[m_nTop];
- }
-
- /// Frees previously allocated hazard pointer
- void free( atomic_hazard_ptr& hp ) CDS_NOEXCEPT
- {
- assert( m_nTop < capacity() );
- hp.clear();
- ++m_nTop;
- CDS_COMPILER_RW_BARRIER ; // ???
- }
-
- /// Allocates hazard pointers array
- /**
- Allocates \p Count hazard pointers from array \p m_arrHazardPtr
- Returns initialized object \p arr
- */
- template <size_t Count>
- void alloc( HPArrayT<hazard_ptr_type, Count>& arr ) CDS_NOEXCEPT
- {
- assert( m_nTop >= Count );
- m_nTop -= Count;
- arr.m_arr = m_arrHazardPtr + m_nTop;
- }
-
- /// Frees hazard pointer array
- /**
- Frees the array of hazard pointers allocated by previous call \p this->alloc.
- */
- template <size_t Count>
- void free( const HPArrayT<hazard_ptr_type, Count>& arr ) CDS_NOEXCEPT
- {
- assert( m_nTop + Count <= capacity());
- for ( size_t i = m_nTop; i < m_nTop + Count; ++i )
- m_arrHazardPtr[ i ].clear();
- m_nTop += Count;
- }
-
- /// Makes all HP free
- void clear() CDS_NOEXCEPT
- {
- make_free();
- }
-
- /// Returns to i-th hazard pointer
- atomic_hazard_ptr& operator []( size_t i ) CDS_NOEXCEPT
- {
- assert( i < capacity() );
- return m_arrHazardPtr[i];
- }
-
- private:
- //@cond
- void make_free() CDS_NOEXCEPT
- {
- for ( size_t i = 0; i < capacity(); ++i )
- m_arrHazardPtr[ i ].clear();
- m_nTop = capacity();
- }
-
- atomic_hazard_ptr * alloc_array( size_t nCapacity )
- {
- return allocator_impl().NewArray( nCapacity );
- }
- //@endcond
- };
-
- }}} // namespace gc::hzp::details
-} // namespace cds
-//@endcond
-
-#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_ALLOC_H
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_HZP_DETAILS_HP_FWD_H
-#define __CDS_GC_HZP_DETAILS_HP_FWD_H
-
-namespace cds {
- namespace gc { namespace hzp {
-
- // forward declarations
- class GarbageCollector;
- class ThreadGC;
- } }
-}
-
-#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_FWD_H
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_HZP_DETAILS_HP_INLINE_H
-#define __CDS_GC_HZP_DETAILS_HP_INLINE_H
-
-namespace cds {
- namespace gc{ namespace hzp { namespace details {
-
- /************************************************************************/
- /* INLINES */
- /************************************************************************/
- inline retired_vector::retired_vector( const cds::gc::hzp::GarbageCollector& HzpMgr )
- : m_arr( HzpMgr.getMaxRetiredPtrCount() ),
- m_nSize(0)
- {}
-
- inline HPRec::HPRec( const cds::gc::hzp::GarbageCollector& HzpMgr )
- : m_hzp( HzpMgr.getHazardPointerCount() ),
- m_arrRetired( HzpMgr )
- {}
-
- } } } // namespace gc::hzp::details
-} // namespace cds
-
-
-#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_INLINE_H
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_HZP_DETAILS_HP_RETIRED_H
-#define __CDS_GC_HZP_DETAILS_HP_RETIRED_H
-
-#include <cds/gc/hzp/details/hp_fwd.h>
-#include <cds/gc/hzp/details/hp_type.h>
-
-#include <cds/details/bounded_array.h>
-
-namespace cds {
- namespace gc{ namespace hzp { namespace details {
-
- /// Retired pointer
- typedef cds::gc::details::retired_ptr retired_ptr;
-
- /// Array of retired pointers
- /**
- The vector of retired pointer ready to delete.
-
- The Hazard Pointer schema is build on thread-static arrays. For each HP-enabled thread the HP manager allocates
- array of retired pointers. The array belongs to the thread: owner thread writes to the array, other threads
- just read it.
- */
- class retired_vector {
- /// Underlying vector implementation
- typedef cds::details::bounded_array<retired_ptr> retired_vector_impl;
-
- retired_vector_impl m_arr ; ///< the array of retired pointers
- size_t m_nSize ; ///< Current size of \p m_arr
-
- public:
- /// Iterator
- typedef retired_vector_impl::iterator iterator;
-
- /// Constructor
- retired_vector( const cds::gc::hzp::GarbageCollector& HzpMgr ) ; // inline
- ~retired_vector()
- {}
-
- /// Vector capacity.
- /**
- The capacity is constant for any thread. It is defined by cds::gc::hzp::GarbageCollector.
- */
- size_t capacity() const { return m_arr.capacity(); }
-
- /// Current vector size (count of retired pointers in the vector)
- size_t size() const { return m_nSize; }
-
- /// Set vector size. Uses internally
- void size( size_t nSize )
- {
- assert( nSize <= capacity() );
- m_nSize = nSize;
- }
-
- /// Pushes retired pointer to the vector
- void push( const retired_ptr& p )
- {
- assert( m_nSize < capacity() );
- m_arr[ m_nSize ] = p;
- ++m_nSize;
- }
-
- /// Checks if the vector is full (size() == capacity() )
- bool isFull() const
- {
- return m_nSize >= capacity();
- }
-
- /// Begin iterator
- iterator begin() { return m_arr.begin(); }
- /// End iterator
- iterator end() { return m_arr.begin() + m_nSize ; }
-
- /// Clears the vector. After clearing, size() == 0
- void clear()
- {
- m_nSize = 0;
- }
- };
-
- } } } // namespace gc::hzp::details
-} // namespace cds
-
-#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_RETIRED_H
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_HZP_DETAILS_HP_TYPE_H
-#define __CDS_GC_HZP_DETAILS_HP_TYPE_H
-
-#include <cds/gc/details/retired_ptr.h>
-
-namespace cds {
- namespace gc {
- namespace hzp {
-
- /// Hazard pointer
- typedef void * hazard_pointer;
-
- /// Pointer to function to free (destruct and deallocate) retired pointer of specific type
- typedef cds::gc::details::free_retired_ptr_func free_retired_ptr_func;
- }
- }
-}
-
-#endif // #ifndef __CDS_GC_HZP_DETAILS_HP_TYPE_H
-
-
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_HZP_HZP_H
-#define __CDS_GC_HZP_HZP_H
-
-#include <vector>
-#include <cds/cxx11_atomic.h>
-#include <cds/os/thread.h>
-#include <cds/gc/hzp/details/hp_fwd.h>
-#include <cds/gc/hzp/details/hp_alloc.h>
-#include <cds/gc/hzp/details/hp_retired.h>
-
-#if CDS_COMPILER == CDS_COMPILER_MSVC
-# pragma warning(push)
- // warning C4251: 'cds::gc::hzp::GarbageCollector::m_pListHead' : class 'cds::cxx11_atomic::atomic<T>'
- // needs to have dll-interface to be used by clients of class 'cds::gc::hzp::GarbageCollector'
-# pragma warning(disable: 4251)
-#endif
-
-/*
- Editions:
- 2007.12.24 khizmax Add statistics and CDS_GATHER_HAZARDPTR_STAT macro
- 2008.03.06 khizmax Refactoring: implementation of HazardPtrMgr is moved to hazardptr.cpp
- 2008.03.08 khizmax Remove HazardPtrMgr singleton. Now you must initialize/destroy HazardPtrMgr calling
- HazardPtrMgr::Construct / HazardPtrMgr::Destruct before use (usually in main() function).
- 2008.12.06 khizmax Refactoring. Changes class name, namespace hierarchy, all helper defs have been moved to details namespace
- 2010.01.27 khizmax Introducing memory order constraint
-*/
-
-namespace cds {
- /**
- @page cds_garbage_collectors_comparison GC comparison
- @ingroup cds_garbage_collector
-
- <table>
- <tr>
- <th>Feature</th>
- <th>%cds::gc::HP</th>
- <th>%cds::gc::PTB</th>
- </tr>
- <tr>
- <td>Implementation quality</td>
- <td>stable</td>
- <td>stable</td>
- </tr>
- <tr>
- <td>Performance rank (1 - slowest, 5 - fastest)</td>
- <td>5</td>
- <td>4</td>
- </tr>
- <tr>
- <td>Max number of guarded (hazard) pointers per thread</td>
- <td>limited (specifies in GC object ctor)</td>
- <td>unlimited (dynamically allocated when needed)</td>
- </tr>
- <tr>
- <td>Max number of retired pointers<sup>1</sup></td>
- <td>bounded</td>
- <td>bounded</td>
- </tr>
- <tr>
- <td>Array of retired pointers</td>
- <td>preallocated for each thread, limited in size</td>
- <td>global for the entire process, unlimited (dynamically allocated when needed)</td>
- </tr>
- <tr>
- <td>Support direct pointer to item of lock-free container (useful for iterators)</td>
- <td>not supported</td>
- <td>not supported</td>
- </tr>
- </table>
-
- <sup>1</sup>Unbounded count of retired pointer means a possibility of memory exhaustion.
- */
-
- /// Different safe memory reclamation schemas (garbage collectors)
- /** @ingroup cds_garbage_collector
-
- This namespace specifies different safe memory reclamation (SMR) algorithms.
- See \ref cds_garbage_collector "Garbage collectors"
- */
- namespace gc {
-
- /// Michael's Hazard Pointers reclamation schema
- /**
- \par Sources:
- - [2002] Maged M.Michael "Safe memory reclamation for dynamic lock-freeobjects using atomic reads and writes"
- - [2003] Maged M.Michael "Hazard Pointers: Safe memory reclamation for lock-free objects"
- - [2004] Andrei Alexandrescy, Maged Michael "Lock-free Data Structures with Hazard Pointers"
-
-
- The cds::gc::hzp namespace and its members are internal representation of Hazard Pointer GC and should not be used directly.
- Use cds::gc::HP class in your code.
-
- Hazard Pointer garbage collector is a singleton. The main user-level part of Hazard Pointer schema is
- GC class and its nested classes. Before use any HP-related class you must initialize HP garbage collector
- by contructing cds::gc::HP object in beginning of your main().
- See cds::gc::HP class for explanation.
- */
- namespace hzp {
-
- namespace details {
- /// Hazard pointer record of the thread
- /**
- The structure of type "single writer - multiple reader": only the owner thread may write to this structure
- other threads have read-only access.
- */
- struct HPRec {
- HPAllocator<hazard_pointer> m_hzp ; ///< array of hazard pointers. Implicit \ref CDS_DEFAULT_ALLOCATOR dependency
- retired_vector m_arrRetired ; ///< Retired pointer array
-
- /// Ctor
- HPRec( const cds::gc::hzp::GarbageCollector& HzpMgr ) ; // inline
- ~HPRec()
- {}
-
- /// Clears all hazard pointers
- void clear()
- {
- m_hzp.clear();
- }
- };
- } // namespace details
-
- /// GarbageCollector::Scan phase strategy
- /**
- See GarbageCollector::Scan for explanation
- */
- enum scan_type {
- classic, ///< classic scan as described in Michael's works (see GarbageCollector::classic_scan)
- inplace ///< inplace scan without allocation (see GarbageCollector::inplace_scan)
- };
-
- /// Hazard Pointer singleton
- /**
- Safe memory reclamation schema by Michael "Hazard Pointers"
-
- \par Sources:
- \li [2002] Maged M.Michael "Safe memory reclamation for dynamic lock-freeobjects using atomic reads and writes"
- \li [2003] Maged M.Michael "Hazard Pointers: Safe memory reclamation for lock-free objects"
- \li [2004] Andrei Alexandrescy, Maged Michael "Lock-free Data Structures with Hazard Pointers"
-
- */
- class CDS_EXPORT_API GarbageCollector
- {
- public:
- typedef cds::atomicity::event_counter event_counter ; ///< event counter type
-
- /// Internal GC statistics
- struct InternalState {
- size_t nHPCount ; ///< HP count per thread (const)
- size_t nMaxThreadCount ; ///< Max thread count (const)
- size_t nMaxRetiredPtrCount ; ///< Max retired pointer count per thread (const)
- size_t nHPRecSize ; ///< Size of HP record, bytes (const)
-
- size_t nHPRecAllocated ; ///< Count of HP record allocations
- size_t nHPRecUsed ; ///< Count of HP record used
- size_t nTotalRetiredPtrCount ; ///< Current total count of retired pointers
- size_t nRetiredPtrInFreeHPRecs ; ///< Count of retired pointer in free (unused) HP records
-
- event_counter::value_type evcAllocHPRec ; ///< Count of HPRec allocations
- event_counter::value_type evcRetireHPRec ; ///< Count of HPRec retire events
- event_counter::value_type evcAllocNewHPRec; ///< Count of new HPRec allocations from heap
- event_counter::value_type evcDeleteHPRec ; ///< Count of HPRec deletions
-
- event_counter::value_type evcScanCall ; ///< Count of Scan calling
- event_counter::value_type evcHelpScanCall ; ///< Count of HelpScan calling
- event_counter::value_type evcScanFromHelpScan;///< Count of Scan calls from HelpScan
-
- event_counter::value_type evcDeletedNode ; ///< Count of deleting of retired objects
- event_counter::value_type evcDeferredNode ; ///< Count of objects that cannot be deleted in Scan phase because of a hazard_pointer guards it
- };
-
- /// No GarbageCollector object is created
- CDS_DECLARE_EXCEPTION( HZPManagerEmpty, "Global Hazard Pointer GarbageCollector is NULL" );
-
- /// Not enough required Hazard Pointer count
- CDS_DECLARE_EXCEPTION( HZPTooMany, "Not enough required Hazard Pointer count" );
-
- private:
- /// Internal GC statistics
- struct Statistics {
- event_counter m_AllocHPRec ; ///< Count of HPRec allocations
- event_counter m_RetireHPRec ; ///< Count of HPRec retire events
- event_counter m_AllocNewHPRec ; ///< Count of new HPRec allocations from heap
- event_counter m_DeleteHPRec ; ///< Count of HPRec deletions
-
- event_counter m_ScanCallCount ; ///< Count of Scan calling
- event_counter m_HelpScanCallCount ; ///< Count of HelpScan calling
- event_counter m_CallScanFromHelpScan ; ///< Count of Scan calls from HelpScan
-
- event_counter m_DeletedNode ; ///< Count of retired objects deleting
- event_counter m_DeferredNode ; ///< Count of objects that cannot be deleted in Scan phase because of a hazard_pointer guards it
- };
-
- /// Internal list of cds::gc::hzp::details::HPRec
- struct hplist_node: public details::HPRec
- {
- hplist_node * m_pNextNode ; ///< next hazard ptr record in list
- atomics::atomic<OS::ThreadId> m_idOwner ; ///< Owner thread id; 0 - the record is free (not owned)
- atomics::atomic<bool> m_bFree ; ///< true if record if free (not owned)
-
- //@cond
- hplist_node( const GarbageCollector& HzpMgr )
- : HPRec( HzpMgr ),
- m_pNextNode( nullptr ),
- m_idOwner( OS::c_NullThreadId ),
- m_bFree( true )
- {}
-
- ~hplist_node()
- {
- assert( m_idOwner.load( atomics::memory_order_relaxed ) == OS::c_NullThreadId );
- assert( m_bFree.load(atomics::memory_order_relaxed) );
- }
- //@endcond
- };
-
- atomics::atomic<hplist_node *> m_pListHead ; ///< Head of GC list
-
- static GarbageCollector * m_pHZPManager ; ///< GC instance pointer
-
- Statistics m_Stat ; ///< Internal statistics
- bool m_bStatEnabled ; ///< true - statistics enabled
-
- const size_t m_nHazardPointerCount ; ///< max count of thread's hazard pointer
- const size_t m_nMaxThreadCount ; ///< max count of thread
- const size_t m_nMaxRetiredPtrCount ; ///< max count of retired ptr per thread
- scan_type m_nScanType ; ///< scan type (see \ref scan_type enum)
-
-
- private:
- /// Ctor
- GarbageCollector(
- size_t nHazardPtrCount = 0, ///< Hazard pointer count per thread
- size_t nMaxThreadCount = 0, ///< Max count of thread
- size_t nMaxRetiredPtrCount = 0, ///< Capacity of the array of retired objects
- scan_type nScanType = inplace ///< Scan type (see \ref scan_type enum)
- );
-
- /// Dtor
- ~GarbageCollector();
-
- /// Allocate new HP record
- hplist_node * NewHPRec();
-
- /// Permanently deletes HPrecord \p pNode
- /**
- Caveat: for performance reason this function is defined as inline and cannot be called directly
- */
- void DeleteHPRec( hplist_node * pNode );
-
- /// Permanently deletes retired pointer \p p
- /**
- Caveat: for performance reason this function is defined as inline and cannot be called directly
- */
- void DeletePtr( details::retired_ptr& p );
-
- //@cond
- void detachAllThread();
- //@endcond
-
- public:
- /// Creates GarbageCollector singleton
- /**
- GC is the singleton. If GC instance is not exist then the function creates the instance.
- Otherwise it does nothing.
-
- The Michael's HP reclamation schema depends of three parameters:
-
- \p nHazardPtrCount - HP pointer count per thread. Usually it is small number (2-4) depending from
- the data structure algorithms. By default, if \p nHazardPtrCount = 0,
- the function uses maximum of HP count for CDS library.
-
- \p nMaxThreadCount - max count of thread with using HP GC in your application. Default is 100.
-
- \p nMaxRetiredPtrCount - capacity of array of retired pointers for each thread. Must be greater than
- \p nHazardPtrCount * \p nMaxThreadCount.
- Default is 2 * \p nHazardPtrCount * \p nMaxThreadCount.
- */
- static void CDS_STDCALL Construct(
- size_t nHazardPtrCount = 0, ///< Hazard pointer count per thread
- size_t nMaxThreadCount = 0, ///< Max count of simultaneous working thread in your application
- size_t nMaxRetiredPtrCount = 0, ///< Capacity of the array of retired objects for the thread
- scan_type nScanType = inplace ///< Scan type (see \ref scan_type enum)
- );
-
- /// Destroys global instance of GarbageCollector
- /**
- The parameter \p bDetachAll should be used carefully: if its value is \p true,
- then the destroying GC automatically detaches all attached threads. This feature
- can be useful when you have no control over the thread termination, for example,
- when \p libcds is injected into existing external thread.
- */
- static void CDS_STDCALL Destruct(
- bool bDetachAll = false ///< Detach all threads
- );
-
- /// Returns pointer to GarbageCollector instance
- static GarbageCollector& instance()
- {
- if ( !m_pHZPManager )
- throw HZPManagerEmpty();
- return *m_pHZPManager;
- }
-
- /// Checks if global GC object is constructed and may be used
- static bool isUsed()
- {
- return m_pHZPManager != nullptr;
- }
-
- /// Returns max Hazard Pointer count defined in construction time
- size_t getHazardPointerCount() const { return m_nHazardPointerCount; }
-
- /// Returns max thread count defined in construction time
- size_t getMaxThreadCount() const { return m_nMaxThreadCount; }
-
- /// Returns max size of retired objects array. It is defined in construction time
- size_t getMaxRetiredPtrCount() const { return m_nMaxRetiredPtrCount; }
-
- // Internal statistics
-
- /// Get internal statistics
- InternalState& getInternalState(InternalState& stat) const;
-
- /// Checks if internal statistics enabled
- bool isStatisticsEnabled() const { return m_bStatEnabled; }
-
- /// Enables/disables internal statistics
- bool enableStatistics( bool bEnable )
- {
- bool bEnabled = m_bStatEnabled;
- m_bStatEnabled = bEnable;
- return bEnabled;
- }
-
- /// Checks that required hazard pointer count \p nRequiredCount is less or equal then max hazard pointer count
- /**
- If \p nRequiredCount > getHazardPointerCount() then the exception HZPTooMany is thrown
- */
- static void checkHPCount( unsigned int nRequiredCount )
- {
- if ( instance().getHazardPointerCount() < nRequiredCount )
- throw HZPTooMany();
- }
-
- /// Get current scan strategy
- scan_type getScanType() const
- {
- return m_nScanType;
- }
-
- /// Set current scan strategy
- /** @anchor hzp_gc_setScanType
- Scan strategy changing is allowed on the fly.
- */
- void setScanType(
- scan_type nScanType ///< new scan strategy
- )
- {
- m_nScanType = nScanType;
- }
-
- public: // Internals for threads
-
- /// Allocates Hazard Pointer GC record. For internal use only
- details::HPRec * AllocateHPRec();
-
- /// Free HP record. For internal use only
- void RetireHPRec( details::HPRec * pRec );
-
- /// The main garbage collecting function
- /**
- This function is called internally by ThreadGC object when upper bound of thread's list of reclaimed pointers
- is reached.
-
- There are the following scan algorithm:
- - \ref hzp_gc_classic_scan "classic_scan" allocates memory for internal use
- - \ref hzp_gc_inplace_scan "inplace_scan" does not allocate any memory
-
- Use \ref hzp_gc_setScanType "setScanType" member function to setup appropriate scan algorithm.
- */
- void Scan( details::HPRec * pRec )
- {
- switch ( m_nScanType ) {
- case inplace:
- inplace_scan( pRec );
- break;
- default:
- assert(false) ; // Forgotten something?..
- case classic:
- classic_scan( pRec );
- break;
- }
- }
-
- /// Helper scan routine
- /**
- The function guarantees that every node that is eligible for reuse is eventually freed, barring
- thread failures. To do so, after executing Scan, a thread executes a HelpScan,
- where it checks every HP record. If an HP record is inactive, the thread moves all "lost" reclaimed pointers
- to thread's list of reclaimed pointers.
-
- The function is called internally by Scan.
- */
- void HelpScan( details::HPRec * pThis );
-
- protected:
- /// Classic scan algorithm
- /** @anchor hzp_gc_classic_scan
- Classical scan algorithm as described in Michael's paper.
-
- A scan includes four stages. The first stage involves scanning the array HP for non-null values.
- Whenever a non-null value is encountered, it is inserted in a local list of currently protected pointer.
- Only stage 1 accesses shared variables. The following stages operate only on private variables.
-
- The second stage of a scan involves sorting local list of protected pointers to allow
- binary search in the third stage.
-
- The third stage of a scan involves checking each reclaimed node
- against the pointers in local list of protected pointers. If the binary search yields
- no match, the node is freed. Otherwise, it cannot be deleted now and must kept in thread's list
- of reclaimed pointers.
-
- The forth stage prepares new thread's private list of reclaimed pointers
- that could not be freed during the current scan, where they remain until the next scan.
-
- This algorithm allocates memory for internal HP array.
-
- This function is called internally by ThreadGC object when upper bound of thread's list of reclaimed pointers
- is reached.
- */
- void classic_scan( details::HPRec * pRec );
-
- /// In-place scan algorithm
- /** @anchor hzp_gc_inplace_scan
- Unlike the \ref hzp_gc_classic_scan "classic_scan" algorithm, \p inplace_scan does not allocate any memory.
- All operations are performed in-place.
- */
- void inplace_scan( details::HPRec * pRec );
- };
-
- /// Thread's hazard pointer manager
- /**
- To use Hazard Pointer reclamation schema each thread object must be linked with the object of ThreadGC class
- that interacts with GarbageCollector global object. The linkage is performed by calling \ref cds_threading "cds::threading::Manager::attachThread()"
- on the start of each thread that uses HP GC. Before terminating the thread linked to HP GC it is necessary to call
- \ref cds_threading "cds::threading::Manager::detachThread()".
- */
- class ThreadGC
- {
- GarbageCollector& m_HzpManager ; ///< Hazard Pointer GC singleton
- details::HPRec * m_pHzpRec ; ///< Pointer to thread's HZP record
-
- public:
- /// Default constructor
- ThreadGC()
- : m_HzpManager( GarbageCollector::instance() ),
- m_pHzpRec( nullptr )
- {}
-
- /// The object is not copy-constructible
- ThreadGC( ThreadGC const& ) = delete;
-
- ~ThreadGC()
- {
- fini();
- }
-
- /// Checks if thread GC is initialized
- bool isInitialized() const { return m_pHzpRec != nullptr; }
-
- /// Initialization. Repeat call is available
- void init()
- {
- if ( !m_pHzpRec )
- m_pHzpRec = m_HzpManager.AllocateHPRec();
- }
-
- /// Finalization. Repeat call is available
- void fini()
- {
- if ( m_pHzpRec ) {
- details::HPRec * pRec = m_pHzpRec;
- m_pHzpRec = nullptr;
- m_HzpManager.RetireHPRec( pRec );
- }
- }
-
- /// Initializes HP guard \p guard
- details::HPGuard& allocGuard()
- {
- assert( m_pHzpRec );
- return m_pHzpRec->m_hzp.alloc();
- }
-
- /// Frees HP guard \p guard
- void freeGuard( details::HPGuard& guard )
- {
- assert( m_pHzpRec );
- m_pHzpRec->m_hzp.free( guard );
- }
-
- /// Initializes HP guard array \p arr
- template <size_t Count>
- void allocGuard( details::HPArray<Count>& arr )
- {
- assert( m_pHzpRec );
- m_pHzpRec->m_hzp.alloc( arr );
- }
-
- /// Frees HP guard array \p arr
- template <size_t Count>
- void freeGuard( details::HPArray<Count>& arr )
- {
- assert( m_pHzpRec );
- m_pHzpRec->m_hzp.free( arr );
- }
-
- /// Places retired pointer \p and its deleter \p pFunc into thread's array of retired pointer for deferred reclamation
- template <typename T>
- void retirePtr( T * p, void (* pFunc)(T *) )
- {
- retirePtr( details::retired_ptr( reinterpret_cast<void *>( p ), reinterpret_cast<free_retired_ptr_func>( pFunc ) ) );
- }
-
- /// Places retired pointer \p into thread's array of retired pointer for deferred reclamation
- void retirePtr( const details::retired_ptr& p )
- {
- m_pHzpRec->m_arrRetired.push( p );
-
- if ( m_pHzpRec->m_arrRetired.isFull() ) {
- // Max of retired pointer count is reached. Do scan
- scan();
- }
- }
-
- //@cond
- void scan()
- {
- m_HzpManager.Scan( m_pHzpRec );
- m_HzpManager.HelpScan( m_pHzpRec );
- }
- //@endcond
- };
-
- /// Auto HPGuard.
- /**
- This class encapsulates Hazard Pointer guard to protect a pointer against deletion .
- It allocates one HP from thread's HP array in constructor and free the HP allocated in destruction time.
- */
- class AutoHPGuard
- {
- //@cond
- details::HPGuard& m_hp ; ///< Hazard pointer guarded
- ThreadGC& m_gc ; ///< Thread GC
- //@endcond
-
- public:
- typedef details::HPGuard::hazard_ptr hazard_ptr ; ///< Hazard pointer type
- public:
- /// Allocates HP guard from \p gc
- AutoHPGuard( ThreadGC& gc )
- : m_hp( gc.allocGuard() )
- , m_gc( gc )
- {}
-
- /// Allocates HP guard from \p gc and protects the pointer \p p of type \p T
- template <typename T>
- AutoHPGuard( ThreadGC& gc, T * p )
- : m_hp( gc.allocGuard() )
- , m_gc( gc )
- {
- m_hp = p;
- }
-
- /// Frees HP guard. The pointer guarded may be deleted after this.
- ~AutoHPGuard()
- {
- m_gc.freeGuard( m_hp );
- }
-
- /// Returns thread GC
- ThreadGC& getGC() const
- {
- return m_gc;
- }
-
- /// Protects the pointer \p p against reclamation (guards the pointer).
- template <typename T>
- T * operator =( T * p )
- {
- return m_hp = p;
- }
-
- //@cond
- std::nullptr_t operator =(std::nullptr_t)
- {
- return m_hp = nullptr;
- }
-
- hazard_ptr get() const
- {
- return m_hp;
- }
- //@endcond
- };
-
- /// Auto-managed array of hazard pointers
- /**
- This class is wrapper around cds::gc::hzp::details::HPArray class.
- \p Count is the size of HP array
- */
- template <size_t Count>
- class AutoHPArray: public details::HPArray<Count>
- {
- ThreadGC& m_mgr ; ///< Thread GC
-
- public:
- /// Rebind array for other size \p COUNT2
- template <size_t Count2>
- struct rebind {
- typedef AutoHPArray<Count2> other ; ///< rebinding result
- };
-
- public:
- /// Allocates array of HP guard from \p mgr
- AutoHPArray( ThreadGC& mgr )
- : m_mgr( mgr )
- {
- mgr.allocGuard( *this );
- }
-
- /// Frees array of HP guard
- ~AutoHPArray()
- {
- m_mgr.freeGuard( *this );
- }
-
- /// Returns thread GC
- ThreadGC& getGC() const { return m_mgr; }
- };
-
- } // namespace hzp
-}} // namespace cds::gc
-
-// Inlines
-#include <cds/gc/hzp/details/hp_inline.h>
-
-#if CDS_COMPILER == CDS_COMPILER_MSVC
-# pragma warning(pop)
-#endif
-
-#endif // #ifndef __CDS_GC_HZP_HZP_H
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_PTB_H
-#define __CDS_GC_PTB_H
-
-#include <cds/gc/ptb_decl.h>
-#include <cds/gc/ptb_impl.h>
-#include <cds/details/lib.h>
-
-#endif // #ifndef __CDS_GC_PTB_H
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_PTB_DECL_H
-#define __CDS_GC_PTB_DECL_H
-
-#include <cds/gc/ptb/ptb.h>
-#include <cds/details/marked_ptr.h>
-#include <cds/details/static_functor.h>
-
-namespace cds { namespace gc {
-
- /// Pass-the-Buck garbage collector
- /** @ingroup cds_garbage_collector
- @headerfile cds/gc/ptb.h
- This class is a wrapper for Pass-the-Buck garbage collector internal implementation.
-
- Sources:
- - [2002] M. Herlihy, V. Luchangco, and M. Moir. The repeat offender problem: A mechanism for supporting
- dynamic-sized lockfree data structures. Technical Report TR-2002-112, Sun Microsystems Laboratories, 2002
- - [2002] M. Herlihy, V. Luchangco, P. Martin, and M. Moir. Dynamic-sized Lockfree Data Structures.
- Technical Report TR-2002-110, Sun Microsystems Laboratories, 2002
- - [2005] M. Herlihy, V. Luchangco, P. Martin, and M. Moir. Nonblocking Memory Management Support
- for Dynamic_Sized Data Structures. ACM Transactions on Computer Systems, Vol.23, No.2, May 2005
-
- See \ref cds_how_to_use "How to use" section for details of garbage collector applying.
- */
- class PTB
- {
- public:
- /// Native guarded pointer type
- typedef void * guarded_pointer;
-
- /// Atomic reference
- /**
- @headerfile cds/gc/ptb.h
- */
- template <typename T> using atomic_ref = atomics::atomic<T *>;
-
- /// Atomic type
- /**
- @headerfile cds/gc/ptb.h
- */
- template <typename T> using atomic_type = atomics::atomic<T>;
-
- /// Atomic marked pointer
- /**
- @headerfile cds/gc/ptb.h
- */
- template <typename MarkedPtr> using atomic_marked_ptr = atomics::atomic<MarkedPtr>;
-
- /// Thread GC implementation for internal usage
- typedef ptb::ThreadGC thread_gc_impl;
-
- /// Wrapper for ptb::ThreadGC class
- /**
- @headerfile cds/gc/ptb.h
- This class performs automatically attaching/detaching Pass-the-Buck GC
- for the current thread.
- */
- class thread_gc: public thread_gc_impl
- {
- //@cond
- bool m_bPersistent;
- //@endcond
- public:
- /// Constructor
- /**
- The constructor attaches the current thread to the Pass-the-Buck GC
- if it is not yet attached.
- The \p bPersistent parameter specifies attachment persistence:
- - \p true - the class destructor will not detach the thread from Pass-the-Buck GC.
- - \p false (default) - the class destructor will detach the thread from Pass-the-Buck GC.
- */
- thread_gc(
- bool bPersistent = false
- ) ; // inline in ptb_impl.h
-
- /// Destructor
- /**
- If the object has been created in persistent mode, the destructor does nothing.
- Otherwise it detaches the current thread from Pass-the-Buck GC.
- */
- ~thread_gc() ; // inline in ptb_impl.h
- };
-
-
- /// Pass-the-Buck guard
- /**
- @headerfile cds/gc/ptb.h
- This class is a wrapper for ptb::Guard.
- */
- class Guard: public ptb::Guard
- {
- //@cond
- typedef ptb::Guard base_class;
- //@endcond
-
- public:
- //@cond
- Guard() ; // inline in ptb_impl.h
- //@endcond
-
- /// Protects a pointer of type <tt> atomic<T*> </tt>
- /**
- Return the value of \p toGuard
-
- The function tries to load \p toGuard and to store it
- to the HP slot repeatedly until the guard's value equals \p toGuard
- */
- template <typename T>
- T protect( atomics::atomic<T> const& toGuard )
- {
- T pCur = toGuard.load(atomics::memory_order_relaxed);
- T pRet;
- do {
- pRet = assign( pCur );
- pCur = toGuard.load(atomics::memory_order_acquire);
- } while ( pRet != pCur );
- return pCur;
- }
-
- /// Protects a converted pointer of type <tt> atomic<T*> </tt>
- /**
- Return the value of \p toGuard
-
- The function tries to load \p toGuard and to store result of \p f functor
- to the HP slot repeatedly until the guard's value equals \p toGuard.
-
- The function is useful for intrusive containers when \p toGuard is a node pointer
- that should be converted to a pointer to the value type before guarding.
- The parameter \p f of type Func is a functor that makes this conversion:
- \code
- struct functor {
- value_type * operator()( T * p );
- };
- \endcode
- Really, the result of <tt> f( toGuard.load() ) </tt> is assigned to the hazard pointer.
- */
- template <typename T, class Func>
- T protect( atomics::atomic<T> const& toGuard, Func f )
- {
- T pCur = toGuard.load(atomics::memory_order_relaxed);
- T pRet;
- do {
- pRet = pCur;
- assign( f( pCur ) );
- pCur = toGuard.load(atomics::memory_order_acquire);
- } while ( pRet != pCur );
- return pCur;
- }
-
- /// Store \p p to the guard
- /**
- The function equals to a simple assignment, no loop is performed.
- Can be used for a pointer that cannot be changed concurrently.
- */
- template <typename T>
- T * assign( T * p )
- {
- return base_class::operator =(p);
- }
-
- //@cond
- std::nullptr_t assign( std::nullptr_t )
- {
- return base_class::operator =(nullptr);
- }
- //@endcond
-
- /// Store marked pointer \p p to the guard
- /**
- The function equals to a simple assignment of <tt>p.ptr()</tt>, no loop is performed.
- Can be used for a marked pointer that cannot be changed concurrently.
- */
- template <typename T, int BITMASK>
- T * assign( cds::details::marked_ptr<T, BITMASK> p )
- {
- return base_class::operator =( p.ptr() );
- }
-
- /// Copy from \p src guard to \p this guard
- void copy( Guard const& src )
- {
- assign( src.get_native() );
- }
-
- /// Clear value of the guard
- void clear()
- {
- base_class::clear();
- }
-
- /// Get the value currently protected (relaxed read)
- template <typename T>
- T * get() const
- {
- return reinterpret_cast<T *>( get_native() );
- }
-
- /// Get native guarded pointer stored
- guarded_pointer get_native() const
- {
- return base_class::get_guard()->pPost.load(atomics::memory_order_relaxed);
- }
-
- };
-
- /// Array of Pass-the-Buck guards
- /**
- @headerfile cds/gc/ptb.h
- This class is a wrapper for ptb::GuardArray template.
- Template parameter \p Count defines the size of PTB array.
- */
- template <size_t Count>
- class GuardArray: public ptb::GuardArray<Count>
- {
- //@cond
- typedef ptb::GuardArray<Count> base_class;
- //@endcond
- public:
- /// Rebind array for other size \p COUNT2
- template <size_t OtherCount>
- struct rebind {
- typedef GuardArray<OtherCount> other ; ///< rebinding result
- };
-
- public:
- //@cond
- GuardArray() ; // inline in ptb_impl.h
- //@endcond
-
- /// Protects a pointer of type \p atomic<T*>
- /**
- Return the value of \p toGuard
-
- The function tries to load \p toGuard and to store it
- to the slot \p nIndex repeatedly until the guard's value equals \p toGuard
- */
- template <typename T>
- T protect(size_t nIndex, atomics::atomic<T> const& toGuard )
- {
- T pRet;
- do {
- pRet = assign( nIndex, toGuard.load(atomics::memory_order_relaxed) );
- } while ( pRet != toGuard.load(atomics::memory_order_acquire));
-
- return pRet;
- }
-
- /// Protects a pointer of type \p atomic<T*>
- /**
- Return the value of \p toGuard
-
- The function tries to load \p toGuard and to store it
- to the slot \p nIndex repeatedly until the guard's value equals \p toGuard
-
- The function is useful for intrusive containers when \p toGuard is a node pointer
- that should be converted to a pointer to the value type before guarding.
- The parameter \p f of type Func is a functor that makes this conversion:
- \code
- struct functor {
- value_type * operator()( T * p );
- };
- \endcode
- Really, the result of <tt> f( toGuard.load() ) </tt> is assigned to the hazard pointer.
- */
- template <typename T, class Func>
- T protect(size_t nIndex, atomics::atomic<T> const& toGuard, Func f )
- {
- T pRet;
- do {
- assign( nIndex, f( pRet = toGuard.load(atomics::memory_order_relaxed) ));
- } while ( pRet != toGuard.load(atomics::memory_order_acquire));
-
- return pRet;
- }
-
- /// Store \p to the slot \p nIndex
- /**
- The function equals to a simple assignment, no loop is performed.
- */
- template <typename T>
- T * assign( size_t nIndex, T * p )
- {
- base_class::set(nIndex, p);
- return p;
- }
-
- /// Store marked pointer \p p to the guard
- /**
- The function equals to a simple assignment of <tt>p.ptr()</tt>, no loop is performed.
- Can be used for a marked pointer that cannot be changed concurrently.
- */
- template <typename T, int Bitmask>
- T * assign( size_t nIndex, cds::details::marked_ptr<T, Bitmask> p )
- {
- return assign( nIndex, p.ptr() );
- }
-
- /// Copy guarded value from \p src guard to slot at index \p nIndex
- void copy( size_t nIndex, Guard const& src )
- {
- assign( nIndex, src.get_native() );
- }
-
- /// Copy guarded value from slot \p nSrcIndex to slot at index \p nDestIndex
- void copy( size_t nDestIndex, size_t nSrcIndex )
- {
- assign( nDestIndex, get_native( nSrcIndex ));
- }
-
- /// Clear value of the slot \p nIndex
- void clear( size_t nIndex)
- {
- base_class::clear( nIndex );
- }
-
- /// Get current value of slot \p nIndex
- template <typename T>
- T * get( size_t nIndex) const
- {
- return reinterpret_cast<T *>( get_native( nIndex ) );
- }
-
- /// Get native guarded pointer stored
- guarded_pointer get_native( size_t nIndex ) const
- {
- return base_class::operator[](nIndex).get_guard()->pPost.load(atomics::memory_order_relaxed);
- }
-
- /// Capacity of the guard array
- static CDS_CONSTEXPR size_t capacity()
- {
- return Count;
- }
- };
-
- public:
- /// Initializes ptb::GarbageCollector singleton
- /**
- The constructor calls GarbageCollector::Construct with passed parameters.
- See ptb::GarbageCollector::Construct for explanation of parameters meaning.
- */
- PTB(
- size_t nLiberateThreshold = 1024
- , size_t nInitialThreadGuardCount = 8
- )
- {
- ptb::GarbageCollector::Construct(
- nLiberateThreshold,
- nInitialThreadGuardCount
- );
- }
-
- /// Terminates ptb::GarbageCollector singleton
- /**
- The destructor calls \code ptb::GarbageCollector::Destruct() \endcode
- */
- ~PTB()
- {
- ptb::GarbageCollector::Destruct();
- }
-
- /// Checks if count of hazard pointer is no less than \p nCountNeeded
- /**
- The function always returns \p true since the guard count is unlimited for
- PTB garbage collector.
- */
- static bool check_available_guards( size_t nCountNeeded, bool /*bRaiseException*/ = true )
- {
- CDS_UNUSED( nCountNeeded );
- return true;
- }
-
- /// Retire pointer \p p with function \p pFunc
- /**
- The function places pointer \p p to array of pointers ready for removing.
- (so called retired pointer array). The pointer can be safely removed when no guarded pointer points to it.
- Deleting the pointer is the function \p pFunc call.
- */
- template <typename T>
- static void retire( T * p, void (* pFunc)(T *) )
- {
- ptb::GarbageCollector::instance().retirePtr( p, pFunc );
- }
-
- /// Retire pointer \p p with functor of type \p Disposer
- /**
- The function places pointer \p p to array of pointers ready for removing.
- (so called retired pointer array). The pointer can be safely removed when no guarded pointer points to it.
-
- See gc::HP::retire for \p Disposer requirements.
- */
- template <class Disposer, typename T>
- static void retire( T * p )
- {
- retire( p, cds::details::static_functor<Disposer, T>::call );
- }
-
- /// Checks if Pass-the-Buck GC is constructed and may be used
- static bool isUsed()
- {
- return ptb::GarbageCollector::isUsed();
- }
-
- /// Forced GC cycle call for current thread
- /**
- Usually, this function should not be called directly.
- */
- static void scan() ; // inline in ptb_impl.h
-
- /// Synonym for \ref scan()
- static void force_dispose()
- {
- scan();
- }
- };
-
-}} // namespace cds::gc
-
-#endif // #ifndef __CDS_GC_PTB_DECL_H
+++ /dev/null
-//$$CDS-header$$
-
-#ifndef __CDS_GC_PTB_IMPL_H
-#define __CDS_GC_PTB_IMPL_H
-
-#include <cds/threading/model.h>
-
-//@cond
-namespace cds { namespace gc {
-
- inline PTB::thread_gc::thread_gc(
- bool bPersistent
- )
- : m_bPersistent( bPersistent )
- {
- if ( !cds::threading::Manager::isThreadAttached() )
- cds::threading::Manager::attachThread();
- }
-
- inline PTB::thread_gc::~thread_gc()
- {
- if ( !m_bPersistent )
- cds::threading::Manager::detachThread();
- }
-
- inline PTB::Guard::Guard()
- : Guard::base_class( cds::threading::getGC<PTB>() )
- {}
-
- template <size_t COUNT>
- inline PTB::GuardArray<COUNT>::GuardArray()
- : GuardArray::base_class( cds::threading::getGC<PTB>() )
- {}
-
- inline void PTB::scan()
- {
- cds::threading::getGC<PTB>().scan();
- }
-
-}} // namespace cds::gc
-//@endcond
-
-#endif // #ifndef __CDS_GC_PTB_IMPL_H
#define __CDS_THREADING__COMMON_H
#include <cds/gc/hp_decl.h>
-#include <cds/gc/ptb_decl.h>
+#include <cds/gc/dhp_decl.h>
#include <cds/urcu/details/gp_decl.h>
#include <cds/urcu/details/sh_decl.h>
<ClInclude Include="..\..\..\cds\details\lib.h" />\r
<ClInclude Include="..\..\..\cds\details\static_functor.h" />\r
<ClInclude Include="..\..\..\cds\gc\dhp.h" />\r
+ <ClInclude Include="..\..\..\cds\gc\dhp_decl.h" />\r
+ <ClInclude Include="..\..\..\cds\gc\dhp_impl.h" />\r
<ClInclude Include="..\..\..\cds\gc\gc_fwd.h" />\r
<ClInclude Include="..\..\..\cds\gc\guarded_ptr.h" />\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_alloc.h" />\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_fwd.h" />\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_inline.h" />\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_retired.h" />\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_type.h" />\r
+ <ClInclude Include="..\..\..\cds\gc\hp\hzp.h" />\r
<ClInclude Include="..\..\..\cds\gc\hp_decl.h" />\r
<ClInclude Include="..\..\..\cds\gc\hp_impl.h" />\r
- <ClInclude Include="..\..\..\cds\gc\ptb_decl.h" />\r
- <ClInclude Include="..\..\..\cds\gc\ptb_impl.h" />\r
<ClInclude Include="..\..\..\cds\intrusive\basket_queue.h" />\r
<ClInclude Include="..\..\..\cds\intrusive\cuckoo_set.h" />\r
<ClInclude Include="..\..\..\cds\intrusive\details\base.h" />\r
<ClInclude Include="..\..\..\cds\gc\default_gc.h" />\r
<ClInclude Include="..\..\..\cds\gc\hp.h" />\r
<ClInclude Include="..\..\..\cds\gc\nogc.h" />\r
- <ClInclude Include="..\..\..\cds\gc\ptb.h" />\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_alloc.h" />\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_fwd.h" />\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_inline.h" />\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_retired.h" />\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_type.h" />\r
- <ClInclude Include="..\..\..\cds\gc\hzp\hzp.h" />\r
<ClInclude Include="..\..\..\cds\gc\ptb\ptb.h" />\r
<ClInclude Include="..\..\..\cds\gc\details\retired_ptr.h" />\r
<ClInclude Include="..\..\..\cds\user_setup\allocator.h" />\r
<Filter Include="Header Files\cds\gc">\r
<UniqueIdentifier>{a3c9928d-5261-4593-a8b9-728235f7056f}</UniqueIdentifier>\r
</Filter>\r
- <Filter Include="Header Files\cds\gc\hzp">\r
- <UniqueIdentifier>{21a6c665-7381-45b8-9f03-b21f3da5503d}</UniqueIdentifier>\r
- </Filter>\r
<Filter Include="Header Files\cds\gc\ptb">\r
<UniqueIdentifier>{53d28ee4-5fe9-4fa1-a617-53d8b0628eac}</UniqueIdentifier>\r
</Filter>\r
<Filter Include="Header Files\cds\container\impl">\r
<UniqueIdentifier>{0a2328b4-ff6f-4afb-8de0-9884ae172fa9}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="Header Files\cds\gc\hp">\r
+ <UniqueIdentifier>{043c4eba-3bd4-4226-b214-e26f18b422a1}</UniqueIdentifier>\r
+ </Filter>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="..\..\..\src\dllmain.cpp">\r
<ClInclude Include="..\..\..\cds\gc\nogc.h">\r
<Filter>Header Files\cds\gc</Filter>\r
</ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\ptb.h">\r
- <Filter>Header Files\cds\gc</Filter>\r
- </ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_alloc.h">\r
- <Filter>Header Files\cds\gc\hzp</Filter>\r
- </ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_fwd.h">\r
- <Filter>Header Files\cds\gc\hzp</Filter>\r
- </ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_inline.h">\r
- <Filter>Header Files\cds\gc\hzp</Filter>\r
- </ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_retired.h">\r
- <Filter>Header Files\cds\gc\hzp</Filter>\r
- </ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\hzp\details\hp_type.h">\r
- <Filter>Header Files\cds\gc\hzp</Filter>\r
- </ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\hzp\hzp.h">\r
- <Filter>Header Files\cds\gc\hzp</Filter>\r
- </ClInclude>\r
<ClInclude Include="..\..\..\cds\gc\ptb\ptb.h">\r
<Filter>Header Files\cds\gc\ptb</Filter>\r
</ClInclude>\r
<ClInclude Include="..\..\..\cds\gc\hp_impl.h">\r
<Filter>Header Files\cds\gc</Filter>\r
</ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\ptb_decl.h">\r
- <Filter>Header Files\cds\gc</Filter>\r
- </ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\ptb_impl.h">\r
- <Filter>Header Files\cds\gc</Filter>\r
- </ClInclude>\r
<ClInclude Include="..\..\..\cds\compiler\cxx11_atomic.h">\r
<Filter>Header Files\cds\compiler</Filter>\r
</ClInclude>\r
<ClInclude Include="..\..\..\cds\container\details\split_list_base.h">\r
<Filter>Header Files\cds\container\details</Filter>\r
</ClInclude>\r
- <ClInclude Include="..\..\..\cds\gc\dhp.h">\r
- <Filter>Header Files\cds\gc</Filter>\r
- </ClInclude>\r
<ClInclude Include="..\..\..\cds\container\michael_list_dhp.h">\r
<Filter>Header Files\cds\container</Filter>\r
</ClInclude>\r
<ClInclude Include="..\..\..\cds\container\ellen_bintree_set_dhp.h">\r
<Filter>Header Files\cds\container</Filter>\r
</ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\dhp.h">\r
+ <Filter>Header Files\cds\gc</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\dhp_decl.h">\r
+ <Filter>Header Files\cds\gc</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\dhp_impl.h">\r
+ <Filter>Header Files\cds\gc</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_alloc.h">\r
+ <Filter>Header Files\cds\gc\hp</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_fwd.h">\r
+ <Filter>Header Files\cds\gc\hp</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_inline.h">\r
+ <Filter>Header Files\cds\gc\hp</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_retired.h">\r
+ <Filter>Header Files\cds\gc\hp</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\hp\details\hp_type.h">\r
+ <Filter>Header Files\cds\gc\hp</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\cds\gc\hp\hzp.h">\r
+ <Filter>Header Files\cds\gc\hp</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
2008.02.10 Maxim.Khiszinsky Created
*/
-#include <cds/gc/hzp/hzp.h>
+#include <cds/gc/hp/hp.h>
#include <algorithm> // std::sort
#include "hzp_const.h"
#include <cds/init.h>
#include <cds/gc/hp.h>
-#include <cds/gc/ptb.h>
+#include <cds/gc/dhp.h>
#include <cds/urcu/general_instant.h>
#include <cds/urcu/general_buffered.h>
#include <cds/urcu/general_threaded.h>