static CDS_CONSTEXPR bool const c_bRelaxedInsert = traits::relaxed_insert;
/// Returned pointer to \p mapped_type of extracted node
- typedef std::unique_ptr< mapped_type, disposer > unique_ptr;
+ typedef std::unique_ptr< T, disposer > unique_ptr;
+
+ typedef typename gc::scoped_lock rcu_lock; ///< RCU scoped lock
protected:
//@cond
- typedef typename sync_monitor::node_injection< bronson_avltree::node< key_type, mapped_type >> node_type;
+ typedef typename sync_monitor::template node_injection< bronson_avltree::node< key_type, mapped_type >> node_type;
typedef typename node_type::version_type version_type;
typedef cds::details::Allocator< node_type, node_allocator_type > cxx_allocator;
retry
};
- enum class update_flags
+ struct update_flags
{
- allow_insert = 1,
- allow_update = 2,
- allow_remove = 4,
+ enum {
+ allow_insert = 1,
+ allow_update = 2,
+ //allow_remove = 4,
- retry = 1024,
+ retry = 1024,
- failed = 0,
- result_inserted = allow_insert,
- result_updated = allow_update,
- result_removed = allow_remove
+ failed = 0,
+ result_inserted = allow_insert,
+ result_updated = allow_update,
+ result_removed = 4
+ };
};
enum node_condition
cxx_allocator().Delete( pNode );
}
+ static node_type * child( node_type * pNode, int nDir, atomics::memory_order order )
+ {
+ return static_cast<node_type *>(pNode->child( nDir ).load( order ));
+ }
+
+ static node_type * parent( node_type * pNode, atomics::memory_order order )
+ {
+ return static_cast<node_type *>(pNode->m_pParent.load( order ));
+ }
+
// RCU safe disposer
class rcu_disposer
{
void dispose( node_type * pNode )
{
- mapped_type * pVal = pNode->value( memory_model::memory_order_relaxed );
+ mapped_type pVal = pNode->value( memory_model::memory_order_relaxed );
if ( pVal ) {
pNode->m_pValue.store( nullptr, memory_model::memory_order_relaxed );
disposer()(pVal);
assert( !gc::is_locked() );
for ( node_type * p = m_pRetiredList; p; ) {
- node_type * pNext = p->m_pNextRemoved;
+ node_type * pNext = static_cast<node_type *>( p->m_pNextRemoved );
// Value already disposed
gc::template retire_ptr<internal_disposer>( p );
p = pNext;
protected:
//@cond
- typename node_type::base_class m_Root;
- node_type * m_pRoot;
- item_counter m_ItemCounter;
- mutable sync_monitor m_Monitor;
- mutable stat m_stat;
+ typename node_type::base_class m_Root;
+ node_type * m_pRoot;
+ item_counter m_ItemCounter;
+ mutable sync_monitor m_Monitor;
+ mutable stat m_stat;
//@endcond
public:
/// Creates empty map
BronsonAVLTreeMap()
- : m_pRoot( static_cast<node_type *>(&m_Root) )
+ : m_pRoot( static_cast<node_type *>( &m_Root ))
{}
/// Destroys the map
Returns \p true if inserting successful, \p false otherwise.
*/
template <typename K>
- bool insert( K const& key, mapped_type * pVal )
+ bool insert( K const& key, mapped_type pVal )
{
return do_update(key, key_comparator(),
- [pVal]( node_type * pNode ) -> mapped_type*
+ [pVal]( node_type * pNode ) -> mapped_type
{
assert( pNode->m_pValue.load( memory_model::memory_order_relaxed ) == nullptr );
return pVal;
},
- update_flags::allow_insert
+ update_flags::allow_insert
) == update_flags::result_inserted;
}
already is in the tree.
*/
template <typename K, typename Func>
- std::pair<bool, bool> update( K const& key, mapped_type * pVal, bool bInsert = true )
+ std::pair<bool, bool> update( K const& key, mapped_type pVal, bool bInsert = true )
{
int result = do_update( key, key_comparator(),
- [pVal]( node_type * ) -> mapped_type*
+ [pVal]( node_type * ) -> mapped_type
{
return pVal;
},
template <typename K>
bool erase( K const& key )
{
- return do_update(
- key,
- key_comparator(),
- []( mapped_type * pVal ) { disposer()(pVal); },
- update_flags::allow_remove
- ) == update_flags::result_removed;
+ return do_remove(
+ key,
+ key_comparator(),
+ []( mapped_type pVal ) { disposer()(pVal); }
+ );
}
/// Deletes the item from the map using \p pred predicate for searching
bool erase_with( K const& key, Less pred )
{
CDS_UNUSED( pred );
- return do_update(
+ return do_remove(
key,
cds::details::predicate_wrapper<key_type, Less, cds::details::trivial_accessor >(),
- []( mapped_type * pVal ) { disposer()(pVal); },
- update_flags::allow_remove
- ) == update_flags::result_removed;
+ []( mapped_type pVal ) { disposer()(pVal); }
+ );
}
/// Delete \p key from the map
template <typename K, typename Func>
bool erase( K const& key, Func f )
{
- return do_update(
+ return do_remove(
key,
key_comparator(),
- [&f]( mapped_type * pVal ) { f( *pVal ); disposer()(pVal); },
- update_flags::allow_remove
- ) == update_flags::result_removed;
+ [&f]( mapped_type pVal ) {
+ assert( pVal );
+ f( *pVal );
+ disposer()(pVal);
+ }
+ );
}
/// Deletes the item from the map using \p pred predicate for searching
bool erase_with( K const& key, Less pred, Func f )
{
CDS_UNUSED( pred );
- return do_update(
+ return do_remove(
key,
cds::details::predicate_wrapper<key_type, Less, cds::details::trivial_accessor >(),
- [&f]( mapped_type * pVal ) { f( *pVal ); disposer()(pVal); },
- update_flags::allow_remove
- ) == update_flags::result_removed;
+ [&f]( mapped_type pVal ) {
+ assert( pVal );
+ f( *pVal );
+ disposer()(pVal);
+ }
+ );
}
/// Extracts an item with minimal key from the map
unique_ptr extract_min()
{
//TODO
+ return unique_ptr();
}
/// Extracts an item with maximal key from the map
unique_ptr extract_max()
{
//TODO
+ return unique_ptr();
}
/// Extracts an item from the map
{
unique_ptr pExtracted;
- do_update(
+ do_remove(
key,
key_comparator(),
- [&pExtracted]( mapped_type * pVal ) { pExtracted.reset( pVal ); },
- update_flags::allow_remove
- );
+ [&pExtracted]( mapped_type pVal ) { pExtracted.reset( pVal ); }
+ );
return pExtracted;
}
CDS_UNUSED( pred );
unique_ptr pExtracted;
- do_update(
+ do_remove(
key,
cds::details::predicate_wrapper<key_type, Less, cds::details::trivial_accessor >(),
- [&pExtracted]( mapped_type * pVal ) { pExtracted.reset( pVal ); },
- update_flags::allow_remove
- );
+ [&pExtracted]( mapped_type pVal ) { pExtracted.reset( pVal ); }
+ );
return pExtracted;
}
The interface of \p Func functor is:
\code
struct functor {
- void operator()( mapped_type& item );
+ void operator()( key_type const& key, mapped_type& item );
};
\endcode
where \p item is the item found.
template <typename K, typename Func>
bool find( K const& key, Func f )
{
- return do_find( key, key_comparator(), [&f]( sync_monitor& monitor, node_type * pNode ) -> bool {
- assert( pNode != nullptr );
- node_scoped_lock l( monitor, *pNode );
- mapped_type * pVal = pNode->m_pValue.load( memory_model::memory_order_relaxed );
- if ( pVal ) {
- f( *pVal );
- return true;
+ return do_find( key, key_comparator(),
+ [&f]( sync_monitor& monitor, node_type * pNode ) -> bool {
+ assert( pNode != nullptr );
+ node_scoped_lock l( monitor, *pNode );
+ mapped_type pVal = pNode->m_pValue.load( memory_model::memory_order_relaxed );
+ if ( pVal ) {
+ f( pNode->m_key, *pVal );
+ return true;
+ }
+ return false;
}
- return false;
- });
+ );
}
/// Finds the key \p val using \p pred predicate for searching
[&f]( sync_monitor& monitor, node_type * pNode ) -> bool {
assert( pNode != nullptr );
node_scoped_lock l( monitor, *pNode );
- mapped_type * pVal = pNode->m_pValue.load( memory_model::memory_order_relaxed );
+ mapped_type pVal = pNode->m_pValue.load( memory_model::memory_order_relaxed );
if ( pVal ) {
- f( *pVal );
+ f( pNode->m_key, *pVal );
return true;
}
return false;
template <typename K>
bool find( K const& key )
{
- return do_find( key, key_comparator(), []( node_type * ) -> bool { return true; });
+ return do_find( key, key_comparator(), []( sync_monitor&, node_type * ) -> bool { return true; });
}
/// Finds the key \p val using \p pred predicate for searching
bool find_with( K const& key, Less pred )
{
CDS_UNUSED( pred );
- return do_find( key, opt::details::make_comparator_from_less<Less>(), []( node_type * ) -> bool { return true; } );
+ return do_find( key, opt::details::make_comparator_from_less<Less>(), []( sync_monitor&, node_type * ) -> bool { return true; } );
}
/// Clears the tree (thread safe, not atomic)
*/
void clear()
{
- for ( ;; ) {
- unique_ptr ep( extract_min() );
- if ( !ep )
- return;
- }
+ while ( extract_min() );
}
/// Clears the tree (not thread safe)
rcu_disposer removed_list;
{
rcu_lock l;
- return try_udate_root( key, cmp, nFlags, funcUpdate, removed_list );
+ return try_update_root( key, cmp, nFlags, funcUpdate, removed_list );
+ }
+ }
+
+ template <typename K, typename Compare, typename Func>
+ bool do_remove( K const& key, Compare cmp, Func func )
+ {
+ check_deadlock_policy::check();
+
+ rcu_disposer removed_list;
+ {
+ rcu_lock l;
+ return try_remove_root( key, cmp, func, removed_list );
}
}
assert( pNode );
while ( true ) {
- node_type * pChild = pNode->child( nDir ).load( memory_model::memory_order_relaxed );
+ node_type * pChild = child( pNode, nDir, memory_model::memory_order_relaxed );
if ( !pChild ) {
if ( pNode->version( memory_model::memory_order_acquire ) != nVersion ) {
m_stat.onFindRetry();
if ( nCmp == 0 ) {
if ( pChild->is_valued( memory_model::memory_order_relaxed ) ) {
// key found
- node_scoped_lock l( m_Monitor, pChild );
+ node_scoped_lock l( m_Monitor, *pChild );
if ( pChild->is_valued( memory_model::memory_order_relaxed )) {
- if ( f( *pChild ) ) {
+ if ( f( m_Monitor, pChild ) ) {
m_stat.onFindSuccess();
return find_result::found;
}
}
template <typename K, typename Compare, typename Func>
- int try_update_root( K const* key, Compare cmp, int nFlags, Func funcUpdate, rcu_disposer& disp )
+ int try_update_root( K const& key, Compare cmp, int nFlags, Func funcUpdate, rcu_disposer& disp )
{
assert( gc::is_locked() );
int result;
do {
// get right child of root
- node_type * pChild = m_Root.child( 1, memory_model::memory_order_acquire );
+ node_type * pChild = child( m_pRoot, 1, memory_model::memory_order_acquire );
if ( pChild ) {
version_type nChildVersion = pChild->version( memory_model::memory_order_relaxed );
if ( nChildVersion & node_type::shrinking ) {
pChild->template wait_until_shrink_completed<back_off>( memory_model::memory_order_relaxed );
result = update_flags::retry;
}
- else if ( pChild == m_Root.child( 1, memory_model::memory_order_acquire ) ) {
+ else if ( pChild == child( m_pRoot, 1, memory_model::memory_order_acquire )) {
result = try_update( key, cmp, nFlags, funcUpdate, m_pRoot, pChild, nChildVersion, disp );
}
}
node_scoped_lock l( m_Monitor, *m_pRoot );
node_type * pNew = alloc_node( key, 1, 0, m_pRoot, nullptr, nullptr );
- mapped_type * pVal = funcUpdate( pNew );
+ mapped_type pVal = funcUpdate( pNew );
assert( pVal != nullptr );
pNew->m_pValue.store( pVal, memory_model::memory_order_release );
return result;
}
+ template <typename K, typename Compare, typename Func>
+ bool try_remove_root( K const& key, Compare cmp, Func func, rcu_disposer& disp )
+ {
+ assert( gc::is_locked() );
+
+ int result;
+ do {
+ // get right child of root
+ node_type * pChild = child( m_pRoot, 1, memory_model::memory_order_acquire );
+ if ( pChild ) {
+ version_type nChildVersion = pChild->version( memory_model::memory_order_relaxed );
+ if ( nChildVersion & node_type::shrinking ) {
+ m_stat.onUpdateRootWaitShrinking();
+ pChild->template wait_until_shrink_completed<back_off>( memory_model::memory_order_relaxed );
+ result = update_flags::retry;
+ }
+ else if ( pChild == child( m_pRoot, 1, memory_model::memory_order_acquire )) {
+ result = try_remove( key, cmp, func, m_pRoot, pChild, nChildVersion, disp );
+ }
+ }
+ else
+ return false;
+ } while ( result == update_flags::retry );
+
+ return result == update_flags::result_removed;
+ }
+
template <typename K, typename Compare, typename Func>
int try_update( K const& key, Compare cmp, int nFlags, Func funcUpdate, node_type * pParent, node_type * pNode, version_type nVersion, rcu_disposer& disp )
{
assert( gc::is_locked() );
assert( nVersion != node_type::unlinked );
+ CDS_UNUSED( pParent );
int nCmp = cmp( key, pNode->m_key );
if ( nCmp == 0 ) {
if ( nFlags & update_flags::allow_update ) {
return try_update_node( funcUpdate, pNode );
}
- if ( nFlags & update_flags::allow_remove ) {
- return try_remove_node( pParent, pNode, nVersion, funcUpdate, disp );
- }
return update_flags::failed;
}
int result;
do {
- node_type * pChild = pNode->child( nCmp ).load( memory_model::memory_order_relaxed );
+ node_type * pChild = child( pNode, nCmp, memory_model::memory_order_relaxed );
if ( pNode->version( memory_model::memory_order_acquire ) != nVersion )
return update_flags::retry;
pChild->template wait_until_shrink_completed<back_off>( memory_model::memory_order_relaxed );
// retry
}
- else if ( pChild == pNode->child( nCmp ).load( memory_model::memory_order_relaxed ) ) {
+ else if ( pChild == child( pNode, nCmp, memory_model::memory_order_relaxed )) {
// this second read is important, because it is protected by nChildVersion
// validate the read that our caller took to get to node
return result;
}
+ template <typename K, typename Compare, typename Func>
+ int try_remove( K const& key, Compare cmp, Func func, node_type * pParent, node_type * pNode, version_type nVersion, rcu_disposer& disp )
+ {
+ assert( gc::is_locked() );
+ assert( nVersion != node_type::unlinked );
+
+ int nCmp = cmp( key, pNode->m_key );
+ if ( nCmp == 0 )
+ return try_remove_node( pParent, pNode, nVersion, func, disp );
+
+ int result;
+ do {
+ node_type * pChild = child( pNode, nCmp, memory_model::memory_order_relaxed );
+ if ( pNode->version( memory_model::memory_order_acquire ) != nVersion )
+ return update_flags::retry;
+
+ if ( pChild == nullptr ) {
+ return update_flags::failed;
+ }
+ else {
+ // update child
+ result = update_flags::retry;
+ version_type nChildVersion = pChild->version( memory_model::memory_order_acquire );
+ if ( nChildVersion & node_type::shrinking ) {
+ m_stat.onUpdateWaitShrinking();
+ pChild->template wait_until_shrink_completed<back_off>( memory_model::memory_order_relaxed );
+ // retry
+ }
+ else if ( pChild == child( pNode, nCmp, memory_model::memory_order_relaxed )) {
+ // this second read is important, because it is protected by nChildVersion
+
+ // validate the read that our caller took to get to node
+ if ( pNode->version( memory_model::memory_order_relaxed ) != nVersion ) {
+ m_stat.onUpdateRetry();
+ return update_flags::retry;
+ }
+
+ // At this point we know that the traversal our parent took to get to node is still valid.
+ // The recursive implementation will validate the traversal from node to
+ // child, so just prior to the node nVersion validation both traversals were definitely okay.
+ // This means that we are no longer vulnerable to node shrinks, and we don't need
+ // to validate node version any more.
+ result = try_remove( key, cmp, func, pNode, pChild, nChildVersion, disp );
+ }
+ }
+ } while ( result == update_flags::retry );
+ return result;
+ }
+
template <typename K, typename Func>
int try_insert_node( K const& key, Func funcUpdate, node_type * pNode, int nDir, version_type nVersion, rcu_disposer& disp )
{
node_type * pNew;
auto fnCreateNode = [&funcUpdate]( node_type * pNew ) {
- mapped_type * pVal = funcUpdate( pNew );
+ mapped_type pVal = funcUpdate( pNew );
assert( pVal != nullptr );
pNew->m_pValue.store( pVal, memory_model::memory_order_relaxed );
};
if ( pNode->version( memory_model::memory_order_relaxed ) != nVersion
|| pNode->child( nDir ).load( memory_model::memory_order_relaxed ) != nullptr )
{
- if ( c_RelaxedInsert ) {
+ if ( c_bRelaxedInsert ) {
free_node( pNew );
m_stat.onRelaxedInsertFailed();
}
template <typename Func>
int try_update_node( Func funcUpdate, node_type * pNode )
{
- mapped_type * pOld;
+ mapped_type pOld;
{
assert( pNode != nullptr );
node_scoped_lock l( m_Monitor, *pNode );
}
pOld = pNode->value( memory_model::memory_order_relaxed );
- mapped_type * pVal = funcUpdate( *pNode );
+ mapped_type pVal = funcUpdate( pNode );
assert( pVal != nullptr );
pNode->m_pValue.store( pVal, memory_model::memory_order_relaxed );
}
if ( pOld ) {
disposer()(pOld);
- m_stat.onDisposeValue();
+ m_stat.onDisposeNode();
}
m_stat.onUpdateSuccess();
if ( !pNode->is_valued( atomics::memory_order_relaxed ) )
return update_flags::failed;
- if ( pNode->child( -1, atomics::memory_order_relaxed ) == nullptr
- || pNode->child( 1, atomics::memory_order_relaxed ) == nullptr )
+ if ( pNode->child( -1 ).load( atomics::memory_order_relaxed ) == nullptr
+ || pNode->child( 1 ).load( atomics::memory_order_relaxed ) == nullptr )
{
node_type * pDamaged;
- mapped_type * pOld;
+ mapped_type pOld;
{
node_scoped_lock lp( m_Monitor, *pParent );
- if ( pParent->is_unlinked( atomics::memory_order_relaxed ) || pNode->m_pParent.load( atomics::memory_order_relaxed ) != pParent )
+ if ( pParent->is_unlinked( atomics::memory_order_relaxed ) || parent( pNode, memory_model::memory_order_relaxed ) != pParent )
return update_flags::retry;
{
node_scoped_lock ln( m_Monitor, *pNode );
- pOld = pNode->value( atomics::memory_order_relaxed );
- if ( pNode->version( atomics::memory_order_relaxed ) == nVersion
+ pOld = pNode->value( memory_model::memory_order_relaxed );
+ if ( pNode->version( memory_model::memory_order_relaxed ) == nVersion
&& pOld
&& try_unlink_locked( pParent, pNode, disp ) )
{
}
func( pOld ); // calls pOld disposer inside
- m_stat.onDisposeValue();
+ m_stat.onDisposeNode();
fix_height_and_rebalance( pDamaged, disp );
- return upfate_flags::result_removed;
+ return update_flags::result_removed;
}
else {
int result = update_flags::retry;
+ mapped_type pOld;
{
node_scoped_lock ln( m_Monitor, *pNode );
- mapped_type * pOld = pNode->value( atomics::memory_order_relaxed );
+ pOld = pNode->value( atomics::memory_order_relaxed );
if ( pNode->version( atomics::memory_order_relaxed ) == nVersion && pOld ) {
pNode->m_pValue.store( nullptr, atomics::memory_order_relaxed );
- result = upfate_flags::result_removed;
+ result = update_flags::result_removed;
}
}
- if ( result == upfate_flags::result_removed ) {
- func( *pOld ); // calls pOld disposer inside
- m_stat.onDisposeValue();
+ if ( result == update_flags::result_removed ) {
+ func( pOld ); // calls pOld disposer inside
+ m_stat.onDisposeNode();
}
return result;
// pParent and pNode must be locked
assert( !pParent->is_unlinked(memory_model::memory_order_relaxed) );
- node_type * pParentLeft = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
- node_type * pParentRight = pParent->m_pRight.load( memory_model::memory_order_relaxed );
+ node_type * pParentLeft = child( pParent, -1, memory_model::memory_order_relaxed );
+ node_type * pParentRight = child( pParent, 1, memory_model::memory_order_relaxed );
if ( pNode != pParentLeft && pNode != pParentRight ) {
// node is no longer a child of parent
return false;
}
- assert( !pNode->is_unlinked());
- assert( pParent == pNode->m_pParent.load(memory_model::memory_order_relaxed));
+ assert( !pNode->is_unlinked( memory_model::memory_order_relaxed ) );
+ assert( pParent == parent( pNode, memory_model::memory_order_relaxed));
- node_type * pLeft = pNode->m_pLeft.load( memory_model::memory_order_relaxed );
- node_type * pRight = pNode->m_pRight.load( memory_model::memory_order_relaxed );
+ node_type * pLeft = child( pNode, -1, memory_model::memory_order_relaxed );
+ node_type * pRight = child( pNode, 1, memory_model::memory_order_relaxed );
if ( pLeft != nullptr && pRight != nullptr ) {
// splicing is no longer possible
return false;
pParent->m_pRight.store( pSplice, memory_model::memory_order_relaxed );
if ( pSplice )
- pSplice->pParent.store( pParent, memory_model::memory_order_release );
+ pSplice->m_pParent.store( pParent, memory_model::memory_order_release );
// Mark the node as unlinked
pNode->version( node_type::unlinked, memory_model::memory_order_release );
//@cond
int estimate_node_condition( node_type * pNode )
{
- node_type * pLeft = pNode->m_pLeft.load( memory_model::memory_order_relaxed );
- node_type * pRight = pNode->m_pRight.load( memory_model::memory_order_relaxed );
+ node_type * pLeft = child( pNode, -1, memory_model::memory_order_relaxed );
+ node_type * pRight = child( pNode, 1, memory_model::memory_order_relaxed );
if ( (pLeft == nullptr || pRight == nullptr) && pNode->value( memory_model::memory_order_relaxed ) == nullptr )
return unlink_required;
return nullptr;
default:
pNode->height( h, memory_model::memory_order_relaxed );
- return pNode->m_pParent.load( memory_model::memory_order_relaxed );
+ return parent( pNode, memory_model::memory_order_relaxed );
}
}
void fix_height_and_rebalance( node_type * pNode, rcu_disposer& disp )
{
- while ( pNode && pNode->m_pParent.load( memory_model::memory_order_relaxed ) ) {
+ while ( pNode && pNode->m_pParent.load( memory_model::memory_order_relaxed )) {
int nCond = estimate_node_condition( pNode );
if ( nCond == nothing_required || pNode->is_unlinked( memory_model::memory_order_relaxed ) )
return;
if ( nCond != unlink_required && nCond != rebalance_required )
pNode = fix_height( pNode );
else {
- node_type * pParent = pNode->m_pParent.load( memory_model::memry_order_relaxed );
+ node_type * pParent = parent( pNode, memory_model::memory_order_relaxed );
assert( pParent != nullptr );
{
node_scoped_lock lp( m_Monitor, *pParent );
if ( !pParent->is_unlinked( memory_model::memory_order_relaxed )
- && pNode->m_pParent.load( memory_model::memory_order_relaxed ) == pParent )
+ && parent( pNode, memory_model::memory_order_relaxed ) == pParent )
{
node_scoped_lock ln( m_Monitor, *pNode );
pNode = rebalance_locked( pParent, pNode, disp );
{
// pParent and pNode should be locked.
// Returns a damaged node, or nullptr if no more rebalancing is necessary
- assert( pNode->m_pParent.load( memory_model::memry_order_relaxed ) == pNode );
- assert( m_pParent->m_pLeft.load( memory_model::memory_order_relaxed ) == pNode
- || m_pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
+ assert( parent( pNode, memory_model::memory_order_relaxed ) == pParent );
+ assert( child( pParent, -1, memory_model::memory_order_relaxed ) == pNode
+ || child( pParent, 1, memory_model::memory_order_relaxed ) == pNode );
- node_type * pLeft = pNode->m_pLeft.load( memory_model::memory_order_relaxed );
- node_type * pRight = pNode->m_pRight.load( memory_model::memory_order_relaxed );
+ node_type * pLeft = child( pNode, -1, memory_model::memory_order_relaxed );
+ node_type * pRight = child( pNode, 1, memory_model::memory_order_relaxed );
if ( (pLeft == nullptr || pRight == nullptr) && pNode->value( memory_model::memory_order_relaxed ) == nullptr ) {
if ( try_unlink_locked( pParent, pNode, disp ))
node_type * rebalance_to_right_locked( node_type * pParent, node_type * pNode, node_type * pLeft, int hR )
{
- assert( pNode->m_pParent.load( memory_model::memry_order_relaxed ) == pNode );
- assert( m_pParent->m_pLeft.load( memory_model::memory_order_relaxed ) == pNode
- || m_pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
+ assert( parent( pNode, memory_model::memory_order_relaxed ) == pParent );
+ assert( child( pParent, -1, memory_model::memory_order_relaxed ) == pNode
+ || child( pParent, 1, memory_model::memory_order_relaxed ) == pNode );
// pParent and pNode is locked yet
// pNode->pLeft is too large, we will rotate-right.
if ( hL - hR <= 1 )
return pNode; // retry
- node_type * pLRight = pLeft->m_pRight.load( memory_model::memory_order_relaxed );
+ node_type * pLRight = child( pLeft, 1, memory_model::memory_order_relaxed );
int hLR = pLRight ? pLRight->height( memory_model::memory_order_relaxed ) : 0;
- node_type * pLLeft = pLeft->m_pLeft.load( memory_model::memory_order_relaxed );
+ node_type * pLLeft = child( pLeft, -1, memory_model::memory_order_relaxed );
int hLL = pLLeft ? pLLeft->height( memory_model::memory_order_relaxed ) : 0;
if ( hLL > hLR ) {
if ( hLL > hLR )
return rotate_right_locked( pParent, pNode, pLeft, hR, hLL, pLRight, hLR );
- node_type * pLRLeft = pLRight->m_pLeft.load( memory_model::memory_order_relaxed );
+ node_type * pLRLeft = child( pLRight, -1, memory_model::memory_order_relaxed );
int hLRL = pLRLeft ? pLRLeft->height( memory_model::memory_order_relaxed ) : 0;
int balance = hLL - hLRL;
if ( balance >= -1 && balance <= 1 && !((hLL == 0 || hLRL == 0) && pLeft->value(memory_model::memory_order_relaxed) == nullptr) ) {
node_type * rebalance_to_left_locked( node_type * pParent, node_type * pNode, node_type * pRight, int hL )
{
- assert( pNode->m_pParent.load( memory_model::memry_order_relaxed ) == pNode );
- assert( m_pParent->m_pLeft.load( memory_model::memory_order_relaxed ) == pNode
- || m_pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
+ assert( parent( pNode, memory_model::memory_order_relaxed ) == pParent );
+ assert( child( pParent, -1, memory_model::memory_order_relaxed ) == pNode
+ || child( pParent, 1, memory_model::memory_order_relaxed ) == pNode );
// pParent and pNode is locked yet
{
if ( hL - hR >= -1 )
return pNode; // retry
- node_type * pRLeft = pRight->m_pLeft.load( memory_model::memory_order_relaxed );
- int hRL = pRLeft > pRLeft->height( memory_model::memory_order_relaxed ) : 0;
- node_type * pRRight = pRight->m_pRight.load( memory_model::memory_order_relaxed );
+ node_type * pRLeft = child( pRight, -1, memory_model::memory_order_relaxed );
+ int hRL = pRLeft ? pRLeft->height( memory_model::memory_order_relaxed ) : 0;
+ node_type * pRRight = child( pRight, 1, memory_model::memory_order_relaxed );
int hRR = pRRight ? pRRight->height( memory_model::memory_order_relaxed ) : 0;
if ( hRR > hRL )
return rotate_left_locked( pParent, pNode, hL, pRight, pRLeft, hRL, hRR );
if ( hRR >= hRL )
return rotate_left_locked( pParent, pNode, hL, pRight, pRLeft, hRL, hRR );
- node_type * pRLRight = pRLeft->m_pRight.load( memory_model::memory_order_relaxed );
+ node_type * pRLRight = child( pRLeft, 1, memory_model::memory_order_relaxed );
int hRLR = pRLRight ? pRLRight->height( memory_model::memory_order_relaxed ) : 0;
int balance = hRR - hRLR;
- if ( b >= -1 && b <= 1 && !((hRR == 0 || hRLR == 0) && pRight->value( memory_odel::memory_order_relaxed ) == nullptr)
+ if ( balance >= -1 && balance <= 1 && !((hRR == 0 || hRLR == 0) && pRight->value( memory_model::memory_order_relaxed ) == nullptr))
return rotate_left_over_right_locked( pParent, pNode, hL, pRight, pRLeft, hRR, hRLR );
}
return rebalance_to_right_locked( pNode, pRight, pRLeft, hRR );
node_type * rotate_right_locked( node_type * pParent, node_type * pNode, node_type * pLeft, int hR, int hLL, node_type * pLRight, int hLR )
{
version_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
- node_type * pParentLeft = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
+ node_type * pParentLeft = child( pParent, -1, memory_model::memory_order_relaxed );
begin_change( pNode, nodeVersion );
if ( pParentLeft == pNode )
pParent->m_pLeft.store( pLeft, memory_model::memory_order_relaxed );
else {
- assert( pParent->m_pRight.load( memory_odel::memory_order_relaxed ) == pNode );
- pParent->m_pRight.store( pLeft, memory_mdel::memory_order_relaxed );
+ assert( pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
+ pParent->m_pRight.store( pLeft, memory_model::memory_order_relaxed );
}
pLeft->m_pParent.store( pParent, memory_model::memory_order_relaxed );
node_type * rotate_left_locked( node_type * pParent, node_type * pNode, int hL, node_type * pRight, node_type * pRLeft, int hRL, int hRR )
{
version_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
- node_type * pParentLeft = pParent->m_pLeft.load( memory_odel::memory_order_relaxed );
+ node_type * pParentLeft = child( pParent, -1, memory_model::memory_order_relaxed );
begin_change( pNode, nodeVersion );
// fix up pNode links, careful to be compatible with concurrent traversal for all but pNode
- pNode->m_pRight.store( pRLeft, memory_nodel::memory_order_relaxed );
+ pNode->m_pRight.store( pRLeft, memory_model::memory_order_relaxed );
if ( pRLeft != nullptr )
pRLeft->m_pParent.store( pNode, memory_model::memory_order_relaxed );
if ( pParentLeft == pNode )
pParent->m_pLeft.store( pRight, memory_model::memory_order_relaxed );
else {
- assert( pParent->m_pRight.load( memory_odel::memory_order_relaxed ) == pNode );
- pParent->m_pRight.store( pRight, memory_model::memory_model_relaxed );
+ assert( pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
+ pParent->m_pRight.store( pRight, memory_model::memory_order_relaxed );
}
pRight->m_pParent.store( pParent, memory_model::memory_order_relaxed );
node_type * rotate_right_over_left_locked( node_type * pParent, node_type * pNode, node_type * pLeft, int hR, int hLL, node_type * pLRight, int hLRL )
{
- typename node_type::value_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
- typename node_type::value_type leftVersion = pLeft->version( memory_model::memory_order_relaxed );
+ version_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
+ version_type leftVersion = pLeft->version( memory_model::memory_order_relaxed );
- node_type * pPL = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
- node_type * pLRL = pLRight->m_pLeft.load( memory_model::memory_order_relaxed );
- node_type * pLRR = pLRight->m_pRight.load( memory_model::memory_order_relaxed );
- int hLRR = pLRR ? pLRR->hight( memory_model::memory_order_relaxed ) : 0;
+ node_type * pPL = child( pParent, -1, memory_model::memory_order_relaxed );
+ node_type * pLRL = child( pLRight, -1, memory_model::memory_order_relaxed );
+ node_type * pLRR = child( pLRight, 1, memory_model::memory_order_relaxed );
+ int hLRR = pLRR ? pLRR->height( memory_model::memory_order_relaxed ) : 0;
begin_change( pNode, nodeVersion );
begin_change( pLeft, leftVersion );
if ( pPL == pNode )
pParent->m_pLeft.store( pLRight, memory_model::memory_order_relaxed );
else {
- assert( Parent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
+ assert( child( pParent, 1, memory_model::memory_order_relaxed ) == pNode );
pParent->m_pRight.store( pLRight, memory_model::memory_order_relaxed );
}
pLRight->m_pParent.store( pParent, memory_model::memory_order_relaxed );
}
// we've already fixed the height at pLRight, do we need a rotation here?
- final int balanceLR = hLeft - hNode;
+ int balanceLR = hLeft - hNode;
if ( balanceLR < -1 || balanceLR > 1 )
- return pLR;
+ return pLRight;
// try to fix the parent height while we've still got the lock
return fix_height_locked( pParent );
version_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
version_type rightVersion = pRight->version( memory_model::memory_order_relaxed );
- node_type * pPL = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
- node_type * pRLL = pRLeft->m_pLeft.load( memory_model::memory_order_relaxed );
- node_type * pRLR = pRLeft->m_pRight.load( memory_model::memory_order_relaxed );
+ node_type * pPL = child( pParent, -1, memory_model::memory_order_relaxed );
+ node_type * pRLL = child( pRLeft, -1, memory_model::memory_order_relaxed );
+ node_type * pRLR = child( pRLeft, 1, memory_model::memory_order_relaxed );
int hRLL = pRLL ? pRLL->height( memory_model::memory_order_relaxed ) : 0;
begin_change( pNode, nodeVersion );
- begin_change( pRight, ghtVersion );
+ begin_change( pRight, rightVersion );
// fix up pNode links, careful about the order!
pNode->m_pRight.store( pRLL, memory_model::memory_order_relaxed );
size_t nEnsureNewFuncCall;
size_t nEraseFuncCall;
size_t nFindFuncCall;
- size_t nFindConstFuncCall;
stat_data()
: nInsertFuncCall( 0 )
, nEnsureNewFuncCall( 0 )
, nEraseFuncCall( 0 )
, nFindFuncCall( 0 )
- , nFindConstFuncCall( 0 )
{}
};
struct find_functor
{
- template <typename T>
- void operator()( value_type& i, T& /*val*/ )
- {
- ++i.stat.nFindFuncCall;
- }
- template <typename T>
- void operator()( value_type& i, T const& /*val*/ )
+ void operator()( key_type, value_type& v ) const
{
- ++i.stat.nFindConstFuncCall;
+ ++v.stat.nFindFuncCall;
}
};
{
Item m_found;
- template <typename T>
- void operator()( Item& i, T& /*val*/ )
+ void operator()( key_type const&, Item& v )
{
- m_found = i;
+ m_found = v;
}
- void operator()( Item const& i )
+ void operator()( Item& v )
{
- m_found = i;
+ m_found = v;
}
};
struct insert_functor
{
template <typename Item>
- void operator()( Item& i )
+ void operator()( key_type key, Item& i )
{
- i.nVal = i.nKey * 100;
+ i.nVal = key * 100;
++i.stat.nInsertFuncCall;
}
};
template <typename Q>
- static void ensure_func( bool bNew, value_type& i, Q& /*val*/ )
+ static void ensure_func( bool bNew, Q /*key*/, value_type& i )
{
if ( bNew )
++i.stat.nEnsureNewFuncCall;
struct ensure_functor
{
template <typename Q>
- void operator()( bool bNew, value_type& i, Q& val )
+ void operator()( bool bNew, Q key, value_type& i )
{
- ensure_func( bNew, i, val );
+ ensure_func( bNew, key, i );
}
};
{
int nKey;
- template <typename Q>
- void operator()( Q&, value_type& v )
+ void operator()( value_type& v )
{
- nKey = v.nKey;
+ nKey = v.nVal;
}
};
CPPUNIT_ASSERT( !s.empty() );
CPPUNIT_ASSERT( check_size( s, 1 ) );
- CPPUNIT_ASSERT( !s.find_with( 20, less() ) );
- CPPUNIT_ASSERT( s.insert( std::make_pair( 20, 25 ) ) );
+ CPPUNIT_ASSERT( !s.find_with( 20, std::less<key_type>() ) );
+ CPPUNIT_ASSERT( s.insert( 20, 25 ) );
CPPUNIT_ASSERT( !s.empty() );
CPPUNIT_ASSERT( check_size( s, 2 ) );
- CPPUNIT_ASSERT( s.find_with( 10, less() ) );
+ CPPUNIT_ASSERT( s.find_with( 10, std::less<key_type>() ) );
CPPUNIT_ASSERT( s.find( key = 20 ) );
- CPPUNIT_ASSERT( s.find_with( key, less(), find_functor() ) );
+ CPPUNIT_ASSERT( s.find_with( key, std::less<key_type>(), find_functor() ) );
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
key = 20;
CPPUNIT_ASSERT( s.find( key, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 20 );
CPPUNIT_ASSERT( f.m_found.nVal == 25 );
CPPUNIT_ASSERT( f.m_found.stat.nFindFuncCall == 1 );
- CPPUNIT_ASSERT( f.m_found.stat.nFindConstFuncCall == 0 );
}
CPPUNIT_ASSERT( s.find( key, find_functor() ) );
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
key = 20;
- CPPUNIT_ASSERT( s.find_with( key, less(), std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 20 );
+ CPPUNIT_ASSERT( s.find_with( key, std::less<key_type>(), std::ref( f ) ) );
CPPUNIT_ASSERT( f.m_found.nVal == 25 );
CPPUNIT_ASSERT( f.m_found.stat.nFindFuncCall == 2 );
- CPPUNIT_ASSERT( f.m_found.stat.nFindConstFuncCall == 0 );
}
CPPUNIT_ASSERT( s.find( 20, find_functor() ) );
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
- CPPUNIT_ASSERT( s.find_with( 20, less(), std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 20 );
+ CPPUNIT_ASSERT( s.find_with( 20, std::less<key_type>(), std::ref( f ) ) );
CPPUNIT_ASSERT( f.m_found.nVal == 25 );
- CPPUNIT_ASSERT( f.m_found.stat.nFindFuncCall == 2 );
- CPPUNIT_ASSERT( f.m_found.stat.nFindConstFuncCall == 1 );
+ CPPUNIT_ASSERT( f.m_found.stat.nFindFuncCall == 3 );
}
CPPUNIT_ASSERT( !s.empty() );
CPPUNIT_ASSERT( check_size( s, 2 ) );
CPPUNIT_ASSERT( !s.find( 25 ) );
- CPPUNIT_ASSERT( s.insert( std::make_pair( 25, -1 ), insert_functor() ) );
+ CPPUNIT_ASSERT( s.insert_with( 25, insert_functor() ) );
CPPUNIT_ASSERT( !s.empty() );
CPPUNIT_ASSERT( check_size( s, 3 ) );
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
key = 25;
CPPUNIT_ASSERT( s.find( key, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 25 );
CPPUNIT_ASSERT( f.m_found.nVal == 2500 );
CPPUNIT_ASSERT( f.m_found.stat.nInsertFuncCall == 1 );
}
key = 10;
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
CPPUNIT_ASSERT( s.find( key, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 10 );
CPPUNIT_ASSERT( f.m_found.nVal == 100 );
CPPUNIT_ASSERT( f.m_found.stat.nEnsureExistFuncCall == 0 );
CPPUNIT_ASSERT( f.m_found.stat.nEnsureNewFuncCall == 0 );
CPPUNIT_ASSERT( check_size( s, 3 ) );
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
CPPUNIT_ASSERT( s.find( key, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 10 );
CPPUNIT_ASSERT( f.m_found.nVal == 100 );
CPPUNIT_ASSERT( f.m_found.stat.nEnsureExistFuncCall == 1 );
CPPUNIT_ASSERT( f.m_found.stat.nEnsureNewFuncCall == 0 );
}
- ensureResult = s.update( std::make_pair( 13, 1300 ), ensure_functor() );
+ ensureResult = s.update( 13, []( bool /*bNew*/, key_type key, value_type& v )
+ {
+ v.nVal = key * 100;
+ ++v.stat.nEnsureExistFuncCall;
+ });
CPPUNIT_ASSERT( ensureResult.first && ensureResult.second );
CPPUNIT_ASSERT( !s.empty() );
CPPUNIT_ASSERT( check_size( s, 4 ) );
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
key = 13;
CPPUNIT_ASSERT( s.find( key, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 13 );
CPPUNIT_ASSERT( f.m_found.nVal == 1300 );
CPPUNIT_ASSERT( f.m_found.stat.nEnsureExistFuncCall == 0 );
CPPUNIT_ASSERT( f.m_found.stat.nEnsureNewFuncCall == 1 );
CPPUNIT_ASSERT( check_size( s, 3 ) );
CPPUNIT_ASSERT( s.find( 10 ) );
- CPPUNIT_ASSERT( s.erase_with( 10, less() ) );
+ CPPUNIT_ASSERT( s.erase_with( 10, std::less<key_type>() ) );
CPPUNIT_ASSERT( !s.find( 10 ) );
CPPUNIT_ASSERT( !s.empty() );
CPPUNIT_ASSERT( check_size( s, 2 ) );
- CPPUNIT_ASSERT( !s.erase_with( 10, less() ) );
+ CPPUNIT_ASSERT( !s.erase_with( 10, std::less<key_type>() ) );
CPPUNIT_ASSERT( !s.empty() );
CPPUNIT_ASSERT( check_size( s, 2 ) );
CPPUNIT_ASSERT( s.find( 20 ) );
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
CPPUNIT_ASSERT( s.erase( 20, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 20 );
CPPUNIT_ASSERT( f.m_found.nVal == 25 );
- CPPUNIT_ASSERT( s.insert( 235 ) )
- CPPUNIT_ASSERT( s.erase_with( 235, less(), std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 235 );
+ CPPUNIT_ASSERT( s.insert( 235, 2350 ) );
+ CPPUNIT_ASSERT( s.erase_with( 235, std::less<key_type>(), std::ref( f ) ) );
CPPUNIT_ASSERT( f.m_found.nVal == 2350 );
}
CPPUNIT_ASSERT( !s.find( 20 ) );
CPPUNIT_ASSERT( check_size( s, 0 ) );
// emplace test
- CPPUNIT_ASSERT( s.emplace( 151 ) ); // key = 151, val = 1510
+ CPPUNIT_ASSERT( s.emplace( 151 ) ); // key = 151, val=0
CPPUNIT_ASSERT( s.emplace( 174, 471 ) ); // key = 174, val = 471
- CPPUNIT_ASSERT( s.emplace( std::make_pair( 190, 91 ) ) ); // key == 190, val = 91
CPPUNIT_ASSERT( !s.empty() );
- CPPUNIT_ASSERT( check_size( s, 3 ) );
+ CPPUNIT_ASSERT( check_size( s, 2 ) );
CPPUNIT_ASSERT( s.find( 151 ) );
- CPPUNIT_ASSERT( s.find_with( 174, less() ) );
- CPPUNIT_ASSERT( s.find( 190 ) );
+ CPPUNIT_ASSERT( s.find_with( 174, std::less<key_type>() ) );
+ CPPUNIT_ASSERT( !s.find( 190 ) );
{
copy_found<value_type> f;
- f.m_found.nKey = 0;
key = 151;
CPPUNIT_ASSERT( s.find( key, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 151 );
- CPPUNIT_ASSERT( f.m_found.nVal == 1510 );
+ CPPUNIT_ASSERT( f.m_found.nVal == 0 );
key = 174;
CPPUNIT_ASSERT( s.find( key, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 174 );
CPPUNIT_ASSERT( f.m_found.nVal == 471 );
-
- key = 190;
- CPPUNIT_ASSERT( s.find( key, std::ref( f ) ) );
- CPPUNIT_ASSERT( f.m_found.nKey == 190 );
- CPPUNIT_ASSERT( f.m_found.nVal == 91 );
}
s.clear();