bugfix in cds::gc::HP::guarded_ptr
[libcds.git] / cds / gc / impl / dhp_decl.h
index 9c26fd8709719de6bde4b6a57468f1e28b99f639..6da2073a658364a0661988b6929052101d157a63 100644 (file)
@@ -1,7 +1,7 @@
 //$$CDS-header$$
 
-#ifndef __CDS_GC_DHP_DHP_DECL_H
-#define __CDS_GC_DHP_DHP_DECL_H
+#ifndef __CDS_GC_IMPL_DHP_DECL_H
+#define __CDS_GC_IMPL_DHP_DECL_H
 
 #include <cds/gc/details/dhp.h>
 #include <cds/details/marked_ptr.h>
@@ -88,6 +88,12 @@ namespace cds { namespace gc {
                 Otherwise it detaches the current thread from Dynamic Hazard Pointer GC.
             */
             ~thread_gc()    ;   // inline in dhp_impl.h
+
+        public: // for internal use only!!!
+            //@cond
+            static void alloc_guard( cds::gc::dhp::details::guard& g ); // inline in dhp_impl.h
+            static void free_guard( cds::gc::dhp::details::guard& g ); // inline in dhp_impl.h
+            //@endcond
         };
 
 
@@ -107,9 +113,14 @@ namespace cds { namespace gc {
             typedef dhp::Guard base_class;
             //@endcond
 
+        public: // for internal use only
+            //@cond
+            typedef cds::gc::dhp::details::guard native_guard;
+            //@endcond
+
         public:
             // Default ctor
-            Guard() CDS_NOEXCEPT;   // inline in dhp_impl.h
+            Guard();   // inline in dhp_impl.h
 
             //@cond
             Guard( Guard const& ) = delete;
@@ -126,7 +137,7 @@ namespace cds { namespace gc {
                 to the HP slot repeatedly until the guard's value equals \p toGuard
             */
             template <typename T>
-            T protect( atomics::atomic<T> const& toGuard ) CDS_NOEXCEPT
+            T protect( atomics::atomic<T> const& toGuard )
             {
                 T pCur = toGuard.load(atomics::memory_order_relaxed);
                 T pRet;
@@ -155,7 +166,7 @@ namespace cds { namespace gc {
                 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 ) CDS_NOEXCEPT_( f( toGuard.load( atomics::memory_order_relaxed )))
+            T protect( atomics::atomic<T> const& toGuard, Func f )
             {
                 T pCur = toGuard.load(atomics::memory_order_relaxed);
                 T pRet;
@@ -174,13 +185,13 @@ namespace cds { namespace gc {
                 or for already guarded pointer.
             */
             template <typename T>
-            T * assign( T * p ) CDS_NOEXCEPT
+            T * assign( T * p )
             {
                 return base_class::operator =(p);
             }
 
             //@cond
-            std::nullptr_t assign( std::nullptr_t ) CDS_NOEXCEPT
+            std::nullptr_t assign( std::nullptr_t )
             {
                 return base_class::operator =(nullptr);
             }
@@ -193,32 +204,32 @@ namespace cds { namespace gc {
                 or for already guarded pointer.
             */
             template <typename T, int BITMASK>
-            T * assign( cds::details::marked_ptr<T, BITMASK> p ) CDS_NOEXCEPT
+            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 ) CDS_NOEXCEPT
+            void copy( Guard const& src )
             {
                 assign( src.get_native() );
             }
 
             /// Clears value of the guard
-            void clear() CDS_NOEXCEPT
+            void clear()
             {
                 base_class::clear();
             }
 
             /// Gets the value currently protected (relaxed read)
             template <typename T>
-            T * get() const CDS_NOEXCEPT
+            T * get() const
             {
                 return reinterpret_cast<T *>( get_native() );
             }
 
             /// Gets native guarded pointer stored
-            guarded_pointer get_native() const CDS_NOEXCEPT
+            guarded_pointer get_native() const
             {
                 return base_class::get_guard()->pPost.load(atomics::memory_order_relaxed);
             }
@@ -248,7 +259,7 @@ namespace cds { namespace gc {
 
         public:
             // Default ctor
-            GuardArray() CDS_NOEXCEPT;   // inline in dhp_impl.h
+            GuardArray();   // inline in dhp_impl.h
 
             //@cond
             GuardArray( GuardArray const& ) = delete;
@@ -265,7 +276,7 @@ namespace cds { namespace gc {
                 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 ) CDS_NOEXCEPT
+            T protect( size_t nIndex, atomics::atomic<T> const& toGuard )
             {
                 T pRet;
                 do {
@@ -293,7 +304,7 @@ namespace cds { namespace gc {
                 Actually, 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 ) CDS_NOEXCEPT_( f( toGuard.load( atomics::memory_order_relaxed )))
+            T protect( size_t nIndex, atomics::atomic<T> const& toGuard, Func f )
             {
                 T pRet;
                 do {
@@ -308,7 +319,7 @@ namespace cds { namespace gc {
                 The function is just an assignment, no loop is performed.
             */
             template <typename T>
-            T * assign( size_t nIndex, T * p ) CDS_NOEXCEPT
+            T * assign( size_t nIndex, T * p )
             {
                 base_class::set(nIndex, p);
                 return p;
@@ -321,49 +332,233 @@ namespace cds { namespace gc {
                 or for already guarded pointer.
             */
             template <typename T, int Bitmask>
-            T * assign( size_t nIndex, cds::details::marked_ptr<T, Bitmask> p ) CDS_NOEXCEPT
+            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 ) CDS_NOEXCEPT
+            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 ) CDS_NOEXCEPT
+            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 ) CDS_NOEXCEPT
+            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 CDS_NOEXCEPT
+            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 CDS_NOEXCEPT
+            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() CDS_NOEXCEPT
+            static CDS_CONSTEXPR size_t capacity()
             {
                 return Count;
             }
         };
 
+        /// Guarded pointer
+        /**
+            A guarded pointer is a pair of a pointer and GC's guard.
+            Usually, it is used for returning a pointer to the item from an lock-free container.
+            The guard prevents the pointer to be early disposed (freed) by GC.
+            After destructing \p %guarded_ptr object the pointer can be disposed (freed) automatically at any time.
+
+            Template arguments:
+            - \p GuardedType - a type which the guard stores
+            - \p ValueType - a value type
+            - \p Cast - a functor for converting <tt>GuardedType*</tt> to <tt>ValueType*</tt>. Default is \p void (no casting).
+
+            For intrusive containers, \p GuardedType is the same as \p ValueType and no casting is needed.
+            In such case the \p %guarded_ptr is:
+            @code
+            typedef cds::gc::DHP::guarded_ptr< foo > intrusive_guarded_ptr;
+            @endcode
+
+            For standard (non-intrusive) containers \p GuardedType is not the same as \p ValueType and casting is needed.
+            For example:
+            @code
+            struct foo {
+                int const   key;
+                std::string value;
+            };
+
+            struct value_accessor {
+                std::string* operator()( foo* pFoo ) const
+                {
+                    return &(pFoo->value);
+                }
+            };
+
+            // Guarded ptr
+            typedef cds::gc::DHP::guarded_ptr< Foo, std::string, value_accessor > nonintrusive_guarded_ptr;
+            @endcode
+
+            You don't need use this class directly.
+            All set/map container classes from \p libcds declare the typedef for \p %guarded_ptr with appropriate casting functor.
+        */
+        template <typename GuardedType, typename ValueType=GuardedType, typename Cast=void >
+        class guarded_ptr
+        {
+            //@cond
+            struct trivial_cast {
+                ValueType * operator()( GuardedType * p ) const
+                {
+                    return p;
+                }
+            };
+            //@endcond
+
+        public:
+            typedef GuardedType guarded_type; ///< Guarded type
+            typedef ValueType   value_type;   ///< Value type
+
+            /// Functor for casting \p guarded_type to \p value_type
+            typedef typename std::conditional< std::is_same<Cast, void>::value, trivial_cast, Cast >::type value_cast;
+
+            //@cond
+            typedef cds::gc::dhp::details::guard native_guard;
+            //@endcond
+
+        private:
+            //@cond
+            native_guard    m_guard;
+            //@endcond
+
+        public:
+            /// Creates empty guarded pointer
+            guarded_ptr() CDS_NOEXCEPT
+            {}
+
+            //@cond
+            /// Initializes guarded pointer with \p p
+            explicit guarded_ptr( guarded_type * p ) CDS_NOEXCEPT
+            {
+                alloc_guard();
+                assert( m_guard.is_initialized() );
+                m_guard.set( p );
+            }
+            explicit guarded_ptr( std::nullptr_t ) CDS_NOEXCEPT
+            {}
+            //@endcond
+
+            /// Move ctor
+            guarded_ptr( guarded_ptr&& gp ) CDS_NOEXCEPT
+            {
+                m_guard.set_guard( gp.m_guard.release_guard() );
+            }
+
+            /// The guarded pointer is not copy-constructible
+            guarded_ptr( guarded_ptr const& gp ) = delete;
+
+            /// Clears the guarded pointer
+            /**
+                \ref release is called if guarded pointer is not \ref empty
+            */
+            ~guarded_ptr() CDS_NOEXCEPT
+            {
+                free_guard();
+            }
+
+            /// Move-assignment operator
+            guarded_ptr& operator=( guarded_ptr&& gp ) CDS_NOEXCEPT
+            {
+                free_guard();
+                m_guard.set_guard( gp.m_guard.release_guard() );
+                return *this;
+            }
+
+            /// The guarded pointer is not copy-assignable
+            guarded_ptr& operator=(guarded_ptr const& gp) = delete;
+
+            /// Returns a pointer to guarded value
+            value_type * operator ->() const CDS_NOEXCEPT
+            {
+                assert( !empty() );
+                return value_cast()( reinterpret_cast<guarded_type *>(m_guard.get()));
+            }
+
+            /// Returns a reference to guarded value
+            value_type& operator *() CDS_NOEXCEPT
+            {
+                assert( !empty());
+                return *value_cast()(reinterpret_cast<guarded_type *>(m_guard.get()));
+            }
+
+            /// Returns const reference to guarded value
+            value_type const& operator *() const CDS_NOEXCEPT
+            {
+                assert( !empty() );
+                return *value_cast()(reinterpret_cast<guarded_type *>(m_guard.get()));
+            }
+
+            /// Checks if the guarded pointer is \p nullptr
+            bool empty() const CDS_NOEXCEPT
+            {
+                return !m_guard.is_initialized() || m_guard.get( atomics::memory_order_relaxed ) == nullptr;
+            }
+
+            /// \p bool operator returns <tt>!empty()</tt>
+            explicit operator bool() const CDS_NOEXCEPT
+            {
+                return !empty();
+            }
+
+            /// Clears guarded pointer
+            /**
+                If the guarded pointer has been released, the pointer can be disposed (freed) at any time.
+                Dereferncing the guarded pointer after \p release() is dangerous.
+            */
+            void release() CDS_NOEXCEPT
+            {
+                if ( m_guard.is_initialized() )
+                    m_guard.clear();
+            }
+
+            //@cond
+            // For internal use only!!!
+            native_guard& guard() CDS_NOEXCEPT
+            {
+                alloc_guard();
+                assert( m_guard.is_initialized() );
+                return m_guard;
+            }
+            //@endcond
+
+        private:
+            //@cond
+            void alloc_guard()
+            {
+                if ( !m_guard.is_initialized() )
+                    thread_gc::alloc_guard( m_guard );
+            }
+
+            void free_guard()
+            {
+                if ( m_guard.is_initialized() )
+                    thread_gc::free_guard( m_guard );
+            }
+            //@endcond
+        };
+
     public:
         /// Initializes dhp::GarbageCollector singleton
         /**
@@ -393,11 +588,16 @@ namespace cds { namespace gc {
         /// 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.
+            \p gc::DHP garbage collector.
         */
-        static CDS_CONSTEXPR bool check_available_guards( size_t nCountNeeded, bool /*bRaiseException*/ = true )
+        static CDS_CONSTEXPR bool check_available_guards( 
+#ifdef CDS_DOXYGEN_INVOKED
+            size_t nCountNeeded, 
+#else
+            size_t,
+#endif
+            bool /*bRaiseException*/ = true )
         {
-            CDS_UNUSED( nCountNeeded );
             return true;
         }
 
@@ -447,4 +647,4 @@ namespace cds { namespace gc {
 
 }} // namespace cds::gc
 
-#endif // #ifndef __CDS_GC_DHP_DHP_DECL_H
+#endif // #ifndef __CDS_GC_IMPL_DHP_DECL_H