/// pointer to extracted node
using exempt_ptr = cds::urcu::exempt_ptr< gc, node_type, value_type, typename maker::intrusive_type_traits::disposer >;
+ private:
+ //@cond
+ struct raw_ptr_converter
+ {
+ value_type * operator()( node_type * p ) const
+ {
+ return p ? &p->m_Value : nullptr;
+ }
+
+ value_type& operator()( node_type& n ) const
+ {
+ return n.m_Value;
+ }
+
+ value_type const& operator()( node_type const& n ) const
+ {
+ return n.m_Value;
+ }
+ };
+ //@endcond
+
+ public:
+ /// Result of \p get(), \p get_with() functions - pointer to the node found
+ typedef cds::urcu::raw_ptr_adaptor< value_type, typename base_class::raw_ptr, raw_ptr_converter > raw_ptr;
+
protected:
//@cond
unsigned int random_level()
{
return base_class::random_level();
}
-
- value_type * to_value_ptr( node_type * pNode ) const CDS_NOEXCEPT
- {
- return pNode ? &pNode->m_Value : nullptr;
- }
//@endcond
public:
/// Finds the key \p key and return the item found
/** \anchor cds_nonintrusive_SkipListMap_rcu_get
- The function searches the item with key equal to \p key and returns the pointer to item found.
- If \p key is not found it returns \p nullptr.
+ The function searches the item with key equal to \p key and returns a \p raw_ptr object pointing to an item found.
+ If \p key is not found it returns empty \p raw_ptr.
Note the compare functor in \p Traits class' template argument
should accept a parameter of type \p K that can be not the same as \p key_type.
typedef cds::container::SkipListMap< cds::urcu::gc< cds::urcu::general_buffered<> >, int, foo, my_traits > skip_list;
skip_list theList;
// ...
+ typename skip_list::raw_ptr pVal;
{
// Lock RCU
skip_list::rcu_lock lock;
- skip_list::value_type * pVal = theList.get( 5 );
- // Deal with pVal
- //...
-
- // Unlock RCU by rcu_lock destructor
- // pVal can be freed at any time after RCU unlocking
+ pVal = theList.get( 5 );
+ if ( pVal ) {
+ // Deal with pVal
+ //...
+ }
}
+ // You can manually release pVal after RCU-locked section
+ pVal.release();
\endcode
-
- After RCU unlocking the \p %force_dispose member function can be called manually,
- see \ref force_dispose for explanation.
*/
template <typename K>
- value_type * get( K const& key )
+ raw_ptr get( K const& key )
{
- return to_value_ptr( base_class::get( key ));
+ return raw_ptr( base_class::get( key ));
}
/// Finds the key \p key and return the item found
\p pred must imply the same element order as the comparator used for building the map.
*/
template <typename K, typename Less>
- value_type * get_with( K const& key, Less pred )
+ raw_ptr get_with( K const& key, Less pred )
{
CDS_UNUSED( pred );
- return to_value_ptr( base_class::get_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::key_accessor >() ));
+ return raw_ptr( base_class::get_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::key_accessor >() ));
}
/// Clears the map (not atomic)
{
return base_class::statistics();
}
-
- /// Clears internal list of ready-to-delete items passing them to RCU reclamation cycle
- /** @copydetails cds_intrusive_SkipListSet_rcu_force_dispose
- */
- void force_dispose()
- {
- return base_class::force_dispose();
- }
};
}} // namespace cds::container
/// pointer to extracted node
using exempt_ptr = cds::urcu::exempt_ptr< gc, node_type, value_type, typename maker::intrusive_traits::disposer >;
+ private:
+ //@cond
+ struct raw_ptr_converter
+ {
+ value_type * operator()( node_type * p ) const
+ {
+ return p ? &p->m_Value : nullptr;
+ }
+
+ value_type& operator()( node_type& n ) const
+ {
+ return n.m_Value;
+ }
+
+ value_type const& operator()( node_type const& n ) const
+ {
+ return n.m_Value;
+ }
+ };
+ //@endcond
+
+ public:
+ /// Result of \p get(), \p get_with() functions - pointer to the node found
+ typedef cds::urcu::raw_ptr_adaptor< value_type, typename base_class::raw_ptr, raw_ptr_converter > raw_ptr;
+
protected:
//@cond
unsigned int random_level()
{
return base_class::random_level();
}
-
- value_type * to_value_ptr( node_type * pNode ) const CDS_NOEXCEPT
- {
- return pNode ? &pNode->m_Value : nullptr;
- }
//@endcond
public:
/// Finds \p key and return the item found
/** \anchor cds_nonintrusive_SkipListSet_rcu_get
- The function searches the item with key equal to \p key and returns the pointer to item found.
- If \p key is not found it returns \p nullptr.
+ The function searches the item with key equal to \p key and returns a \p raw_ptr object pointed to item found.
+ If \p key is not found it returns empty \p raw_ptr.
Note the compare functor in \p Traits class' template argument
should accept a parameter of type \p Q that can be not the same as \p value_type.
typedef cds::container::SkipListSet< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > skip_list;
skip_list theList;
// ...
+ typename skip_list::raw_ptr pVal;
{
// Lock RCU
skip_list::rcu_lock lock;
- foo * pVal = theList.get( 5 );
- // Deal with pVal
- //...
-
- // Unlock RCU by rcu_lock destructor
- // pVal can be freed at any time after RCU unlocking
+ pVal = theList.get( 5 );
+ if ( pVal ) {
+ // Deal with pVal
+ //...
+ }
}
+ // You can manually release pVal after RCU-locked section
+ pVal.release();
\endcode
-
- After RCU unlocking the \p %force_dispose member function can be called manually,
- see \ref force_dispose for explanation.
*/
template <typename Q>
- value_type * get( Q const& key )
+ raw_ptr get( Q const& key )
{
- return to_value_ptr( base_class::get( key ));
+ return raw_ptr( base_class::get( key ));
}
/// Finds the key \p val and return the item found
\p pred must imply the same element order as the comparator used for building the set.
*/
template <typename Q, typename Less>
- value_type * get_with( Q const& val, Less pred )
+ raw_ptr get_with( Q const& val, Less pred )
{
CDS_UNUSED( pred );
- return to_value_ptr( base_class::get_with( val, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >() ));
+ return raw_ptr( base_class::get_with( val, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >() ));
}
/// Clears the set (non-atomic).
{
return base_class::statistics();
}
-
- /// Clears internal list of ready-to-delete items passing them to RCU reclamation cycle
- /**
- See \ref cds_intrusive_SkipListSet_rcu_force_dispose "intrusive SkipListSet" for explanation
- */
- void force_dispose()
- {
- return base_class::force_dispose();
- }
};
}} // namespace cds::container
--- /dev/null
+//$$CDS-header$$
+
+#ifndef CDSLIB_INTRUSIVE_DETAILS_RAW_PTR_DISPOSER_H
+#define CDSLIB_INTRUSIVE_DETAILS_RAW_PTR_DISPOSER_H
+
+#include <cds/details/defs.h>
+
+//@cond
+namespace cds { namespace intrusive { namespace details {
+
+ template <typename RCU, typename NodeType, typename Disposer>
+ struct raw_ptr_disposer
+ {
+ typedef RCU gc;
+ typedef NodeType node_type;
+ typedef Disposer disposer;
+
+ node_type * pReclaimedChain;
+
+ raw_ptr_disposer()
+ : pReclaimedChain( nullptr )
+ {}
+
+ template <typename Position>
+ explicit raw_ptr_disposer( Position& pos )
+ : pReclaimedChain( pos.pDelChain )
+ {
+ pos.pDelChain = nullptr;
+ }
+
+ raw_ptr_disposer( raw_ptr_disposer&& d )
+ : pReclaimedChain( d.pReclaimedChain )
+ {
+ d.pReclaimedChain = nullptr;
+ }
+
+ raw_ptr_disposer( raw_ptr_disposer const& ) = delete;
+
+ ~raw_ptr_disposer()
+ {
+ apply();
+ }
+
+ raw_ptr_disposer& operator=(raw_ptr_disposer&& d)
+ {
+ assert( pReclaimedChain == nullptr );
+ pReclaimedChain = d.pReclaimedChain;
+ d.pReclaimedChain = nullptr;
+ retur *this;
+ }
+
+ void apply()
+ {
+ if ( pReclaimedChain ) {
+ assert( !gc::is_locked());
+ disposer()( pReclaimedChain );
+ pReclaimedChain = nullptr;
+ }
+ }
+ };
+
+}}} // namespace cds::intrusive::details
+//@endcond
+
+#endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_RAW_PTR_DISPOSER_H
#include <cds/details/make_const_type.h>
#include <cds/urcu/exempt_ptr.h>
#include <cds/urcu/raw_ptr.h>
+#include <cds/intrusive/details/raw_ptr_disposer.h>
namespace cds { namespace intrusive {
private:
//@cond
- struct raw_ptr_disposer
- {
- node_type * pReclaimedChain;
-
- raw_ptr_disposer()
- : pReclaimedChain( nullptr )
- {}
-
- raw_ptr_disposer( position& pos )
- : pReclaimedChain( pos.pDelChain )
+ struct chain_disposer {
+ void operator()( node_type * pChain ) const
{
- pos.pDelChain = nullptr;
- }
-
- raw_ptr_disposer( raw_ptr_disposer&& d )
- : pReclaimedChain( d.pReclaimedChain )
- {
- d.pReclaimedChain = nullptr;
- }
-
- raw_ptr_disposer( raw_ptr_disposer const& ) = delete;
-
- ~raw_ptr_disposer()
- {
- apply();
- }
-
- void apply()
- {
- if ( pReclaimedChain ) {
- assert( !gc::is_locked());
- dispose_chain( pReclaimedChain );
- pReclaimedChain = nullptr;
- }
+ dispose_chain( pChain );
}
};
+ typedef cds::intrusive::details::raw_ptr_disposer< gc, node_type, chain_disposer> raw_ptr_disposer;
//@endcond
public:
#include <cds/urcu/details/check_deadlock.h>
#include <cds/details/binary_functor_wrapper.h>
#include <cds/urcu/exempt_ptr.h>
-
+#include <cds/urcu/raw_ptr.h>
+#include <cds/intrusive/details/raw_ptr_disposer.h>
namespace cds { namespace intrusive {
typedef std::unique_ptr< node_type, typename node_builder::node_disposer > scoped_node_ptr;
+ static void dispose_node( value_type * pVal )
+ {
+ assert( pVal );
+
+ typename node_builder::node_disposer()( node_traits::to_node_ptr(pVal) );
+ disposer()( pVal );
+ }
+
+ struct node_disposer
+ {
+ void operator()( value_type * pVal )
+ {
+ dispose_node( pVal );
+ }
+ };
+
+ static void dispose_chain( node_type * pChain )
+ {
+ if ( pChain ) {
+ assert( !gc::is_locked() );
+
+ auto f = [&pChain]() -> cds::urcu::retired_ptr {
+ node_type * p = pChain;
+ if ( p ) {
+ pChain = p->m_pDelChain;
+ return cds::urcu::make_retired_ptr<node_disposer>( node_traits::to_value_ptr( p ));
+ }
+ return cds::urcu::make_retired_ptr<node_disposer>( static_cast<value_type *>(nullptr));
+ };
+ gc::batch_retire(std::ref(f));
+ }
+ }
+
struct position {
node_type * pPrev[ c_nMaxHeight ];
node_type * pSucc[ c_nMaxHeight ];
position()
: pDelChain( nullptr )
{}
-# ifdef _DEBUG
+
~position()
{
- assert( pDelChain == nullptr );
+ dispose_chain( pDelChain );
+ }
+
+ void dispose( node_type * p )
+ {
+ assert( p != nullptr );
+ assert( p->m_pDelChain == nullptr );
+
+ p->m_pDelChain = pDelChain;
+ pDelChain = p;
}
-# endif
};
typedef cds::urcu::details::check_deadlock_policy< gc, rcu_check_deadlock> check_deadlock_policy;
{
return node_builder::make_tower( v, m_RandomLevelGen );
}
+ //@endcond
- static void dispose_node( value_type * pVal )
- {
- assert( pVal );
-
- typename node_builder::node_disposer()( node_traits::to_node_ptr(pVal) );
- disposer()( pVal );
- }
+ public:
+ using exempt_ptr = cds::urcu::exempt_ptr< gc, value_type, value_type, node_disposer, void >; ///< pointer to extracted node
- struct node_disposer
- {
- void operator()( value_type * pVal )
+ private:
+ //@cond
+ struct chain_disposer {
+ void operator()( node_type * pChain ) const
{
- dispose_node( pVal );
+ dispose_chain( pChain );
}
};
+ typedef cds::intrusive::details::raw_ptr_disposer< gc, node_type, chain_disposer> raw_ptr_disposer;
//@endcond
public:
- using exempt_ptr = cds::urcu::exempt_ptr< gc, value_type, value_type, node_disposer, void >; ///< pointer to extracted node
+ /// Result of \p get(), \p get_with() functions - pointer to the node found
+ typedef cds::urcu::raw_ptr< gc, value_type, raw_ptr_disposer > raw_ptr;
protected:
//@cond
if ( !is_extracted( pSucc )) {
// We cannot free the node at this moment since RCU is locked
// Link deleted nodes to a chain to free later
- link_for_remove( pos, pCur.ptr() );
+ pos.dispose( pCur.ptr() );
m_Stat.onEraseWhileFind();
}
else {
if ( !is_extracted( pSucc )) {
// We cannot free the node at this moment since RCU is locked
// Link deleted nodes to a chain to free later
- link_for_remove( pos, pCur.ptr() );
+ pos.dispose( pCur.ptr() );
m_Stat.onEraseWhileFind();
}
else {
marked_node_ptr pSucc;
marked_node_ptr pCur;
-retry:
+ retry:
pPred = m_Head.head();
for ( int nLevel = static_cast<int>(c_nMaxHeight - 1); nLevel >= 0; --nLevel ) {
if ( !is_extracted( pSucc )) {
// We cannot free the node at this moment since RCU is locked
// Link deleted nodes to a chain to free later
- link_for_remove( pos, pCur.ptr() );
+ pos.dispose( pCur.ptr() );
m_Stat.onEraseWhileFind();
}
else {
return true;
}
- static void link_for_remove( position& pos, node_type * pDel )
- {
- assert( pDel->m_pDelChain == nullptr );
-
- pDel->m_pDelChain = pos.pDelChain;
- pos.pDelChain = pDel;
- }
-
template <typename Func>
bool try_remove_at( node_type * pDel, position& pos, Func f, bool bExtract )
{
if ( !bExtract ) {
// We cannot free the node at this moment since RCU is locked
// Link deleted nodes to a chain to free later
- link_for_remove( pos, pDel );
+ pos.dispose( pDel );
m_Stat.onFastErase();
}
else
bool do_find_with( Q& val, Compare cmp, Func f )
{
position pos;
+ return do_find_with( val, cmp, f, pos );
+ }
+
+ template <typename Q, typename Compare, typename Func>
+ bool do_find_with( Q& val, Compare cmp, Func f, position& pos )
+ {
bool bRet;
- rcu_lock l;
+ {
+ rcu_lock l;
- switch ( find_fastpath( val, cmp, f )) {
- case find_fastpath_found:
- m_Stat.onFindFastSuccess();
- return true;
- case find_fastpath_not_found:
- m_Stat.onFindFastFailed();
- return false;
- default:
- break;
- }
+ switch ( find_fastpath( val, cmp, f )) {
+ case find_fastpath_found:
+ m_Stat.onFindFastSuccess();
+ return true;
+ case find_fastpath_not_found:
+ m_Stat.onFindFastFailed();
+ return false;
+ default:
+ break;
+ }
- if ( find_slowpath( val, cmp, f, pos )) {
- m_Stat.onFindSlowSuccess();
- bRet = true;
- }
- else {
- m_Stat.onFindSlowFailed();
- bRet = false;
+ if ( find_slowpath( val, cmp, f, pos )) {
+ m_Stat.onFindSlowSuccess();
+ bRet = true;
+ }
+ else {
+ m_Stat.onFindSlowFailed();
+ bRet = false;
+ }
}
-
- defer_chain( pos );
-
return bRet;
}
}
}
- dispose_chain( pos );
return bRet;
}
template <typename Q, typename Compare>
- value_type * do_extract_key( Q const& key, Compare cmp )
+ value_type * do_extract_key( Q const& key, Compare cmp, position& pos )
{
// RCU should be locked!!!
assert( gc::is_locked() );
- position pos;
node_type * pDel;
if ( !find_position( key, pos, cmp, false ) ) {
}
}
- defer_chain( pos );
return pDel ? node_traits::to_value_ptr( pDel ) : nullptr;
}
{
check_deadlock_policy::check();
value_type * pDel = nullptr;
+ position pos;
{
rcu_lock l;
- pDel = do_extract_key( key, key_comparator() );
+ pDel = do_extract_key( key, key_comparator(), pos );
}
- dispose_deferred();
return pDel;
}
CDS_UNUSED(pred);
check_deadlock_policy::check();
value_type * pDel = nullptr;
-
+ position pos;
{
rcu_lock l;
- pDel = do_extract_key( key, cds::opt::details::make_comparator_from_less<Less>() );
+ pDel = do_extract_key( key, cds::opt::details::make_comparator_from_less<Less>(), pos );
}
- dispose_deferred();
return pDel;
}
pDel = nullptr;
}
}
-
- defer_chain( pos );
}
- dispose_deferred();
return pDel ? node_traits::to_value_ptr( pDel ) : nullptr;
}
pDel = nullptr;
}
}
-
- defer_chain( pos );
}
- dispose_deferred();
return pDel ? node_traits::to_value_ptr( pDel ) : nullptr;
}
if ( nCur < nHeight )
m_nHeight.compare_exchange_strong( nCur, nHeight, memory_model::memory_order_release, atomics::memory_order_relaxed );
}
-
- class deferred_list_iterator
- {
- node_type * pCur;
- public:
- explicit deferred_list_iterator( node_type * p )
- : pCur(p)
- {}
- deferred_list_iterator()
- : pCur( nullptr )
- {}
-
- cds::urcu::retired_ptr operator *() const
- {
- return cds::urcu::retired_ptr( node_traits::to_value_ptr(pCur), dispose_node );
- }
-
- void operator ++()
- {
- pCur = pCur->m_pDelChain;
- }
-
- bool operator ==( deferred_list_iterator const& i ) const
- {
- return pCur == i.pCur;
- }
- bool operator !=( deferred_list_iterator const& i ) const
- {
- return !operator ==( i );
- }
- };
-
- void dispose_chain( node_type * pHead )
- {
- // RCU should NOT be locked
- check_deadlock_policy::check();
-
- gc::batch_retire( deferred_list_iterator( pHead ), deferred_list_iterator() );
- }
-
- void dispose_chain( position& pos )
- {
- // RCU should NOT be locked
- check_deadlock_policy::check();
-
- // Delete local chain
- if ( pos.pDelChain ) {
- dispose_chain( pos.pDelChain );
- pos.pDelChain = nullptr;
- }
-
- // Delete deferred chain
- dispose_deferred();
- }
-
- void dispose_deferred()
- {
- dispose_chain( m_pDeferredDelChain.exchange( nullptr, memory_model::memory_order_acq_rel ) );
- }
-
- void defer_chain( position& pos )
- {
- if ( pos.pDelChain ) {
- node_type * pHead = pos.pDelChain;
- node_type * pTail = pHead;
- while ( pTail->m_pDelChain )
- pTail = pTail->m_pDelChain;
-
- node_type * pDeferList = m_pDeferredDelChain.load( memory_model::memory_order_relaxed );
- do {
- pTail->m_pDelChain = pDeferList;
- } while ( !m_pDeferredDelChain.compare_exchange_weak( pDeferList, pHead, memory_model::memory_order_acq_rel, atomics::memory_order_relaxed ));
-
- pos.pDelChain = nullptr;
- }
- }
-
//@endcond
public:
}
}
- dispose_chain( pos );
-
return bRet;
}
}
}
- dispose_chain( pos );
-
return bRet;
}
bool bRet;
{
- rcu_lock rcuLock;
+ rcu_lock l;
if ( !find_position( val, pos, key_comparator(), false ) ) {
m_Stat.onUnlinkFailed();
}
}
- dispose_chain( pos );
-
return bRet;
}
/// Finds \p key and return the item found
/** \anchor cds_intrusive_SkipListSet_rcu_get
- The function searches the item with key equal to \p key and returns the pointer to item found.
- If \p key is not found it returns \p nullptr.
+ The function searches the item with key equal to \p key and returns a \p raw_ptr object pointed to item found.
+ If \p key is not found it returns empty \p raw_ptr.
Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
typedef cds::intrusive::SkipListSet< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > skip_list;
skip_list theList;
// ...
+ typename skip_list::raw_ptr pVal;
{
// Lock RCU
skip_list::rcu_lock lock;
- foo * pVal = theList.get( 5 );
+ pVal = theList.get( 5 );
if ( pVal ) {
// Deal with pVal
//...
}
}
- // Unlock RCU by rcu_lock destructor
- // pVal can be retired by disposer at any time after RCU has been unlocked
+ // You can manually release pVal after RCU-locked section
+ pVal.release();
\endcode
-
- After RCU unlocking the \p %force_dispose member function can be called manually,
- see \ref force_dispose for explanation.
*/
template <typename Q>
- value_type * get( Q const& key )
+ raw_ptr get( Q const& key )
{
assert( gc::is_locked());
+ position pos;
value_type * pFound;
- return do_find_with( key, key_comparator(), [&pFound](value_type& found, Q const& ) { pFound = &found; } )
- ? pFound : nullptr;
+ if ( do_find_with( key, key_comparator(), [&pFound](value_type& found, Q const& ) { pFound = &found; }, pos ))
+ return raw_ptr( pFound, raw_ptr_disposer( pos ));
+ return raw_ptr( raw_ptr_disposer( pos ));
}
/// Finds \p key and return the item found
\p pred must imply the same element order as the comparator used for building the set.
*/
template <typename Q, typename Less>
- value_type * get_with( Q const& key, Less pred )
+ raw_ptr get_with( Q const& key, Less pred )
{
CDS_UNUSED( pred );
assert( gc::is_locked());
value_type * pFound;
- return do_find_with( key, cds::opt::details::make_comparator_from_less<Less>(),
- [&pFound](value_type& found, Q const& ) { pFound = &found; } )
- ? pFound : nullptr;
+ position pos;
+ if ( do_find_with( key, cds::opt::details::make_comparator_from_less<Less>(),
+ [&pFound](value_type& found, Q const& ) { pFound = &found; }, pos ))
+ {
+ return raw_ptr( pFound, raw_ptr_disposer( pos ));
+ }
+ return raw_ptr( raw_ptr_disposer( pos ));
}
/// Returns item count in the set
{
return m_Stat;
}
-
- /// Clears internal list of ready-to-remove items passing it to RCU reclamation cycle
- /** @anchor cds_intrusive_SkipListSet_rcu_force_dispose
- Skip list has complex multi-step algorithm for removing an item. In fact, when you
- remove the item it is just marked as removed that is enough for the success of your operation.
- Actual removing can take place in the future, in another call or even in another thread.
- Inside RCU lock the removed item cannot be passed to RCU reclamation cycle
- since it can lead to deadlock. To solve this problem, the current skip list implementation
- has internal list of items which is ready to remove but is not yet passed to RCU reclamation.
- Usually, this list will be passed to RCU reclamation in the next suitable call of skip list member function.
- In some cases we want to pass it to RCU reclamation immediately after RCU unlocking.
- This function provides such opportunity: it checks whether the RCU is not locked and if it is true
- the function passes the internal ready-to-remove list to RCU reclamation cycle.
-
- The RCU \p synchronize can be called.
- */
- void force_dispose()
- {
- if ( !gc::is_locked() )
- dispose_deferred();
- }
};
}} // namespace cds::intrusive
outside RCU lock.
The object of \p %raw_ptr solves that problem: it contains the pointer to the node found
- and a chain of nodes that be reclaimed during traversing. The \p %raw_ptr object destructor
+ and a chain of nodes that were reclaimed during traversing. The \p %raw_ptr object destructor
frees the chain (but not the node found) passing it to RCU \p batch_retire().
The object of \p %raw_ptr class must be destructed only outside RCU-lock of current thread.
/**
This operator may be called only inside RCU-lock.
The \p this should be empty.
-
- In general, move assignment is intented for internal use.
*/
raw_ptr& operator=( raw_ptr&& p ) CDS_NOEXCEPT
{
assert( empty() );
+ if ( !rcu::is_locked() )
+ release();
m_ptr = p.m_ptr;
m_Enum = std::move( p.m_Enum );
/// Releases the \p %raw_ptr object
/**
- This function may be called only outside RCU section.
- After \p %release() the object can be reused.
+ This function may be called only outside RCU locked region.
+ After \p %release() the object becomes empty and can be reused.
*/
void release()
{
see doc.
Thus, semantics of extract()/get() of all RCU-based set and maps based on
MichaelList (MichaelSet/Map, SplitListSet/Map) has been changed too.
+ - Changed: SplitListSet/Map functions get() and get_with() return special wrapper
+ object of type raw_ptr, see doc.
+ - Removed: SplitListSet/Map force_dispose() function.
- cds::lock namespace is renamed to cds::sync. All classes defined in cds::lock namespace
are moved to cds::sync with new names (for example, cds::lock::SpinLock is renamed to
cds::sync::spin_lock). cds::lock namespace and its contents is deprecated, it is kept
<ClInclude Include="..\..\..\cds\intrusive\details\michael_list_base.h" />\r
<ClInclude Include="..\..\..\cds\intrusive\details\michael_set_base.h" />\r
<ClInclude Include="..\..\..\cds\intrusive\details\node_traits.h" />\r
+ <ClInclude Include="..\..\..\cds\intrusive\details\raw_ptr_disposer.h" />\r
<ClInclude Include="..\..\..\cds\intrusive\details\single_link_struct.h" />\r
<ClInclude Include="..\..\..\cds\intrusive\details\skip_list_base.h" />\r
<ClInclude Include="..\..\..\cds\intrusive\details\split_list_base.h" />\r
<ClInclude Include="..\..\..\cds\urcu\raw_ptr.h">\r
<Filter>Header Files\cds\urcu</Filter>\r
</ClInclude>\r
+ <ClInclude Include="..\..\..\cds\intrusive\details\raw_ptr_disposer.h">\r
+ <Filter>Header Files\cds\intrusive\details</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
typedef typename Map::value_type value_type;
typename Map::exempt_ptr ep;
+ typename Map::raw_ptr rp;
// extract/get
for ( int i = 0; i < nLimit; ++i ) {
int nKey = arrItem[i];
{
rcu_lock l;
- value_type * pVal = m.get( nKey );
- CPPUNIT_ASSERT( pVal != nullptr );
- CPPUNIT_CHECK( pVal->first == nKey );
- CPPUNIT_CHECK( pVal->second.m_val == nKey * 2 );
+ rp = m.get( nKey );
+ CPPUNIT_ASSERT( rp );
+ CPPUNIT_CHECK( rp->first == nKey );
+ CPPUNIT_CHECK( rp->second.m_val == nKey * 2 );
}
+ rp.release();
ep = m.extract( nKey );
CPPUNIT_ASSERT( ep );
{
rcu_lock l;
- CPPUNIT_CHECK( m.get( nKey ) == nullptr );
+ CPPUNIT_CHECK( !m.get( nKey ));
}
ep = m.extract( nKey );
CPPUNIT_CHECK( !ep );
int nKey = arrItem[i];
{
rcu_lock l;
- value_type * pVal = m.get_with( wrapped_item(nKey), wrapped_less() );
- CPPUNIT_ASSERT( pVal != nullptr );
- CPPUNIT_CHECK( pVal->first == nKey );
- CPPUNIT_CHECK( pVal->second.m_val == nKey * 2 );
+ rp = m.get_with( wrapped_item(nKey), wrapped_less() );
+ CPPUNIT_ASSERT( rp );
+ CPPUNIT_CHECK( rp->first == nKey );
+ CPPUNIT_CHECK( rp->second.m_val == nKey * 2 );
}
+ rp.release();
ep = m.extract_with( wrapped_item( nKey ), wrapped_less() );
CPPUNIT_ASSERT( ep );
{
rcu_lock l;
- CPPUNIT_CHECK( m.get_with( wrapped_item(nKey), wrapped_less() ) == nullptr );
+ CPPUNIT_CHECK( !m.get_with( wrapped_item(nKey), wrapped_less() ));
}
ep = m.extract_with( wrapped_item( nKey ), wrapped_less() );
CPPUNIT_CHECK( !ep );
// extract/get test
{
typename Set::exempt_ptr ep;
+ typename Set::raw_ptr rp;
// extract
{
fill_skiplist( s, v );
- value_type * pVal;
+
for ( int i = c_nArrSize - 1; i >= 0; i -= 1 ) {
{
rcu_lock l;
- pVal = s.get( i );
- CPPUNIT_ASSERT( pVal != nullptr );
- CPPUNIT_CHECK( pVal->nKey == i );
- CPPUNIT_CHECK( pVal->nVal == i * 2 );
- pVal->nVal *= 2;
+ rp = s.get( i );
+ CPPUNIT_ASSERT( rp );
+ CPPUNIT_CHECK( rp->nKey == i );
+ CPPUNIT_CHECK( rp->nVal == i * 2 );
+ rp->nVal *= 2;
}
+ rp.release();
ep = s.extract( i );
CPPUNIT_ASSERT( ep );
{
rcu_lock l;
- CPPUNIT_CHECK( s.get( i ) == nullptr );
+ CPPUNIT_CHECK( !s.get( i ));
}
ep = s.extract( i );
CPPUNIT_CHECK( !ep );
for ( int i = c_nArrSize - 1; i >= 0; i -= 1 ) {
{
rcu_lock l;
- value_type * pVal = s.get_with( other_key(i), other_key_less<typename Set::value_type>() );
- CPPUNIT_ASSERT( pVal != nullptr );
- CPPUNIT_CHECK( pVal->nKey == i );
- CPPUNIT_CHECK( pVal->nVal == i * 2 );
- pVal->nVal *= 2;
+ rp = s.get_with( other_key(i), other_key_less<typename Set::value_type>() );
+ CPPUNIT_ASSERT( rp );
+ CPPUNIT_CHECK( rp->nKey == i );
+ CPPUNIT_CHECK( rp->nVal == i * 2 );
+ rp->nVal *= 2;
}
+ rp.release();
ep = s.extract_with( other_key( i ), other_key_less<typename Set::value_type>() );
CPPUNIT_ASSERT( ep );
{
rcu_lock l;
- CPPUNIT_CHECK( s.get_with( other_key( i ), other_key_less<typename Set::value_type>() ) == nullptr );
+ CPPUNIT_CHECK( !s.get_with( other_key( i ), other_key_less<typename Set::value_type>() ));
}
ep = s.extract_with( other_key( i ), other_key_less<typename Set::value_type>() );
CPPUNIT_CHECK( !ep );
// extract/get tests
{
typedef typename base_class::less<typename Set::value_type> less_predicate;
- typename Set::value_type * pVal;
typename Set::exempt_ptr ep;
+ typename Set::raw_ptr rp;
// extract/get
for ( int i = 0; i < nLimit; ++i ) {
int nKey = arrRandom[i];
{
rcu_lock l;
- pVal = s.get( nKey );
- CPPUNIT_ASSERT( pVal != nullptr );
- CPPUNIT_CHECK( pVal->nKey == nKey );
- CPPUNIT_CHECK( pVal->nVal == nKey * 2 );
+ rp = s.get( nKey );
+ CPPUNIT_ASSERT( rp );
+ CPPUNIT_CHECK( rp->nKey == nKey );
+ CPPUNIT_CHECK( rp->nVal == nKey * 2 );
}
+ rp.release();
ep = s.extract( nKey );
CPPUNIT_ASSERT( ep );
{
rcu_lock l;
- CPPUNIT_CHECK( s.get( nKey ) == nullptr );
+ CPPUNIT_CHECK( !s.get( nKey ));
}
ep = s.extract( nKey );
CPPUNIT_CHECK( !ep );
int nKey = arrRandom[i];
{
rcu_lock l;
- pVal = s.get_with( wrapped_item(nKey), wrapped_less() );
- CPPUNIT_ASSERT( pVal != nullptr );
- CPPUNIT_CHECK( pVal->nKey == nKey );
- CPPUNIT_CHECK( pVal->nVal == nKey );
+ rp = s.get_with( wrapped_item(nKey), wrapped_less() );
+ CPPUNIT_ASSERT( rp );
+ CPPUNIT_CHECK( rp->nKey == nKey );
+ CPPUNIT_CHECK( rp->nVal == nKey );
}
+ rp.release();
ep = s.extract_with( wrapped_item( nKey ), wrapped_less() );
CPPUNIT_ASSERT( ep );
{
rcu_lock l;
- CPPUNIT_CHECK( s.get_with( wrapped_item( nKey ), wrapped_less() ) == nullptr );
+ CPPUNIT_CHECK( !s.get_with( wrapped_item( nKey ), wrapped_less() ));
}
ep = s.extract_with( wrapped_item( nKey ), wrapped_less() );
CPPUNIT_CHECK( !ep );