//$$CDS-header$$
-#ifndef __CDS_GC_IMPL_DHP_DECL_H
-#define __CDS_GC_IMPL_DHP_DECL_H
+#ifndef CDSLIB_GC_IMPL_DHP_DECL_H
+#define CDSLIB_GC_IMPL_DHP_DECL_H
#include <cds/gc/details/dhp.h>
#include <cds/details/marked_ptr.h>
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
};
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()
+ {}
//@cond
Guard( Guard const& ) = delete;
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 pCur = toGuard.load(atomics::memory_order_acquire);
T pRet;
do {
pRet = assign( pCur );
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_( noexcept( Func( atomics::atomic<T>().load(atomics::memory_order_relaxed))))
+ T protect( atomics::atomic<T> const& toGuard, Func f )
{
- T pCur = toGuard.load(atomics::memory_order_relaxed);
+ T pCur = toGuard.load(atomics::memory_order_acquire);
T pRet;
do {
pRet = pCur;
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);
}
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);
}
public:
// Default ctor
- GuardArray() CDS_NOEXCEPT; // inline in dhp_impl.h
+ GuardArray()
+ {}
//@cond
GuardArray( GuardArray const& ) = delete;
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 {
- pRet = assign( nIndex, toGuard.load(atomics::memory_order_relaxed) );
- } while ( pRet != toGuard.load(atomics::memory_order_acquire));
+ pRet = assign( nIndex, toGuard.load(atomics::memory_order_acquire) );
+ } while ( pRet != toGuard.load(atomics::memory_order_relaxed));
return pRet;
}
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_( noexcept( Func( atomics::atomic<T>().load(atomics::memory_order_relaxed))))
+ 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));
+ assign( nIndex, f( pRet = toGuard.load(atomics::memory_order_acquire) ));
+ } while ( pRet != toGuard.load(atomics::memory_order_relaxed));
return pRet;
}
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;
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
+ {
+ free_guard();
+ }
+
+ //@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
+ /// Initializes %DHP memory manager singleton
/**
- The constructor calls GarbageCollector::Construct with passed parameters.
- See dhp::GarbageCollector::Construct for explanation of parameters meaning.
+ Constructor creates and initializes %DHP global object.
+ %DHP object should be created before using CDS data structure based on \p %cds::gc::DHP GC. Usually,
+ it is created in the \p main() function.
+ After creating of global object you may use CDS data structures based on \p %cds::gc::DHP.
+
+ \par Parameters
+ - \p nLiberateThreshold - \p scan() threshold. When count of retired pointers reaches this value,
+ the \p scan() member function would be called for freeing retired pointers.
+ - \p nInitialThreadGuardCount - initial count of guard allocated for each thread.
+ When a thread is initialized the GC allocates local guard pool for the thread from common guard pool.
+ By perforce the local thread's guard pool is grown automatically from common pool.
+ When the thread terminated its guard pool is backed to common GC's pool.
*/
DHP(
size_t nLiberateThreshold = 1024
);
}
- /// Terminates dhp::GarbageCollector singleton
+ /// Destroys %DHP memory manager
/**
- The destructor calls \code dhp::GarbageCollector::Destruct() \endcode
+ The destructor destroys %DHP global object. After calling of this function you may \b NOT
+ use CDS data structures based on \p %cds::gc::DHP.
+ Usually, %DHP object is destroyed at the end of your \p main().
*/
~DHP()
{
/// 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
- \p gc::DHP garbage collector.
+ \p %gc::DHP garbage collector.
*/
- static CDS_CONSTEXPR bool check_available_guards(
+ static CDS_CONSTEXPR bool check_available_guards(
#ifdef CDS_DOXYGEN_INVOKED
- size_t nCountNeeded,
+ size_t nCountNeeded,
#else
size_t,
#endif
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.
+ See \p gc::HP::retire for \p Disposer requirements.
*/
template <class Disposer, typename T>
static void retire( T * p )
}} // namespace cds::gc
-#endif // #ifndef __CDS_GC_IMPL_DHP_DECL_H
+#endif // #ifndef CDSLIB_GC_IMPL_DHP_DECL_H