reimplement guarded_ptr from scratch
[libcds.git] / cds / gc / details / dhp.h
index 8e117c7a0c3ef5ce16acbc987599318225713ea8..93580a2ea7985c1184666b9e98281929d20b4257 100644 (file)
@@ -15,6 +15,7 @@
 #   pragma warning(disable:4251)    // C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
 #endif
 
+//@cond
 namespace cds { namespace gc {
 
     /// Dynamic Hazard Pointer reclamation schema
@@ -62,17 +63,14 @@ namespace cds { namespace gc {
 
             /// Internal guard representation
             struct guard_data {
-                typedef retired_ptr_node *      handoff_ptr ;   ///< trapped value type
-                typedef void *  guarded_ptr  ;   ///< type of value guarded
+                typedef void * guarded_ptr;  ///< type of value guarded
 
-                atomics::atomic<guarded_ptr>         pPost   ;   ///< pointer guarded
+                atomics::atomic<guarded_ptr>  pPost;       ///< pointer guarded
+                atomics::atomic<guard_data *> pGlobalNext; ///< next item of global list of allocated guards
+                atomics::atomic<guard_data *> pNextFree;   ///< pointer to the next item in global or thread-local free-list
 
-                atomics::atomic<guard_data *>     pGlobalNext ;   ///< next item of global list of allocated guards
-                atomics::atomic<guard_data *>     pNextFree   ;   ///< pointer to the next item in global or thread-local free-list
+                guard_data * pThreadNext; ///< next item of thread's local list of guards
 
-                guard_data *             pThreadNext ;   ///< next item of thread's local list of guards
-
-                //@cond
                 guard_data() CDS_NOEXCEPT
                     : pPost( nullptr )
                     , pGlobalNext( nullptr )
@@ -84,7 +82,6 @@ namespace cds { namespace gc {
                 {
                     pPost.store( nullptr, atomics::memory_order_relaxed );
                 }
-                //@endcond
 
                 /// Checks if the guard is free, that is, it does not contain any pointer guarded
                 bool isFree() const CDS_NOEXCEPT
@@ -118,7 +115,7 @@ namespace cds { namespace gc {
                     details::guard_data * pGuard = m_GuardAllocator.New();
 
                     // Link guard to the list
-                    // m_GuardList is accumulated list and it cannot support concurrent deletion,
+                    // m_GuardList is an accumulating list and it cannot support concurrent deletion,
                     // so, ABA problem is impossible for it
                     details::guard_data * pHead = m_GuardList.load( atomics::memory_order_acquire );
                     do {
@@ -250,8 +247,7 @@ namespace cds { namespace gc {
                 atomics::atomic<size_t>              m_nItemCount;   ///< buffer's item count
 
             public:
-                //@cond
-                CDS_CONSTEXPR retired_ptr_buffer() CDS_NOEXCEPT
+                retired_ptr_buffer() CDS_NOEXCEPT
                     : m_pHead( nullptr )
                     , m_nItemCount(0)
                 {}
@@ -260,7 +256,6 @@ namespace cds { namespace gc {
                 {
                     assert( m_pHead.load( atomics::memory_order_relaxed ) == nullptr );
                 }
-                //@endcond
 
                 /// Pushes new node into the buffer. Returns current buffer size
                 size_t push( retired_ptr_node& node ) CDS_NOEXCEPT
@@ -331,7 +326,6 @@ namespace cds { namespace gc {
                 cds::details::Allocator< block, Alloc > m_BlockAllocator    ;   ///< block allocator
 
             private:
-                //@cond
                 void allocNewBlock()
                 {
                     // allocate new block
@@ -372,10 +366,8 @@ namespace cds { namespace gc {
                 {
                     return (m_nCurEpoch.load(atomics::memory_order_acquire) - 1) & (c_nEpochCount - 1);
                 }
-                //@endcond
 
             public:
-                //@cond
                 retired_ptr_pool()
                     : m_pBlockListHead( nullptr )
                     , m_nCurEpoch(0)
@@ -402,8 +394,6 @@ namespace cds { namespace gc {
                     m_nCurEpoch.fetch_add( 1, atomics::memory_order_acq_rel );
                 }
 
-                //@endcond
-
                 /// Allocates new retired pointer
                 retired_ptr_node&  alloc()
                 {
@@ -466,33 +456,42 @@ namespace cds { namespace gc {
                 friend class ThreadGC;
             protected:
                 details::guard_data * m_pGuard ;    ///< Pointer to guard data
+
             public:
                 /// Initialize empty guard.
                 CDS_CONSTEXPR guard() CDS_NOEXCEPT
                     : m_pGuard( nullptr )
                 {}
 
-                /// The object is not copy-constructible
+                /// Ñopy-ctor is disabled
                 guard( guard const& ) = delete;
 
+                /// Move-ctor is disabled
+                guard( guard&& ) = delete;
+
                 /// Object destructor, does nothing
                 ~guard() CDS_NOEXCEPT
                 {}
 
+                /// Get current guarded pointer
+                void * get( atomics::memory_order order = atomics::memory_order_acquire ) const CDS_NOEXCEPT
+                {
+                    assert( m_pGuard != nullptr );
+                    return m_pGuard->pPost.load( order );
+                }
+
                 /// Guards pointer \p p
-                void set( void * p ) CDS_NOEXCEPT
+                void set( void * p, atomics::memory_order order = atomics::memory_order_release ) CDS_NOEXCEPT
                 {
                     assert( m_pGuard != nullptr );
-                    m_pGuard->pPost.store( p, atomics::memory_order_release );
-                    //CDS_COMPILER_RW_BARRIER;
+                    m_pGuard->pPost.store( p, order );
                 }
 
                 /// Clears the guard
-                void clear() CDS_NOEXCEPT
+                void clear( atomics::memory_order order = atomics::memory_order_relaxed ) CDS_NOEXCEPT
                 {
                     assert( m_pGuard != nullptr );
-                    m_pGuard->pPost.store( nullptr, atomics::memory_order_relaxed );
-                    CDS_STRICT_DO( CDS_COMPILER_RW_BARRIER );
+                    m_pGuard->pPost.store( nullptr, order );
                 }
 
                 /// Guards pointer \p p
@@ -503,20 +502,17 @@ namespace cds { namespace gc {
                     return p;
                 }
 
-                //@cond
                 std::nullptr_t operator=(std::nullptr_t) CDS_NOEXCEPT
                 {
                     clear();
                     return nullptr;
                 }
-                //@endcond
 
             public: // for ThreadGC.
                 /*
                     GCC cannot compile code for template versions of ThreasGC::allocGuard/freeGuard,
                     the compiler produces error: \91cds::gc::dhp::details::guard_data* cds::gc::dhp::details::guard::m_pGuard\92 is protected
                     despite the fact that ThreadGC is declared as friend for guard class.
-                    We should not like to declare m_pGuard member as public one.
                     Therefore, we have to add set_guard/get_guard public functions
                 */
                 /// Set guard data
@@ -536,6 +532,18 @@ namespace cds { namespace gc {
                 {
                     return m_pGuard;
                 }
+
+                details::guard_data * release_guard() CDS_NOEXCEPT
+                {
+                    details::guard_data * p = m_pGuard;
+                    m_pGuard = nullptr;
+                    return p;
+                }
+
+                bool is_initialized() const
+                {
+                    return m_pGuard != nullptr;
+                }
             };
 
         } // namespace details
@@ -547,18 +555,16 @@ namespace cds { namespace gc {
         */
         class Guard: public details::guard
         {
-            //@cond
             typedef details::guard    base_class;
             friend class ThreadGC;
-            //@endcond
 
             ThreadGC&    m_gc    ;    ///< ThreadGC object of current thread
         public:
             /// Allocates a guard from \p gc GC. \p gc must be ThreadGC object of current thread
-            Guard( ThreadGC& gc ) CDS_NOEXCEPT;
+            Guard( ThreadGC& gc );
 
             /// Returns guard allocated back to pool of free guards
-            ~Guard() CDS_NOEXCEPT;    // inline after GarbageCollector
+            ~Guard();    // inline after GarbageCollector
 
             /// Returns DHP GC object
             ThreadGC& getGC() CDS_NOEXCEPT
@@ -573,12 +579,10 @@ namespace cds { namespace gc {
                 return base_class::operator =<T>( p );
             }
 
-            //@cond
             std::nullptr_t operator=(std::nullptr_t) CDS_NOEXCEPT
             {
                 return base_class::operator =(nullptr);
             }
-            //@endcond
         };
 
         /// Array of guards
@@ -602,7 +606,7 @@ namespace cds { namespace gc {
 
         public:
             /// Allocates array of guards from \p gc which must be the ThreadGC object of current thread
-            GuardArray( ThreadGC& gc ) CDS_NOEXCEPT;    // inline below
+            GuardArray( ThreadGC& gc );    // inline below
 
             /// The object is not default-constructible
             GuardArray() = delete;
@@ -611,7 +615,7 @@ namespace cds { namespace gc {
             GuardArray( GuardArray const& ) = delete;
 
             /// Returns guards allocated back to pool
-            ~GuardArray() CDS_NOEXCEPT;    // inline below
+            ~GuardArray();    // inline below
 
             /// Returns the capacity of array
             CDS_CONSTEXPR size_t capacity() const CDS_NOEXCEPT
@@ -666,7 +670,6 @@ namespace cds { namespace gc {
         class CDS_EXPORT_API GarbageCollector
         {
         private:
-            //@cond
             friend class ThreadGC;
 
             /// Internal GC statistics
@@ -680,7 +683,6 @@ namespace cds { namespace gc {
                     , m_nFreeGuardCount(0)
                 {}
             };
-            //@endcond
 
         public:
             /// Exception "No GarbageCollector object is created"
@@ -692,7 +694,6 @@ namespace cds { namespace gc {
                 size_t m_nGuardCount       ;   ///< Total guard count
                 size_t m_nFreeGuardCount   ;   ///< Count of free guard
 
-                //@cond
                 InternalState()
                     : m_nGuardCount(0)
                     , m_nFreeGuardCount(0)
@@ -705,7 +706,6 @@ namespace cds { namespace gc {
 
                     return *this;
                 }
-                //@endcond
             };
 
         private:
@@ -844,10 +844,8 @@ namespace cds { namespace gc {
             }
 
         private:
-            //@cond none
             GarbageCollector( size_t nLiberateThreshold, size_t nInitialThreadGuardCount );
             ~GarbageCollector();
-            //@endcond
         };
 
         /// Thread GC
@@ -906,27 +904,32 @@ namespace cds { namespace gc {
 
         public:
             /// Initializes guard \p g
-            void allocGuard( Guard& g )
+            void allocGuard( dhp::details::guard& g )
             {
                 assert( m_pList != nullptr );
-                if ( m_pFree ) {
-                    g.m_pGuard = m_pFree;
-                    m_pFree = m_pFree->pNextFree.load(atomics::memory_order_relaxed);
-                }
-                else {
-                    g.m_pGuard = m_gc.allocGuard();
-                    g.m_pGuard->pThreadNext = m_pList;
-                    m_pList = g.m_pGuard;
+                if ( !g.m_pGuard ) {
+                    if ( m_pFree ) {
+                        g.m_pGuard = m_pFree;
+                        m_pFree = m_pFree->pNextFree.load( atomics::memory_order_relaxed );
+                    }
+                    else {
+                        g.m_pGuard = m_gc.allocGuard();
+                        g.m_pGuard->pThreadNext = m_pList;
+                        m_pList = g.m_pGuard;
+                    }
                 }
             }
 
             /// Frees guard \p g
-            void freeGuard( Guard& g )
+            void freeGuard( dhp::details::guard& g )
             {
                 assert( m_pList != nullptr );
-                g.m_pGuard->pPost.store( nullptr, atomics::memory_order_relaxed );
-                g.m_pGuard->pNextFree.store( m_pFree, atomics::memory_order_relaxed );
-                m_pFree = g.m_pGuard;
+                if ( g.m_pGuard ) {
+                    g.m_pGuard->pPost.store( nullptr, atomics::memory_order_relaxed );
+                    g.m_pGuard->pNextFree.store( m_pFree, atomics::memory_order_relaxed );
+                    m_pFree = g.m_pGuard;
+                    g.m_pGuard = nullptr;
+                }
             }
 
             /// Initializes guard array \p arr
@@ -975,13 +978,10 @@ namespace cds { namespace gc {
                 m_gc.retirePtr( p, pFunc );
             }
 
-            //@cond
             void scan()
             {
                 m_gc.scan();
             }
-            //@endcond
-
         };
 
         //////////////////////////////////////////////////////////
@@ -1011,6 +1011,7 @@ namespace cds { namespace gc {
 
     }   // namespace dhp
 }}  // namespace cds::gc
+//@endcond
 
 #if CDS_COMPILER == CDS_COMPILER_MSVC
 #   pragma warning(pop)