From: khizmax Date: Sat, 15 Nov 2014 15:08:49 +0000 (+0300) Subject: rearrange cds/gc directory X-Git-Tag: v2.0.0~85 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=ef671b8b2db9357d83de1a0fae3044f1d2b9d6f7;p=libcds.git rearrange cds/gc directory --- diff --git a/cds/gc/dhp.h b/cds/gc/dhp.h index 6209e815..35f7d871 100644 --- a/cds/gc/dhp.h +++ b/cds/gc/dhp.h @@ -3,8 +3,8 @@ #ifndef __CDS_GC_DHP_H #define __CDS_GC_DHP_H -#include -#include +#include +#include #include #endif // #ifndef __CDS_GC_DHP_H diff --git a/cds/gc/dhp/dhp_decl.h b/cds/gc/dhp/dhp_decl.h deleted file mode 100644 index 05e5f058..00000000 --- a/cds/gc/dhp/dhp_decl.h +++ /dev/null @@ -1,413 +0,0 @@ -//$$CDS-header$$ - -#ifndef __CDS_GC_DHP_DHP_DECL_H -#define __CDS_GC_DHP_DHP_DECL_H - -#include -#include -#include - -namespace cds { namespace gc { - - /// Dynamic Hazard Pointer garbage collector - /** @ingroup cds_garbage_collector - @headerfile cds/gc/dhp.h - This class is a wrapper for Dynamic Hazard Pointer garbage collector internal implementation. - - See \ref cds_how_to_use "How to use" section for details of garbage collector applying. - */ - class DHP - { - public: - /// Native guarded pointer type - typedef void * guarded_pointer; - - /// Atomic reference - /** - @headerfile cds/gc/dhp.h - */ - template using atomic_ref = atomics::atomic; - - /// Atomic type - /** - @headerfile cds/gc/dhp.h - */ - template using atomic_type = atomics::atomic; - - /// Atomic marked pointer - /** - @headerfile cds/gc/dhp.h - */ - template using atomic_marked_ptr = atomics::atomic; - - /// Thread GC implementation for internal usage - typedef dhp::ThreadGC thread_gc_impl; - - /// Wrapper for dhp::ThreadGC class - /** - @headerfile cds/gc/dhp.h - This class performs automatically attaching/detaching Dynamic Hazard Pointer 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 Dynamic Hazard Pointer 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 Dynamic Hazard Pointer GC. - - \p false (default) - the class destructor will detach the thread from Dynamic Hazard Pointer GC. - */ - thread_gc( - bool bPersistent = false - ) ; // inline in dhp_impl.h - - /// Destructor - /** - If the object has been created in persistent mode, the destructor does nothing. - Otherwise it detaches the current thread from Dynamic Hazard Pointer GC. - */ - ~thread_gc() ; // inline in dhp_impl.h - }; - - - /// Dynamic Hazard Pointer guard - /** - @headerfile cds/gc/dhp.h - This class is a wrapper for dhp::Guard. - */ - class Guard: public dhp::Guard - { - //@cond - typedef dhp::Guard base_class; - //@endcond - - public: - //@cond - Guard() ; // inline in dhp_impl.h - //@endcond - - /// Protects a pointer of type atomic - /** - 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 - T protect( atomics::atomic 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 atomic - /** - 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 f( toGuard.load() ) is assigned to the hazard pointer. - */ - template - T protect( atomics::atomic 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 - 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 p.ptr(), no loop is performed. - Can be used for a marked pointer that cannot be changed concurrently. - */ - template - T * assign( cds::details::marked_ptr 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 - T * get() const - { - return reinterpret_cast( 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 Dynamic Hazard Pointer guards - /** - @headerfile cds/gc/dhp.h - This class is a wrapper for dhp::GuardArray template. - Template parameter \p Count defines the size of DHP array. - */ - template - class GuardArray: public dhp::GuardArray - { - //@cond - typedef dhp::GuardArray base_class; - //@endcond - public: - /// Rebind array for other size \p COUNT2 - template - struct rebind { - typedef GuardArray other ; ///< rebinding result - }; - - public: - //@cond - GuardArray() ; // inline in dhp_impl.h - //@endcond - - /// Protects a pointer of type \p atomic - /** - 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 - T protect(size_t nIndex, atomics::atomic 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 - /** - 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 f( toGuard.load() ) is assigned to the hazard pointer. - */ - template - T protect(size_t nIndex, atomics::atomic 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 - 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 p.ptr(), no loop is performed. - Can be used for a marked pointer that cannot be changed concurrently. - */ - template - T * assign( size_t nIndex, cds::details::marked_ptr 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 - T * get( size_t nIndex) const - { - return reinterpret_cast( 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 dhp::GarbageCollector singleton - /** - The constructor calls GarbageCollector::Construct with passed parameters. - See dhp::GarbageCollector::Construct for explanation of parameters meaning. - */ - DHP( - size_t nLiberateThreshold = 1024 - , size_t nInitialThreadGuardCount = 8 - ) - { - dhp::GarbageCollector::Construct( - nLiberateThreshold, - nInitialThreadGuardCount - ); - } - - /// Terminates dhp::GarbageCollector singleton - /** - The destructor calls \code dhp::GarbageCollector::Destruct() \endcode - */ - ~DHP() - { - dhp::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 - DHP 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 - static void retire( T * p, void (* pFunc)(T *) ) - { - dhp::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 - static void retire( T * p ) - { - retire( p, cds::details::static_functor::call ); - } - - /// Checks if Dynamic Hazard Pointer GC is constructed and may be used - static bool isUsed() - { - return dhp::GarbageCollector::isUsed(); - } - - /// Forced GC cycle call for current thread - /** - Usually, this function should not be called directly. - */ - static void scan() ; // inline in dhp_impl.h - - /// Synonym for \ref scan() - static void force_dispose() - { - scan(); - } - }; - -}} // namespace cds::gc - -#endif // #ifndef __CDS_GC_DHP_DHP_DECL_H diff --git a/cds/gc/dhp/dhp_impl.h b/cds/gc/dhp/dhp_impl.h deleted file mode 100644 index a21db87a..00000000 --- a/cds/gc/dhp/dhp_impl.h +++ /dev/null @@ -1,43 +0,0 @@ -//$$CDS-header$$ - -#ifndef __CDS_GC_DHP_DHP_IMPL_H -#define __CDS_GC_DHP_DHP_IMPL_H - -#include - -//@cond -namespace cds { namespace gc { - - inline DHP::thread_gc::thread_gc( - bool bPersistent - ) - : m_bPersistent( bPersistent ) - { - if ( !cds::threading::Manager::isThreadAttached() ) - cds::threading::Manager::attachThread(); - } - - inline DHP::thread_gc::~thread_gc() - { - if ( !m_bPersistent ) - cds::threading::Manager::detachThread(); - } - - inline DHP::Guard::Guard() - : Guard::base_class( cds::threading::getGC() ) - {} - - template - inline DHP::GuardArray::GuardArray() - : GuardArray::base_class( cds::threading::getGC() ) - {} - - inline void DHP::scan() - { - cds::threading::getGC().scan(); - } - -}} // namespace cds::gc -//@endcond - -#endif // #ifndef __CDS_GC_DHP_DHP_IMPL_H diff --git a/cds/gc/hp.h b/cds/gc/hp.h index 54618eab..eea63e4e 100644 --- a/cds/gc/hp.h +++ b/cds/gc/hp.h @@ -3,8 +3,8 @@ #ifndef __CDS_GC_HP_H #define __CDS_GC_HP_H -#include -#include +#include +#include #include #endif // #ifndef __CDS_GC_HP_H diff --git a/cds/gc/hp/hp_decl.h b/cds/gc/hp/hp_decl.h deleted file mode 100644 index 9de3da4e..00000000 --- a/cds/gc/hp/hp_decl.h +++ /dev/null @@ -1,504 +0,0 @@ -//$$CDS-header$$ - -#ifndef __CDS_GC_HP_HP_DECL_H -#define __CDS_GC_HP_HP_DECL_H - -#include // overflow_error -#include -#include - -namespace cds { namespace gc { - /// @defgroup cds_garbage_collector Garbage collectors - - /// Hazard Pointer garbage collector - /** @ingroup cds_garbage_collector - @headerfile cds/gc/hp.h - - This class realizes a wrapper for Hazard Pointer garbage collector internal implementation. - - 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" - - See \ref cds_how_to_use "How to use" section for details of garbage collector applying. - */ - class HP - { - public: - /// Native guarded pointer type - typedef gc::hp::hazard_pointer guarded_pointer; - - /// Atomic reference - /** - @headerfile cds/gc/hp.h - */ - template using atomic_ref = atomics::atomic; - - /// Atomic marked pointer - /** - @headerfile cds/gc/hp.h - */ - template using atomic_marked_ptr = atomics::atomic; - - /// Atomic type - /** - @headerfile cds/gc/hp.h - */ - template using atomic_type = atomics::atomic; - - /// Thread GC implementation for internal usage - typedef hp::ThreadGC thread_gc_impl; - - /// Wrapper for hp::ThreadGC class - /** - @headerfile cds/gc/hp.h - This class performs automatically attaching/detaching Hazard Pointer 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 Hazard Pointer 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 Hazard Pointer GC. - - \p false (default) - the class destructor will detach the thread from Hazard Pointer GC. - */ - thread_gc( - bool bPersistent = false - ) ; //inline in hp_impl.h - - /// Destructor - /** - If the object has been created in persistent mode, the destructor does nothing. - Otherwise it detaches the current thread from Hazard Pointer GC. - */ - ~thread_gc() ; // inline in hp_impl.h - }; - - /// Hazard Pointer guard - /** - @headerfile cds/gc/hp.h - This class is a wrapper for \p hp::guard. - */ - class Guard : public hp::guard - { - //@cond - typedef hp::guard base_class; - //@endcond - - public: - //@cond - Guard() ; // inline in hp_impl.h - //@endcond - - /// Protects a pointer of type \p atomic - /** - 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 - T protect( atomics::atomic 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 \p atomic - /** - 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 protecting. - 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 f( toGuard.load() ) is assigned to the hazard pointer. - */ - template - T protect( atomics::atomic 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 the value \p p to guard, no loop is performed. - Can be used for a pointer that cannot be changed concurrently - */ - template - T * assign( T * p ) - { - return base_class::operator =(p); - } - - //@cond - std::nullptr_t assign( std::nullptr_t ) - { - return base_class::operator =(nullptr); - } - //@endcond - - /// Copy from \p src guard to \p this guard - void copy( Guard const& src ) - { - assign( src.get_native() ); - } - - /// Store marked pointer \p p to the guard - /** - The function equals to a simple assignment of p.ptr(), no loop is performed. - Can be used for a marked pointer that cannot be changed concurrently. - */ - template - T * assign( cds::details::marked_ptr p ) - { - return base_class::operator =( p.ptr() ); - } - - /// Clear value of the guard - void clear() - { - assign( nullptr ); - } - - /// Get the value currently protected - template - T * get() const - { - return reinterpret_cast( get_native() ); - } - - /// Get native hazard pointer stored - guarded_pointer get_native() const - { - return base_class::get(); - } - }; - - /// Array of Hazard Pointer guards - /** - @headerfile cds/gc/hp.h - This class is a wrapper for \p hp::array template. - Template parameter \p Count defines the size of HP array. - */ - template - class GuardArray : public hp::array - { - //@cond - typedef hp::array base_class; - //@endcond - public: - /// Rebind array for other size \p Count2 - template - struct rebind { - typedef GuardArray other ; ///< rebinding result - }; - - public: - //@cond - GuardArray() ; // inline in hp_impl.h - //@endcond - /// Protects a pointer of type \p atomic - /** - 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 - T protect(size_t nIndex, atomics::atomic const& toGuard ) - { - T pRet; - do { - pRet = assign( nIndex, toGuard.load(atomics::memory_order_acquire) ); - } while ( pRet != toGuard.load(atomics::memory_order_relaxed)); - - return pRet; - } - - /// Protects a pointer of type \p atomic - /** - 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 f( toGuard.load() ) is assigned to the hazard pointer. - */ - template - T protect(size_t nIndex, atomics::atomic const& toGuard, Func f ) - { - T pRet; - do { - assign( nIndex, f( pRet = toGuard.load(atomics::memory_order_acquire) )); - } while ( pRet != toGuard.load(atomics::memory_order_relaxed)); - - return pRet; - } - - /// Store \p to the slot \p nIndex - /** - The function equals to a simple assignment, no loop is performed. - */ - template - 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 p.ptr(), no loop is performed. - Can be used for a marked pointer that cannot be changed concurrently. - */ - template - T * assign( size_t nIndex, cds::details::marked_ptr 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 - T * get( size_t nIndex) const - { - return reinterpret_cast( get_native( nIndex ) ); - } - - /// Get native hazard pointer stored - guarded_pointer get_native( size_t nIndex ) const - { - return base_class::operator[](nIndex).get(); - } - - /// Capacity of the guard array - static CDS_CONSTEXPR size_t capacity() - { - return Count; - } - }; - - public: - /// Initializes hp::GarbageCollector singleton - /** - The constructor initializes GC singleton with passed parameters. - 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 - hazard pointer count per thread. Usually it is small number (up to 10) depending from - the data structure algorithms. By default, if \p nHazardPtrCount = 0, the function - uses maximum of the hazard pointer count for CDS library. - - \p nMaxThreadCount - max count of thread with using Hazard Pointer GC in your application. Default is 100. - - \p nMaxRetiredPtrCount - capacity of array of retired pointers for each thread. Must be greater than - nHazardPtrCount * nMaxThreadCount . Default is 2 * nHazardPtrCount * nMaxThreadCount . - */ - HP( - 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 - hp::scan_type nScanType = hp::inplace ///< Scan type (see \ref hp::scan_type enum) - ) - { - hp::GarbageCollector::Construct( - nHazardPtrCount, - nMaxThreadCount, - nMaxRetiredPtrCount, - nScanType - ); - } - - /// Terminates GC singleton - /** - The destructor calls \code hp::GarbageCollector::Destruct( true ) \endcode - */ - ~HP() - { - hp::GarbageCollector::Destruct( true ); - } - - /// Checks if count of hazard pointer is no less than \p nCountNeeded - /** - If \p bRaiseException is \p true (that is the default), the function raises - an \p std::overflow_error exception "Too few hazard pointers" - if \p nCountNeeded is more than the count of hazard pointer per thread. - */ - static bool check_available_guards( size_t nCountNeeded, bool bRaiseException = true ) - { - if ( hp::GarbageCollector::instance().getHazardPointerCount() < nCountNeeded ) { - if ( bRaiseException ) - throw std::overflow_error( "Too few hazard pointers" ); - return false; - } - return true; - } - - /// Returns max Hazard Pointer count - size_t max_hazard_count() const - { - return hp::GarbageCollector::instance().getHazardPointerCount(); - } - - /// Returns max count of thread - size_t max_thread_count() const - { - return hp::GarbageCollector::instance().getMaxThreadCount(); - } - - /// Returns capacity of retired pointer array - size_t retired_array_capacity() const - { - return hp::GarbageCollector::instance().getMaxRetiredPtrCount(); - } - - /// 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 hazard pointer points to it. - Deleting the pointer is the function \p pFunc call. - */ - template - static void retire( T * p, void (* pFunc)(T *) ) ; // inline in hp_impl.h - - /// 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 hazard pointer points to it. - - Deleting the pointer is an invocation of some object of type \p Disposer; the interface of \p Disposer is: - \code - template - struct disposer { - void operator()( T * p ) ; // disposing operator - }; - \endcode - Since the functor call can happen at any time after \p retire call, additional restrictions are imposed to \p Disposer type: - - it should be stateless functor - - it should be default-constructible - - the result of functor call with argument \p p should not depend on where the functor will be called. - - \par Examples: - Operator \p delete functor: - \code - template - struct disposer { - void operator ()( T * p ) { - delete p; - } - }; - - // How to call GC::retire method - int * p = new int; - - // ... use p in lock-free manner - - cds::gc::HP::retire( p ) ; // place p to retired pointer array of HP GC - \endcode - - Functor based on \p std::allocator : - \code - template > - struct disposer { - template - void operator()( T * p ) { - typedef typename ALLOC::templare rebind::other alloc_t; - alloc_t a; - a.destroy( p ); - a.deallocate( p, 1 ); - } - }; - \endcode - */ - template - static void retire( T * p ) ; // inline in hp_impl.h - - /// Get current scan strategy - hp::scan_type getScanType() const - { - return hp::GarbageCollector::instance().getScanType(); - } - - /// Set current scan strategy - void setScanType( - hp::scan_type nScanType ///< new scan strategy - ) - { - hp::GarbageCollector::instance().setScanType( nScanType ); - } - - /// Checks if Hazard Pointer GC is constructed and may be used - static bool isUsed() - { - return hp::GarbageCollector::isUsed(); - } - - - /// Forced GC cycle call for current thread - /** - Usually, this function should not be called directly. - */ - static void scan() ; // inline in hp_impl.h - - /// Synonym for \ref scan() - static void force_dispose() - { - scan(); - } - }; -}} // namespace cds::gc - -#endif // #ifndef __CDS_GC_HP_HP_DECL_H diff --git a/cds/gc/hp/hp_impl.h b/cds/gc/hp/hp_impl.h deleted file mode 100644 index b78e1607..00000000 --- a/cds/gc/hp/hp_impl.h +++ /dev/null @@ -1,57 +0,0 @@ -//$$CDS-header$$ - -#ifndef __CDS_GC_HP_HP_IMPL_H -#define __CDS_GC_HP_HP_IMPL_H - -#include -#include - -//@cond -namespace cds { namespace gc { - - inline HP::thread_gc::thread_gc( - bool bPersistent - ) - : m_bPersistent( bPersistent ) - { - if ( !threading::Manager::isThreadAttached() ) - threading::Manager::attachThread(); - } - - inline HP::thread_gc::~thread_gc() - { - if ( !m_bPersistent ) - cds::threading::Manager::detachThread(); - } - - inline HP::Guard::Guard() - : Guard::base_class( cds::threading::getGC() ) - {} - - template - inline HP::GuardArray::GuardArray() - : GuardArray::base_class( cds::threading::getGC() ) - {} - - template - inline void HP::retire( T * p, void (* pFunc)(T *) ) - { - cds::threading::getGC().retirePtr( p, pFunc ); - } - - template - inline void HP::retire( T * p ) - { - cds::threading::getGC().retirePtr( p, cds::details::static_functor::call ); - } - - inline void HP::scan() - { - cds::threading::getGC().scan(); - } - - -}} // namespace cds::gc -//@endcond - -#endif // #ifndef __CDS_GC_HP_HP_IMPL_H diff --git a/cds/gc/impl/dhp_decl.h b/cds/gc/impl/dhp_decl.h new file mode 100644 index 00000000..9c26fd87 --- /dev/null +++ b/cds/gc/impl/dhp_decl.h @@ -0,0 +1,450 @@ +//$$CDS-header$$ + +#ifndef __CDS_GC_DHP_DHP_DECL_H +#define __CDS_GC_DHP_DHP_DECL_H + +#include +#include +#include + +namespace cds { namespace gc { + + /// Dynamic Hazard Pointer garbage collector + /** @ingroup cds_garbage_collector + @headerfile cds/gc/dhp.h + + Implementation of Dynamic Hazard Pointer garbage collector. + + 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" + + Dynamic Hazard Pointers SMR (safe memory reclamation) provides an unbounded number of hazard pointer per thread + despite of classic Hazard Pointer SMR in which the count of the hazard pointef per thread is limited. + + See \ref cds_how_to_use "How to use" section for details how to apply garbage collector. + */ + class DHP + { + public: + /// Native guarded pointer type + /** + @headerfile cds/gc/dhp.h + */ + typedef void * guarded_pointer; + + /// Atomic reference + /** + @headerfile cds/gc/dhp.h + */ + template using atomic_ref = atomics::atomic; + + /// Atomic type + /** + @headerfile cds/gc/dhp.h + */ + template using atomic_type = atomics::atomic; + + /// Atomic marked pointer + /** + @headerfile cds/gc/dhp.h + */ + template using atomic_marked_ptr = atomics::atomic; + + /// Thread GC implementation for internal usage + /** + @headerfile cds/gc/dhp.h + */ + typedef dhp::ThreadGC thread_gc_impl; + + /// Thread-level garbage collector + /** + @headerfile cds/gc/dhp.h + This class performs automatically attaching/detaching Dynamic Hazard Pointer 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 Dynamic Hazard Pointer 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 Dynamic Hazard Pointer GC. + - \p false (default) - the class destructor will detach the thread from Dynamic Hazard Pointer GC. + */ + thread_gc( + bool bPersistent = false + ) ; // inline in dhp_impl.h + + /// Destructor + /** + If the object has been created in persistent mode, the destructor does nothing. + Otherwise it detaches the current thread from Dynamic Hazard Pointer GC. + */ + ~thread_gc() ; // inline in dhp_impl.h + }; + + + /// Dynamic Hazard Pointer guard + /** + @headerfile cds/gc/dhp.h + + A guard is the hazard pointer. + Additionally, the \p %Guard class manages allocation and deallocation of the hazard pointer + + A \p %Guard object is not copy- and move-constructible + and not copy- and move-assignable. + */ + class Guard: public dhp::Guard + { + //@cond + typedef dhp::Guard base_class; + //@endcond + + public: + // Default ctor + Guard() CDS_NOEXCEPT; // inline in dhp_impl.h + + //@cond + Guard( Guard const& ) = delete; + Guard( Guard&& s ) = delete; + Guard& operator=(Guard const&) = delete; + Guard& operator=(Guard&&) = delete; + //@endcond + + /// Protects a pointer of type atomic + /** + 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 + T protect( atomics::atomic const& toGuard ) CDS_NOEXCEPT + { + 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 atomic + /** + 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 f( toGuard.load() ) is assigned to the hazard pointer. + */ + template + T protect( atomics::atomic const& toGuard, Func f ) CDS_NOEXCEPT_( f( toGuard.load( atomics::memory_order_relaxed ))) + { + 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 is just an assignment, no loop is performed. + Can be used for a pointer that cannot be changed concurrently + or for already guarded pointer. + */ + template + T * assign( T * p ) CDS_NOEXCEPT + { + return base_class::operator =(p); + } + + //@cond + std::nullptr_t assign( std::nullptr_t ) CDS_NOEXCEPT + { + return base_class::operator =(nullptr); + } + //@endcond + + /// Store marked pointer \p p to the guard + /** + The function is just an assignment of p.ptr(), no loop is performed. + Can be used for a marked pointer that cannot be changed concurrently + or for already guarded pointer. + */ + template + T * assign( cds::details::marked_ptr p ) CDS_NOEXCEPT + { + return base_class::operator =( p.ptr() ); + } + + /// Copy from \p src guard to \p this guard + void copy( Guard const& src ) CDS_NOEXCEPT + { + assign( src.get_native() ); + } + + /// Clears value of the guard + void clear() CDS_NOEXCEPT + { + base_class::clear(); + } + + /// Gets the value currently protected (relaxed read) + template + T * get() const CDS_NOEXCEPT + { + return reinterpret_cast( get_native() ); + } + + /// Gets native guarded pointer stored + guarded_pointer get_native() const CDS_NOEXCEPT + { + return base_class::get_guard()->pPost.load(atomics::memory_order_relaxed); + } + }; + + /// Array of Dynamic Hazard Pointer guards + /** + @headerfile cds/gc/dhp.h + The class is intended for allocating an array of hazard pointer guards. + Template parameter \p Count defines the size of the array. + + A \p %GuardArray object is not copy- and move-constructible + and not copy- and move-assignable. + */ + template + class GuardArray: public dhp::GuardArray + { + //@cond + typedef dhp::GuardArray base_class; + //@endcond + public: + /// Rebind array for other size \p OtherCount + template + struct rebind { + typedef GuardArray other ; ///< rebinding result + }; + + public: + // Default ctor + GuardArray() CDS_NOEXCEPT; // inline in dhp_impl.h + + //@cond + GuardArray( GuardArray const& ) = delete; + GuardArray( GuardArray&& ) = delete; + GuardArray& operator=(GuardArray const&) = delete; + GuardArray& operator-(GuardArray&&) = delete; + //@endcond + + /// Protects a pointer of type \p atomic + /** + 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 + T protect( size_t nIndex, atomics::atomic const& toGuard ) CDS_NOEXCEPT + { + 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 + /** + 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 to make that conversion: + \code + struct functor { + value_type * operator()( T * p ); + }; + \endcode + Actually, the result of f( toGuard.load() ) is assigned to the hazard pointer. + */ + template + T protect( size_t nIndex, atomics::atomic const& toGuard, Func f ) CDS_NOEXCEPT_( f( toGuard.load( atomics::memory_order_relaxed ))) + { + 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 p to the slot \p nIndex + /** + The function is just an assignment, no loop is performed. + */ + template + T * assign( size_t nIndex, T * p ) CDS_NOEXCEPT + { + base_class::set(nIndex, p); + return p; + } + + /// Store marked pointer \p p to the guard + /** + The function is just an assignment of p.ptr(), no loop is performed. + Can be used for a marked pointer that cannot be changed concurrently + or for already guarded pointer. + */ + template + T * assign( size_t nIndex, cds::details::marked_ptr p ) CDS_NOEXCEPT + { + 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 ) CDS_NOEXCEPT + { + 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 ) CDS_NOEXCEPT + { + assign( nDestIndex, get_native( nSrcIndex )); + } + + /// Clear value of the slot \p nIndex + void clear( size_t nIndex ) CDS_NOEXCEPT + { + base_class::clear( nIndex ); + } + + /// Get current value of slot \p nIndex + template + T * get( size_t nIndex ) const CDS_NOEXCEPT + { + return reinterpret_cast( get_native( nIndex ) ); + } + + /// Get native guarded pointer stored + guarded_pointer get_native( size_t nIndex ) const CDS_NOEXCEPT + { + return base_class::operator[](nIndex).get_guard()->pPost.load(atomics::memory_order_relaxed); + } + + /// Capacity of the guard array + static CDS_CONSTEXPR size_t capacity() CDS_NOEXCEPT + { + return Count; + } + }; + + public: + /// Initializes dhp::GarbageCollector singleton + /** + The constructor calls GarbageCollector::Construct with passed parameters. + See dhp::GarbageCollector::Construct for explanation of parameters meaning. + */ + DHP( + size_t nLiberateThreshold = 1024 + , size_t nInitialThreadGuardCount = 8 + ) + { + dhp::GarbageCollector::Construct( + nLiberateThreshold, + nInitialThreadGuardCount + ); + } + + /// Terminates dhp::GarbageCollector singleton + /** + The destructor calls \code dhp::GarbageCollector::Destruct() \endcode + */ + ~DHP() + { + dhp::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 + DHP garbage collector. + */ + static CDS_CONSTEXPR 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 + static void retire( T * p, void (* pFunc)(T *) ) + { + dhp::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 + static void retire( T * p ) + { + retire( p, cds::details::static_functor::call ); + } + + /// Checks if Dynamic Hazard Pointer GC is constructed and may be used + static bool isUsed() + { + return dhp::GarbageCollector::isUsed(); + } + + /// Forced GC cycle call for current thread + /** + Usually, this function should not be called directly. + */ + static void scan() ; // inline in dhp_impl.h + + /// Synonym for \ref scan() + static void force_dispose() + { + scan(); + } + }; + +}} // namespace cds::gc + +#endif // #ifndef __CDS_GC_DHP_DHP_DECL_H diff --git a/cds/gc/impl/dhp_impl.h b/cds/gc/impl/dhp_impl.h new file mode 100644 index 00000000..10b15f8e --- /dev/null +++ b/cds/gc/impl/dhp_impl.h @@ -0,0 +1,43 @@ +//$$CDS-header$$ + +#ifndef __CDS_GC_DHP_DHP_IMPL_H +#define __CDS_GC_DHP_DHP_IMPL_H + +#include + +//@cond +namespace cds { namespace gc { + + inline DHP::thread_gc::thread_gc( + bool bPersistent + ) + : m_bPersistent( bPersistent ) + { + if ( !cds::threading::Manager::isThreadAttached() ) + cds::threading::Manager::attachThread(); + } + + inline DHP::thread_gc::~thread_gc() + { + if ( !m_bPersistent ) + cds::threading::Manager::detachThread(); + } + + inline DHP::Guard::Guard() CDS_NOEXCEPT + : Guard::base_class( cds::threading::getGC() ) + {} + + template + inline DHP::GuardArray::GuardArray() CDS_NOEXCEPT + : GuardArray::base_class( cds::threading::getGC() ) + {} + + inline void DHP::scan() + { + cds::threading::getGC().scan(); + } + +}} // namespace cds::gc +//@endcond + +#endif // #ifndef __CDS_GC_DHP_DHP_IMPL_H diff --git a/cds/gc/impl/hp_decl.h b/cds/gc/impl/hp_decl.h new file mode 100644 index 00000000..2beb88af --- /dev/null +++ b/cds/gc/impl/hp_decl.h @@ -0,0 +1,531 @@ +//$$CDS-header$$ + +#ifndef __CDS_GC_HP_HP_DECL_H +#define __CDS_GC_HP_HP_DECL_H + +#include // overflow_error +#include +#include + +namespace cds { namespace gc { + /// @defgroup cds_garbage_collector Garbage collectors + + /// Hazard Pointer garbage collector + /** @ingroup cds_garbage_collector + @headerfile cds/gc/hp.h + + Implementation of classic Hazard Pointer garbage collector. + + 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" + + See \ref cds_how_to_use "How to use" section for details how to apply garbage collector. + */ + class HP + { + public: + /// Native guarded pointer type + /** + @headerfile cds/gc/hp.h + */ + typedef gc::hp::hazard_pointer guarded_pointer; + + /// Atomic reference + /** + @headerfile cds/gc/hp.h + */ + template using atomic_ref = atomics::atomic; + + /// Atomic marked pointer + /** + @headerfile cds/gc/hp.h + */ + template using atomic_marked_ptr = atomics::atomic; + + /// Atomic type + /** + @headerfile cds/gc/hp.h + */ + template using atomic_type = atomics::atomic; + + /// Thread GC implementation for internal usage + /** + @headerfile cds/gc/hp.h + */ + typedef hp::ThreadGC thread_gc_impl; + + /// Wrapper for hp::ThreadGC class + /** + @headerfile cds/gc/hp.h + This class performs automatically attaching/detaching Hazard Pointer 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 Hazard Pointer 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 Hazard Pointer GC. + - \p false (default) - the class destructor will detach the thread from Hazard Pointer GC. + */ + thread_gc( + bool bPersistent = false + ) ; //inline in hp_impl.h + + /// Destructor + /** + If the object has been created in persistent mode, the destructor does nothing. + Otherwise it detaches the current thread from Hazard Pointer GC. + */ + ~thread_gc() ; // inline in hp_impl.h + }; + + /// Hazard Pointer guard + /** + @headerfile cds/gc/hp.h + + A guard is the hazard pointer. + Additionally, the \p %Guard class manages allocation and deallocation of the hazard pointer + + A \p %Guard object is not copy- and move-constructible + and not copy- and move-assignable. + */ + class Guard : public hp::guard + { + //@cond + typedef hp::guard base_class; + //@endcond + + public: + /// Default ctor + Guard() CDS_NOEXCEPT; // inline in hp_impl.h + + //@cond + Guard( Guard const& ) = delete; + Guard( Guard&& s ) = delete; + Guard& operator=(Guard const&) = delete; + Guard& operator=(Guard&&) = delete; + //@endcond + + /// Protects a pointer of type \p atomic + /** + 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 + T protect( atomics::atomic const& toGuard ) CDS_NOEXCEPT + { + 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 \p atomic + /** + 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 protecting. + 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 f( toGuard.load() ) is assigned to the hazard pointer. + */ + template + T protect( atomics::atomic const& toGuard, Func f ) CDS_NOEXCEPT_( f( toGuard.load( atomics::memory_order_relaxed ) ) ) + { + 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 the value \p p to guard, no loop is performed. + Can be used for a pointer that cannot be changed concurrently + */ + template + T * assign( T * p ) CDS_NOEXCEPT + { + return base_class::operator =(p); + } + + //@cond + std::nullptr_t assign( std::nullptr_t ) CDS_NOEXCEPT + { + return base_class::operator =(nullptr); + } + //@endcond + + /// Copy from \p src guard to \p this guard + void copy( Guard const& src ) CDS_NOEXCEPT + { + assign( src.get_native() ); + } + + /// Store marked pointer \p p to the guard + /** + The function equals to a simple assignment of p.ptr(), no loop is performed. + Can be used for a marked pointer that cannot be changed concurrently. + */ + template + T * assign( cds::details::marked_ptr p ) CDS_NOEXCEPT + { + return base_class::operator =( p.ptr() ); + } + + /// Clear value of the guard + void clear() CDS_NOEXCEPT + { + assign( nullptr ); + } + + /// Get the value currently protected + template + T * get() const CDS_NOEXCEPT + { + return reinterpret_cast( get_native() ); + } + + /// Get native hazard pointer stored + guarded_pointer get_native() const CDS_NOEXCEPT + { + return base_class::get(); + } + }; + + /// Array of Hazard Pointer guards + /** + @headerfile cds/gc/hp.h + The class is intended for allocating an array of hazard pointer guards. + Template parameter \p Count defines the size of the array. + + A \p %GuardArray object is not copy- and move-constructible + and not copy- and move-assignable. + */ + template + class GuardArray : public hp::array + { + //@cond + typedef hp::array base_class; + //@endcond + public: + /// Rebind array for other size \p Count2 + template + struct rebind { + typedef GuardArray other ; ///< rebinding result + }; + + public: + /// Default ctor + GuardArray() CDS_NOEXCEPT; // inline in hp_impl.h + + //@cond + GuardArray( GuardArray const& ) = delete; + GuardArray( GuardArray&& ) = delete; + GuardArray& operator=(GuardArray const&) = delete; + GuardArray& operator-(GuardArray&&) = delete; + //@endcond + + /// Protects a pointer of type \p atomic + /** + 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 + T protect( size_t nIndex, atomics::atomic const& toGuard ) CDS_NOEXCEPT + { + T pRet; + do { + pRet = assign( nIndex, toGuard.load(atomics::memory_order_acquire) ); + } while ( pRet != toGuard.load(atomics::memory_order_relaxed)); + + return pRet; + } + + /// Protects a pointer of type \p atomic + /** + 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 f( toGuard.load() ) is assigned to the hazard pointer. + */ + template + T protect( size_t nIndex, atomics::atomic const& toGuard, Func f ) CDS_NOEXCEPT_( f( toGuard.load( atomics::memory_order_acquire ))) + { + T pRet; + do { + assign( nIndex, f( pRet = toGuard.load(atomics::memory_order_acquire) )); + } while ( pRet != toGuard.load(atomics::memory_order_relaxed)); + + return pRet; + } + + /// Store \p to the slot \p nIndex + /** + The function equals to a simple assignment, no loop is performed. + */ + template + T * assign( size_t nIndex, T * p ) CDS_NOEXCEPT + { + base_class::set(nIndex, p); + return p; + } + + /// Store marked pointer \p p to the guard + /** + The function equals to a simple assignment of p.ptr(), no loop is performed. + Can be used for a marked pointer that cannot be changed concurrently. + */ + template + T * assign( size_t nIndex, cds::details::marked_ptr p ) CDS_NOEXCEPT + { + 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 ) CDS_NOEXCEPT + { + 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 ) CDS_NOEXCEPT + { + assign( nDestIndex, get_native( nSrcIndex )); + } + + /// Clear value of the slot \p nIndex + void clear( size_t nIndex ) CDS_NOEXCEPT + { + base_class::clear( nIndex ); + } + + /// Get current value of slot \p nIndex + template + T * get( size_t nIndex ) const CDS_NOEXCEPT + { + return reinterpret_cast( get_native( nIndex ) ); + } + + /// Get native hazard pointer stored + guarded_pointer get_native( size_t nIndex ) const CDS_NOEXCEPT + { + return base_class::operator[](nIndex).get(); + } + + /// Capacity of the guard array + static CDS_CONSTEXPR size_t capacity() CDS_NOEXCEPT + { + return Count; + } + }; + + public: + /// Initializes hp::GarbageCollector singleton + /** + The constructor initializes GC singleton with passed parameters. + 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 - hazard pointer count per thread. Usually it is small number (up to 10) depending from + the data structure algorithms. By default, if \p nHazardPtrCount = 0, the function + uses maximum of the hazard pointer count for CDS library. + - \p nMaxThreadCount - max count of thread with using Hazard Pointer GC in your application. Default is 100. + - \p nMaxRetiredPtrCount - capacity of array of retired pointers for each thread. Must be greater than + nHazardPtrCount * nMaxThreadCount . Default is 2 * nHazardPtrCount * nMaxThreadCount . + */ + HP( + 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 + hp::scan_type nScanType = hp::inplace ///< Scan type (see \ref hp::scan_type enum) + ) + { + hp::GarbageCollector::Construct( + nHazardPtrCount, + nMaxThreadCount, + nMaxRetiredPtrCount, + nScanType + ); + } + + /// Terminates GC singleton + /** + The destructor calls \code hp::GarbageCollector::Destruct( true ) \endcode + */ + ~HP() + { + hp::GarbageCollector::Destruct( true ); + } + + /// Checks if count of hazard pointer is no less than \p nCountNeeded + /** + If \p bRaiseException is \p true (that is the default), the function raises + an \p std::overflow_error exception "Too few hazard pointers" + if \p nCountNeeded is more than the count of hazard pointer per thread. + */ + static bool check_available_guards( size_t nCountNeeded, bool bRaiseException = true ) + { + if ( hp::GarbageCollector::instance().getHazardPointerCount() < nCountNeeded ) { + if ( bRaiseException ) + throw std::overflow_error( "Too few hazard pointers" ); + return false; + } + return true; + } + + /// Returns max Hazard Pointer count + size_t max_hazard_count() const + { + return hp::GarbageCollector::instance().getHazardPointerCount(); + } + + /// Returns max count of thread + size_t max_thread_count() const + { + return hp::GarbageCollector::instance().getMaxThreadCount(); + } + + /// Returns capacity of retired pointer array + size_t retired_array_capacity() const + { + return hp::GarbageCollector::instance().getMaxRetiredPtrCount(); + } + + /// 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 hazard pointer points to it. + Deleting the pointer is the function \p pFunc call. + */ + template + static void retire( T * p, void (* pFunc)(T *) ) ; // inline in hp_impl.h + + /// 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 hazard pointer points to it. + + Deleting the pointer is an invocation of some object of type \p Disposer; the interface of \p Disposer is: + \code + template + struct disposer { + void operator()( T * p ) ; // disposing operator + }; + \endcode + Since the functor call can happen at any time after \p retire call, additional restrictions are imposed to \p Disposer type: + - it should be stateless functor + - it should be default-constructible + - the result of functor call with argument \p p should not depend on where the functor will be called. + + \par Examples: + Operator \p delete functor: + \code + template + struct disposer { + void operator ()( T * p ) { + delete p; + } + }; + + // How to call GC::retire method + int * p = new int; + + // ... use p in lock-free manner + + cds::gc::HP::retire( p ) ; // place p to retired pointer array of HP GC + \endcode + + Functor based on \p std::allocator : + \code + template > + struct disposer { + template + void operator()( T * p ) { + typedef typename ALLOC::templare rebind::other alloc_t; + alloc_t a; + a.destroy( p ); + a.deallocate( p, 1 ); + } + }; + \endcode + */ + template + static void retire( T * p ) ; // inline in hp_impl.h + + /// Get current scan strategy + hp::scan_type getScanType() const + { + return hp::GarbageCollector::instance().getScanType(); + } + + /// Set current scan strategy + void setScanType( + hp::scan_type nScanType ///< new scan strategy + ) + { + hp::GarbageCollector::instance().setScanType( nScanType ); + } + + /// Checks if Hazard Pointer GC is constructed and may be used + static bool isUsed() + { + return hp::GarbageCollector::isUsed(); + } + + + /// Forced GC cycle call for current thread + /** + Usually, this function should not be called directly. + */ + static void scan() ; // inline in hp_impl.h + + /// Synonym for \ref scan() + static void force_dispose() + { + scan(); + } + }; +}} // namespace cds::gc + +#endif // #ifndef __CDS_GC_HP_HP_DECL_H diff --git a/cds/gc/impl/hp_impl.h b/cds/gc/impl/hp_impl.h new file mode 100644 index 00000000..8d046e30 --- /dev/null +++ b/cds/gc/impl/hp_impl.h @@ -0,0 +1,57 @@ +//$$CDS-header$$ + +#ifndef __CDS_GC_HP_HP_IMPL_H +#define __CDS_GC_HP_HP_IMPL_H + +#include +#include + +//@cond +namespace cds { namespace gc { + + inline HP::thread_gc::thread_gc( + bool bPersistent + ) + : m_bPersistent( bPersistent ) + { + if ( !threading::Manager::isThreadAttached() ) + threading::Manager::attachThread(); + } + + inline HP::thread_gc::~thread_gc() + { + if ( !m_bPersistent ) + cds::threading::Manager::detachThread(); + } + + inline HP::Guard::Guard() CDS_CONSTEXPR + : Guard::base_class( cds::threading::getGC() ) + {} + + template + inline HP::GuardArray::GuardArray() CDS_CONSTEXPR + : GuardArray::base_class( cds::threading::getGC() ) + {} + + template + inline void HP::retire( T * p, void (* pFunc)(T *) ) + { + cds::threading::getGC().retirePtr( p, pFunc ); + } + + template + inline void HP::retire( T * p ) + { + cds::threading::getGC().retirePtr( p, cds::details::static_functor::call ); + } + + inline void HP::scan() + { + cds::threading::getGC().scan(); + } + + +}} // namespace cds::gc +//@endcond + +#endif // #ifndef __CDS_GC_HP_HP_IMPL_H diff --git a/cds/threading/details/_common.h b/cds/threading/details/_common.h index 1079303f..73db0ec4 100644 --- a/cds/threading/details/_common.h +++ b/cds/threading/details/_common.h @@ -3,8 +3,8 @@ #ifndef __CDS_THREADING__COMMON_H #define __CDS_THREADING__COMMON_H -#include -#include +#include +#include #include #include diff --git a/projects/Win/vc12/cds.vcxproj b/projects/Win/vc12/cds.vcxproj index 044f1fe9..b6db0612 100644 --- a/projects/Win/vc12/cds.vcxproj +++ b/projects/Win/vc12/cds.vcxproj @@ -740,12 +740,11 @@ - - - - - + + + + diff --git a/projects/Win/vc12/cds.vcxproj.filters b/projects/Win/vc12/cds.vcxproj.filters index 2281455a..9c287647 100644 --- a/projects/Win/vc12/cds.vcxproj.filters +++ b/projects/Win/vc12/cds.vcxproj.filters @@ -148,11 +148,8 @@ {0a2328b4-ff6f-4afb-8de0-9884ae172fa9} - - {043c4eba-3bd4-4226-b214-e26f18b422a1} - - - {9c0f5739-8d9d-46c2-bb91-90ca5beecc6d} + + {3195cce2-1710-4b79-a1cf-6c7cea085fa3} @@ -1154,21 +1151,6 @@ Header Files\cds\gc - - Header Files\cds\gc\hp - - - Header Files\cds\gc\dhp - - - Header Files\cds\gc\dhp - - - Header Files\cds\gc\hp - - - Header Files\cds\gc\hp - Source Files @@ -1187,5 +1169,17 @@ Header Files\cds\gc\details + + Header Files\cds\gc\impl + + + Header Files\cds\gc\impl + + + Header Files\cds\gc\impl + + + Header Files\cds\gc\impl + \ No newline at end of file