3 #ifndef CDSLIB_CONTAINER_IMPL_BRONSON_AVLTREE_MAP_RCU_H
4 #define CDSLIB_CONTAINER_IMPL_BRONSON_AVLTREE_MAP_RCU_H
6 #include <memory> // unique_ptr
7 #include <cds/container/details/bronson_avltree_base.h>
8 #include <cds/urcu/details/check_deadlock.h>
9 #include <cds/details/binary_functor_wrapper.h>
12 namespace cds { namespace container {
14 /// Bronson et al AVL-tree (RCU specialization for storing pointer to values)
15 /** @ingroup cds_nonintrusive_map
16 @ingroup cds_nonintrusive_tree
17 @headerfile cds/container/bronson_avltree_map_rcu.h
19 This is the specialization of \ref cds_container_BronsonAVLTreeMap_rcu "RCU-based Bronson et al AVL-tree"
20 for "key -> value pointer" map. This specialization stores the pointer to user-allocated values instead of the copy
21 of the value. When a tree node is removed, the algorithm does not free the value pointer directly, instead, it call
22 the disposer functor provided by \p Traits template parameter.
24 <b>Template arguments</b>:
25 - \p RCU - one of \ref cds_urcu_gc "RCU type"
27 - \p T - value type to be stored in tree's nodes. Note, the specialization stores the pointer to user-allocated
29 - \p Traits - tree traits, default is \p bronson_avltree::traits
30 It is possible to declare option-based tree with \p bronson_avltree::make_traits metafunction
31 instead of \p Traits template argument.
33 @note Before including <tt><cds/container/bronson_avltree_map_rcu.h></tt> you should include appropriate RCU header file,
34 see \ref cds_urcu_gc "RCU type" for list of existing RCU class and corresponding header files.
40 # ifdef CDS_DOXYGEN_INVOKED
41 typename Traits = bronson_avltree::traits
46 class BronsonAVLTreeMap< cds::urcu::gc<RCU>, Key, T*, Traits >
49 typedef cds::urcu::gc<RCU> gc; ///< RCU Garbage collector
50 typedef Key key_type; ///< type of a key stored in the map
51 typedef T * mapped_type; ///< type of value stored in the map
52 typedef Traits traits; ///< Traits template parameter
54 # ifdef CDS_DOXYGEN_INVOKED
55 typedef implementation_defined key_comparator; ///< key compare functor based on \p Traits::compare and \p Traits::less
57 typedef typename opt::details::make_comparator< key_type, traits >::type key_comparator;
59 typedef typename traits::item_counter item_counter; ///< Item counting policy
60 typedef typename traits::memory_model memory_model; ///< Memory ordering, see \p cds::opt::memory_model option
61 typedef typename traits::node_allocator node_allocator_type; ///< allocator for maintaining internal nodes
62 typedef typename traits::stat stat; ///< internal statistics
63 typedef typename traits::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy
64 typedef typename traits::back_off back_off; ///< Back-off strategy
65 typedef typename traits::disposer disposer; ///< Value disposer
66 typedef typename traits::sync_monitor sync_monitor; ///< @ref cds_sync_monitor "Synchronization monitor" type for node-level locking
68 /// Enabled or disabled @ref bronson_avltree::relaxed_insert "relaxed insertion"
69 static bool const c_bRelaxedInsert = traits::relaxed_insert;
71 /// Pointer to removed value object
72 typedef std::unique_ptr< mapped_type, disposer > exempt_ptr;
76 typedef typename sync_monitor::template node_injection< bronson_avltree::node< key_type, mapped_type >> node_type;
77 typedef typename node_type::version_type version_type;
79 typedef cds::details::Allocator< node_type, node_allocator_type > cxx_allocator;
80 typedef cds::urcu::details::check_deadlock_policy< gc, rcu_check_deadlock > check_deadlock_policy;
82 enum class find_result
89 enum class update_flags
98 result_inserted = allow_insert,
99 result_updated = allow_update,
100 result_removed = allow_remove
105 nothing_required = -3,
106 rebalance_required = -2,
110 typedef typename sync_monitor::template scoped_lock<node_type> node_scoped_lock;
115 template <typename K>
116 static node_type * alloc_node( K&& key, int nHeight, version_type version, node_type * pParent, node_type * pLeft, node_type * pRight )
118 return cxx_allocator().New( std::forward<K>( key ), nHeight, version, pParent, pLeft, pRight );
121 static void free_node( node_type * pNode )
123 // Free node without disposer
124 cxx_allocator().Delete( pNode );
130 node_type * m_pRetiredList; ///< head of retired list
134 : m_pRetiredList( nullptr )
142 void dispose( node_type * pNode )
144 mapped_type * pVal = pNode->value( memory_model::memory_order_relaxed );
146 pNode->m_pValue.store( nullptr, memory_model::memory_order_relaxed );
150 pNode->m_pNextRemoved = m_pRetiredList;
151 m_pRetiredList = pNode;
155 struct internal_disposer
157 void operator()( node_type * p ) const
165 assert( !gc::is_locked() );
167 for ( node_type * p = m_pRetiredList; p; ) {
168 node_type * pNext = p->m_pNextRemoved;
169 // Value already disposed
170 gc::template retire_ptr<internal_disposer>( p );
180 typename node_type::base_class m_Root;
182 item_counter m_ItemCounter;
183 mutable sync_monitor m_Monitor;
188 /// Creates empty map
190 : m_pRoot( static_cast<node_type *>(&m_Root) )
201 The \p key_type should be constructible from a value of type \p K.
203 RCU \p synchronize() can be called. RCU should not be locked.
205 Returns \p true if inserting successful, \p false otherwise.
207 template <typename K>
208 bool insert( K const& key, mapped_type * pVal )
210 return do_update(key, key_comparator(),
211 [pVal]( node_type * pNode ) -> mapped_type*
213 assert( pNode->m_pValue.load( memory_model::memory_order_relaxed ) == nullptr );
216 update_flags::allow_insert
217 ) == update_flags::result_inserted;
220 /// Updates the value for \p key
222 The operation performs inserting or updating the value for \p key with lock-free manner.
223 If \p bInsert is \p false, only updating of existing node is possible.
225 If \p key is not found and inserting is allowed (i.e. \p bInsert is \p true),
226 then the new node created from \p key is inserted into the map; note that in this case the \ref key_type should be
227 constructible from type \p K.
228 Otherwise, the value is changed to \p pVal.
230 RCU \p synchronize() method can be called. RCU should not be locked.
232 Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
233 \p second is \p true if new node has been added or \p false if the node with \p key
234 already is in the tree.
236 template <typename K, typename Func>
237 std::pair<bool, bool> update( K const& key, mapped_type * pVal, bool bInsert = true )
239 int result = do_update( key, key_comparator(),
240 [pVal]( node_type * ) -> mapped_type*
244 update_flags::allow_update | (bInsert ? update_flags::allow_insert : 0)
246 return std::make_pair( result != 0, (result & update_flags::result_inserted) != 0 );
249 /// Delete \p key from the map
251 RCU \p synchronize() method can be called. RCU should not be locked.
253 Return \p true if \p key is found and deleted, \p false otherwise
255 template <typename K>
256 bool erase( K const& key )
261 []( mapped_type * pVal ) { disposer()(pVal); },
262 update_flags::allow_remove
263 ) == update_flags::result_removed;
266 /// Deletes the item from the map using \p pred predicate for searching
268 The function is an analog of \p erase(K const&)
269 but \p pred is used for key comparing.
270 \p Less functor has the interface like \p std::less.
271 \p Less must imply the same element order as the comparator used for building the map.
273 template <typename K, typename Less>
274 bool erase_with( K const& key, Less pred )
279 cds::details::predicate_wrapper<key_type, Less, cds::details::trivial_accessor >(),
280 []( mapped_type * pVal ) { disposer()(pVal); },
281 update_flags::allow_remove
282 ) == update_flags::result_removed;
285 /// Delete \p key from the map
287 The function searches an item with key \p key, calls \p f functor
288 and deletes the item. If \p key is not found, the functor is not called.
290 The functor \p Func interface:
293 void operator()(mapped_type& item) { ... }
297 RCU \p synchronize method can be called. RCU should not be locked.
299 Return \p true if key is found and deleted, \p false otherwise
301 template <typename K, typename Func>
302 bool erase( K const& key, Func f )
307 [&f]( mapped_type * pVal ) { f( *pVal ); disposer()(pVal); },
308 update_flags::allow_remove
309 ) == update_flags::result_removed;
312 /// Deletes the item from the map using \p pred predicate for searching
314 The function is an analog of \p erase(K const&, Func)
315 but \p pred is used for key comparing.
316 \p Less functor has the interface like \p std::less.
317 \p Less must imply the same element order as the comparator used for building the map.
319 template <typename K, typename Less, typename Func>
320 bool erase_with( K const& key, Less pred, Func f )
325 cds::details::predicate_wrapper<key_type, Less, cds::details::trivial_accessor >(),
326 [&f]( mapped_type * pVal ) { f( *pVal ); disposer()(pVal); },
327 update_flags::allow_remove
328 ) == update_flags::result_removed;
331 /// Extracts an item with minimal key from the map
333 Returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to the leftmost item.
334 If the set is empty, returns empty \p exempt_ptr.
336 @note Due the concurrent nature of the map, the function extracts <i>nearly</i> minimum key.
337 It means that the function gets leftmost leaf of the tree and tries to unlink it.
338 During unlinking, a concurrent thread may insert an item with key less than leftmost item's key.
339 So, the function returns the item with minimum key at the moment of tree traversing.
341 RCU \p synchronize method can be called. RCU should NOT be locked.
342 The function does not free the item.
343 The deallocator will be implicitly invoked when the returned object is destroyed or when
344 its \p release() member function is called.
346 exempt_ptr extract_min()
351 /// Extracts an item with maximal key from the map
353 Returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to the rightmost item.
354 If the set is empty, returns empty \p exempt_ptr.
356 @note Due the concurrent nature of the map, the function extracts <i>nearly</i> maximal key.
357 It means that the function gets rightmost leaf of the tree and tries to unlink it.
358 During unlinking, a concurrent thread may insert an item with key great than leftmost item's key.
359 So, the function returns the item with maximum key at the moment of tree traversing.
361 RCU \p synchronize method can be called. RCU should NOT be locked.
362 The function does not free the item.
363 The deallocator will be implicitly invoked when the returned object is destroyed or when
364 its \p release() is called.
365 @note Before reusing \p result object you should call its \p release() method.
367 exempt_ptr extract_max()
372 /// Extracts an item from the map
374 The function searches an item with key equal to \p key in the tree,
375 unlinks it, and returns \p exempt_ptr pointer to a value found.
376 If \p key is not found the function returns an empty \p exempt_ptr.
378 RCU \p synchronize method can be called. RCU should NOT be locked.
379 The function does not destroy the value found.
380 The disposer will be implicitly invoked when the returned object is destroyed or when
381 its \p reset(nullptr) member function is called.
383 template <typename Q>
384 exempt_ptr extract( Q const& key )
386 exempt_ptr pExtracted;
391 [&pExtracted]( mapped_type * pVal ) { pExtracted.reset( pVal ); },
392 update_flags::allow_remove
397 /// Extracts an item from the map using \p pred for searching
399 The function is an analog of \p extract(Q const&)
400 but \p pred is used for key compare.
401 \p Less has the interface like \p std::less.
402 \p pred must imply the same element order as the comparator used for building the tree.
404 template <typename Q, typename Less>
405 exempt_ptr extract_with( Q const& key, Less pred )
408 exempt_ptr pExtracted;
412 cds::details::predicate_wrapper<key_type, Less, cds::details::trivial_accessor >(),
413 [&pExtracted]( mapped_type * pVal ) { pExtracted.reset( pVal ); },
414 update_flags::allow_remove
419 /// Find the key \p key
421 The function searches the item with key equal to \p key and calls the functor \p f for item found.
422 The interface of \p Func functor is:
425 void operator()( mapped_type& item );
428 where \p item is the item found.
429 The functor is called under node-level lock.
431 The function applies RCU lock internally.
433 The function returns \p true if \p key is found, \p false otherwise.
435 template <typename K, typename Func>
436 bool find( K const& key, Func f )
438 return do_find( key, key_comparator(), [&f]( sync_monitor& monitor, node_type * pNode ) -> bool {
439 assert( pNode != nullptr );
440 node_scoped_lock l( monitor, *pNode );
441 mapped_type * pVal = pNode->m_pValue.load( memory_model::memory_order_relaxed );
450 /// Finds the key \p val using \p pred predicate for searching
452 The function is an analog of \p find(K const&, Func)
453 but \p pred is used for key comparing.
454 \p Less functor has the interface like \p std::less.
455 \p Less must imply the same element order as the comparator used for building the map.
457 template <typename K, typename Less, typename Func>
458 bool find_with( K const& key, Less pred, Func f )
461 return do_find( key, opt::details::make_comparator_from_less<Less>(),
462 [&f]( sync_monitor& monitor, node_type * pNode ) -> bool {
463 assert( pNode != nullptr );
464 node_scoped_lock l( monitor, *pNode );
465 mapped_type * pVal = pNode->m_pValue.load( memory_model::memory_order_relaxed );
475 /// Find the key \p key
477 The function searches the item with key equal to \p key
478 and returns \p true if it is found, and \p false otherwise.
480 The function applies RCU lock internally.
482 template <typename K>
483 bool find( K const& key )
485 return do_find( key, key_comparator(), []( node_type * ) -> bool { return true; });
488 /// Finds the key \p val using \p pred predicate for searching
490 The function is an analog of \p find(K const&)
491 but \p pred is used for key comparing.
492 \p Less functor has the interface like \p std::less.
493 \p Less must imply the same element order as the comparator used for building the map.
495 template <typename K, typename Less>
496 bool find_with( K const& key, Less pred )
499 return do_find( key, opt::details::make_comparator_from_less<Less>(), []( node_type * ) -> bool { return true; } );
502 /// Finds \p key and return the item found
504 The function searches the item with key equal to \p key and returns the pointer to item found.
505 If \p key is not found it returns \p nullptr.
507 RCU should be locked before call the function.
508 Returned pointer is valid while RCU is locked.
510 template <typename Q>
511 mapped_type * get( Q const& key ) const
516 /// Finds \p key with \p pred predicate and return the item found
518 The function is an analog of \p get(Q const&)
519 but \p pred is used for comparing the keys.
521 \p Less functor has the semantics like \p std::less but should take arguments of type \p key_type
522 and \p Q in any order.
523 \p pred must imply the same element order as the comparator used for building the map.
525 template <typename Q, typename Less>
526 mapped_type * get_with( Q const& key, Less pred ) const
532 /// Clears the tree (thread safe, not atomic)
534 The function unlink all items from the tree.
535 The function is thread safe but not atomic: in multi-threaded environment with parallel insertions
539 assert( set.empty() );
541 the assertion could be raised.
543 For each node the \ref disposer will be called after unlinking.
545 RCU \p synchronize method can be called. RCU should not be locked.
549 for ( exempt_ptr ep = extract_min(); !ep.empty(); ep = extract_min() )
553 /// Clears the tree (not thread safe)
555 This function is not thread safe and may be called only when no other thread deals with the tree.
556 The function is used in the tree destructor.
563 /// Checks if the map is empty
566 return m_Root.m_pRight.load( memory_model::memory_order_relaxed ) == nullptr;
569 /// Returns item count in the map
571 Only leaf nodes containing user data are counted.
573 The value returned depends on item counter type provided by \p Traits template parameter.
574 If it is \p atomicity::empty_item_counter this function always returns 0.
576 The function is not suitable for checking the tree emptiness, use \p empty()
577 member function for this purpose.
581 return m_ItemCounter;
584 /// Returns const reference to internal statistics
585 stat const& statistics() const
590 /// Checks internal consistency (not atomic, not thread-safe)
592 The debugging function to check internal consistency of the tree.
594 bool check_consistency() const
601 template <typename Q, typename Compare, typename Func>
602 bool do_find( Q& key, Compare cmp, Func f ) const
607 result = try_find( key, cmp, f, m_pRoot, 1, 0 );
609 assert( result != find_result::retry );
610 return result == find_result::found;
613 template <typename K, typename Compare, typename Func>
614 int do_update( K const& key, Compare cmp, Func funcUpdate, int nFlags )
616 check_deadlock_policy::check();
618 rcu_disposer removed_list;
621 return try_udate_root( key, cmp, nFlags, funcUpdate, removed_list );
629 template <typename Q, typename Compare, typename Func>
630 find_result try_find( Q const& key, Compare cmp, Func f, node_type * pNode, int nDir, version_type nVersion ) const
632 assert( gc::is_locked() );
636 node_type * pChild = pNode->child( nDir ).load( memory_model::memory_order_relaxed );
638 if ( pNode->version( memory_model::memory_order_acquire ) != nVersion ) {
639 m_stat.onFindRetry();
640 return find_result::retry;
643 m_stat.onFindFailed();
644 return find_result::not_found;
647 int nCmp = cmp( key, pChild->m_key );
649 if ( pChild->is_valued( memory_model::memory_order_relaxed ) ) {
651 node_scoped_lock l( m_Monitor, pChild );
652 if ( pChild->is_valued( memory_model::memory_order_relaxed )) {
653 if ( f( *pChild ) ) {
654 m_stat.onFindSuccess();
655 return find_result::found;
660 m_stat.onFindFailed();
661 return find_result::not_found;
664 version_type nChildVersion = pChild->version( memory_model::memory_order_acquire );
665 if ( nChildVersion & node_type::shrinking ) {
666 m_stat.onFindWaitShrinking();
667 pChild->template wait_until_shrink_completed<back_off>( memory_model::memory_order_relaxed );
669 if ( pNode->version( memory_model::memory_order_acquire ) != nVersion ) {
670 m_stat.onFindRetry();
671 return find_result::retry;
674 else if ( nChildVersion != node_type::unlinked ) {
676 if ( pNode->version( memory_model::memory_order_acquire ) != nVersion ) {
677 m_stat.onFindRetry();
678 return find_result::retry;
681 find_result found = try_find( key, cmp, f, pChild, nCmp, nChildVersion );
682 if ( found != find_result::retry )
688 template <typename K, typename Compare, typename Func>
689 int try_update_root( K const* key, Compare cmp, int nFlags, Func funcUpdate, rcu_disposer& disp )
691 assert( gc::is_locked() );
695 // get right child of root
696 node_type * pChild = m_Root.child( 1, memory_model::memory_order_acquire );
698 version_type nChildVersion = pChild->version( memory_model::memory_order_relaxed );
699 if ( nChildVersion & node_type::shrinking ) {
700 m_stat.onUpdateRootWaitShrinking();
701 pChild->template wait_until_shrink_completed<back_off>( memory_model::memory_order_relaxed );
702 result = update_flags::retry;
704 else if ( pChild == m_Root.child( 1, memory_model::memory_order_acquire ) ) {
705 result = try_update( key, cmp, nFlags, funcUpdate, m_pRoot, pChild, nChildVersion, disp );
710 if ( nFlags & update_flags::allow_insert ) {
711 // insert into tree as right child of the root
713 node_scoped_lock l( m_Monitor, *m_pRoot );
715 node_type * pNew = alloc_node( key, 1, 0, m_pRoot, nullptr, nullptr );
716 mapped_type * pVal = funcUpdate( pNew );
717 assert( pVal != nullptr );
718 pNew->m_pValue.store( pVal, memory_model::memory_order_release );
720 m_pRoot->child( pNew, 1, memory_model::memory_order_relaxed );
721 m_pRoot->height( 2, memory_model::memory_order_relaxed );
725 m_stat.onInsertSuccess();
726 return update_flags::result_inserted;
729 return update_flags::failed;
731 } while ( result == update_flags::retry );
735 template <typename K, typename Compare, typename Func>
736 int try_update( K const& key, Compare cmp, int nFlags, Func funcUpdate, node_type * pParent, node_type * pNode, version_type nVersion, rcu_disposer& disp )
738 assert( gc::is_locked() );
739 assert( nVersion != node_type::unlinked );
741 int nCmp = cmp( key, pNode->m_key );
743 if ( nFlags & update_flags::allow_update ) {
744 return try_update_node( funcUpdate, pNode );
746 if ( nFlags & update_flags::allow_remove ) {
747 return try_remove_node( pParent, pNode, nVersion, funcUpdate, disp );
749 return update_flags::failed;
754 node_type * pChild = pNode->child( nCmp ).load( memory_model::memory_order_relaxed );
755 if ( pNode->version( memory_model::memory_order_acquire ) != nVersion )
756 return update_flags::retry;
758 if ( pChild == nullptr ) {
760 if ( nFlags & update_flags::allow_insert )
761 result = try_insert_node( key, funcUpdate, pNode, nCmp, nVersion, disp );
763 result = update_flags::failed;
767 result = update_flags::retry;
768 version_type nChildVersion = pChild->version( memory_model::memory_order_acquire );
769 if ( nChildVersion & node_type::shrinking ) {
770 m_stat.onUpdateWaitShrinking();
771 pChild->template wait_until_shrink_completed<back_off>( memory_model::memory_order_relaxed );
774 else if ( pChild == pNode->child( nCmp ).load( memory_model::memory_order_relaxed ) ) {
775 // this second read is important, because it is protected by nChildVersion
777 // validate the read that our caller took to get to node
778 if ( pNode->version( memory_model::memory_order_relaxed ) != nVersion ) {
779 m_stat.onUpdateRetry();
780 return update_flags::retry;
783 // At this point we know that the traversal our parent took to get to node is still valid.
784 // The recursive implementation will validate the traversal from node to
785 // child, so just prior to the node nVersion validation both traversals were definitely okay.
786 // This means that we are no longer vulnerable to node shrinks, and we don't need
787 // to validate node version any more.
788 result = try_update( key, cmp, nFlags, funcUpdate, pNode, pChild, nChildVersion, disp );
791 } while ( result == update_flags::retry );
795 template <typename K, typename Func>
796 int try_insert_node( K const& key, Func funcUpdate, node_type * pNode, int nDir, version_type nVersion, rcu_disposer& disp )
800 auto fnCreateNode = [&funcUpdate]( node_type * pNew ) {
801 mapped_type * pVal = funcUpdate( pNew );
802 assert( pVal != nullptr );
803 pNew->m_pValue.store( pVal, memory_model::memory_order_relaxed );
806 if ( c_bRelaxedInsert ) {
807 if ( pNode->version( memory_model::memory_order_acquire ) != nVersion
808 || pNode->child( nDir ).load( memory_model::memory_order_relaxed ) != nullptr )
810 m_stat.onInsertRetry();
811 return update_flags::retry;
814 fnCreateNode( pNew = alloc_node( key, 1, 0, pNode, nullptr, nullptr ));
817 node_type * pDamaged;
819 assert( pNode != nullptr );
820 node_scoped_lock l( m_Monitor, *pNode );
822 if ( pNode->version( memory_model::memory_order_relaxed ) != nVersion
823 || pNode->child( nDir ).load( memory_model::memory_order_relaxed ) != nullptr )
825 if ( c_RelaxedInsert ) {
827 m_stat.onRelaxedInsertFailed();
830 m_stat.onInsertRetry();
831 return update_flags::retry;
834 if ( !c_bRelaxedInsert )
835 fnCreateNode( pNew = alloc_node( key, 1, 0, pNode, nullptr, nullptr ));
837 pNode->child( pNew, nDir, memory_model::memory_order_relaxed );
838 pDamaged = fix_height_locked( pNode );
840 m_stat.onInsertSuccess();
842 fix_height_and_rebalance( pDamaged, disp );
844 return update_flags::result_inserted;
847 template <typename Func>
848 int try_update_node( Func funcUpdate, node_type * pNode )
852 assert( pNode != nullptr );
853 node_scoped_lock l( m_Monitor, *pNode );
855 if ( pNode->is_unlinked( memory_model::memory_order_relaxed )) {
856 m_stat.onUpdateUnlinked();
857 return update_flags::retry;
860 pOld = pNode->value( memory_model::memory_order_relaxed );
861 mapped_type * pVal = funcUpdate( *pNode );
862 assert( pVal != nullptr );
863 pNode->m_pValue.store( pVal, memory_model::memory_order_relaxed ));
868 m_stat.onDisposeValue();
871 m_stat.onUpdateSuccess();
872 return update_flags::result_updated;
875 template <typename Func>
876 int try_remove_node( node_type * pParent, node_type * pNode, version_type nVersion, Func func, rcu_disposer& disp )
878 assert( pParent != nullptr );
879 assert( pNode != nullptr );
881 if ( !pNode->is_valued( atomics::memory_order_relaxed ) )
882 return update_flags::failed;
884 if ( pNode->child( -1, atomics::memory_order_relaxed ) == nullptr
885 || pNode->child( 1, atomics::memory_order_relaxed ) == nullptr )
887 node_type * pDamaged;
890 node_scoped_lock lp( m_Monitor, *pParent );
891 if ( pParent->is_unlinked( atomics::memory_order_relaxed ) || pNode->m_pParent.load( atomics::memory_order_relaxed ) != pParent )
892 return update_flags::retry;
895 node_scoped_lock ln( m_Monitor, *pNode );
896 pOld = pNode->value( atomics::memory_order_relaxed );
897 if ( pNode->version( atomics::memory_order_relaxed ) == nVersion
899 && try_unlink_locked( pParent, pNode, disp ) )
901 pNode->m_pValue.store( nullptr, atomics::memory_order_relaxed );
904 return update_flags::retry;
906 pDamaged = fix_height_locked( pParent );
909 func( pOld ); // calls pOld disposer inside
910 m_stat.onDisposeValue();
912 fix_height_and_rebalance( pDamaged, disp );
913 return upfate_flags::result_removed;
916 int result = update_flags::retry;
918 node_scoped_lock ln( m_Monitor, *pNode );
919 mapped_type * pOld = pNode->value( atomics::memory_order_relaxed );
920 if ( pNode->version( atomics::memory_order_relaxed ) == nVersion && pOld ) {
921 pNode->m_pValue.store( nullptr, atomics::memory_order_relaxed );
922 result = upfate_flags::result_removed;
926 if ( result == upfate_flags::result_removed ) {
927 func( *pOld ); // calls pOld disposer inside
928 m_stat.onDisposeValue();
935 bool try_unlink_locked( node_type * pParent, node_type * pNode, rcu_disposer& disp )
937 // pParent and pNode must be locked
938 assert( !pParent->is_unlinked(memory_model::memory_order_relaxed) );
940 node_type * pParentLeft = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
941 node_type * pParentRight = pParent->m_pRight.load( memory_model::memory_order_relaxed );
942 if ( pNode != pParentLeft && pNode != pParentRight ) {
943 // node is no longer a child of parent
947 assert( !pNode->is_unlinked());
948 assert( pParent == pNode->m_pParent.load(memory_model::memory_order_relaxed));
950 node_type * pLeft = pNode->m_pLeft.load( memory_model::memory_order_relaxed );
951 node_type * pRight = pNode->m_pRight.load( memory_model::memory_order_relaxed );
952 if ( pLeft != nullptr && pRight != nullptr ) {
953 // splicing is no longer possible
956 node_type * pSplice = pLeft ? pLeft : pRight;
958 if ( pParentLeft == pNode )
959 pParent->m_pLeft.store( pSplice, memory_model::memory_order_relaxed );
961 pParent->m_pRight.store( pSplice, memory_model::memory_order_relaxed );
964 pSplice->pParent.store( pParent, memory_model::memory_order_release );
966 // Mark the node as unlinked
967 pNode->version( node_type::unlinked, memory_model::memory_order_release );
968 disp.dispose( pNode );
974 private: // rotations
976 int estimate_node_condition( node_type * pNode )
978 node_type * pLeft = pNode->m_pLeft.load( memory_model::memory_order_relaxed );
979 node_type * pRight = pNode->m_pRight.load( memory_model::memory_order_relaxed );
981 if ( (pLeft == nullptr || pRight == nullptr) && pNode->value( memory_model::memory_order_relaxed ) == nullptr )
982 return unlink_required;
984 int h = pNode->height( memory_model::memory_order_relaxed );
985 int hL = pLeft ? pLeft->height( memory_model::memory_order_relaxed ) : 0;
986 int hR = pRight ? pRight->height( memory_model::memory_order_relaxed ) : 0;
988 int hNew = 1 + std::max( hL, hR );
989 int nBalance = hL - hR;
991 if ( nBalance < -1 || nBalance > 1 )
992 return rebalance_required;
994 return h != hNew ? hNew : nothing_required;
997 node_type * fix_height( node_type * pNode )
999 assert( pNode != nullptr );
1000 node_scoped_lock l( m_Monitor, *pNode );
1001 return fix_height_locked( pNode );
1004 node_type * fix_height_locked( node_type * pNode )
1006 // pNode must be locked!!!
1007 int h = estimate_node_condition( pNode );
1009 case rebalance_required:
1010 case unlink_required:
1012 case nothing_required:
1015 pNode->height( h, memory_model::memory_order_relaxed );
1016 return pNode->m_pParent.load( memory_model::memory_order_relaxed );
1020 void fix_height_and_rebalance( node_type * pNode, rcu_disposer& disp )
1022 while ( pNode && pNode->m_pParent.load( memory_model::memory_order_relaxed ) ) {
1023 int nCond = estimate_node_condition( pNode );
1024 if ( nCond == nothing_required || pNode->is_unlinked( memory_model::memory_order_relaxed ) )
1027 if ( nCond != unlink_required && nCond != rebalance_required )
1028 pNode = fix_height( pNode );
1030 node_type * pParent = pNode->m_pParent.load( memory_model::memry_order_relaxed );
1031 assert( pParent != nullptr );
1033 node_scoped_lock lp( m_Monitor, *pParent );
1034 if ( !pParent->is_unlinked( memory_model::memory_order_relaxed )
1035 && pNode->m_pParent.load( memory_model::memory_order_relaxed ) == pParent )
1037 node_scoped_lock ln( m_Monitor, *pNode );
1038 pNode = rebalance_locked( pParent, pNode, disp );
1045 node_type * rebalance_locked( node_type * pParent, node_type * pNode, rcu_disposer& disp )
1047 // pParent and pNode shlould be locked.
1048 // Returns a damaged node, or nullptr if no more rebalancing is necessary
1049 assert( pNode->m_pParent.load( memory_model::memry_order_relaxed ) == pNode );
1050 assert( m_pParent->m_pLeft.load( memory_model::memory_order_relaxed ) == pNode
1051 || m_pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
1053 node_type * pLeft = pNode->m_pLeft.load( memory_model::memory_order_relaxed );
1054 node_type * pRight = pNode->m_pRight.load( memory_model::memory_order_relaxed );
1056 if ( (pLeft == nullptr || pRight == nullptr) && pNode->value( memory_model::memory_order_relaxed ) == nullptr ) {
1057 if ( try_unlink_locked( pParent, pNode, disp ))
1058 return fix_height_locked( pParent );
1060 // retry needed for pNode
1065 int h = pNode->height( memory_model::memory_order_relaxed );
1066 int hL = pLeft ? pLeft->height( memory_model::memory_order_relaxed ) : 0;
1067 int hR = pRight ? pRight->height( memory_model::memory_order_relaxed ) : 0;
1068 int hNew = 1 + std::max( hL, hR );
1069 int balance = hL - hR;
1072 return rebalance_to_right_locked( pParent, pNode, pLeft, hR );
1073 else if ( balance < -1 )
1074 return rebalance_to_left_locked( pParent, pNode, pRight, hL );
1075 else if ( hNew != h ) {
1076 pNode->height( hNew, memory_model::memory_order_relaxed );
1078 // pParent is already locked
1079 return fix_height_locked( pParent );
1085 node_type * rebalance_to_right_locked( node_type * pParent, node_type * pNode, node_type * pLeft, int hR )
1087 assert( pNode->m_pParent.load( memory_model::memry_order_relaxed ) == pNode );
1088 assert( m_pParent->m_pLeft.load( memory_model::memory_order_relaxed ) == pNode
1089 || m_pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
1091 // pParent and pNode is locked yet
1092 // pNode->pLeft is too large, we will rotate-right.
1093 // If pLeft->pRight is taller than pLeft->pLeft, then we will first rotate-left pLeft.
1096 assert( pLeft != nullptr );
1097 node_scoped_lock l( m_Monitor, *pLeft );
1098 if ( pNode->m_pLeft.load( memory_model::memory_order_relaxed ) != pLeft )
1099 return pNode; // retry for pNode
1101 int hL = pLeft->height( memory_model::memory_order_relaxed );
1103 return pNode; // retry
1105 node_type * pLRight = pLeft->m_pRight.load( memory_model::memory_order_relaxed );
1106 int hLR = pLRight ? pLRight->height( memory_model::memory_order_relaxed ) : 0;
1107 node_type * pLLeft = pLeft->m_pLeft.load( memory_model::memory_order_relaxed );
1108 int hLL = pLLeft ? pLLeft->height( memory_model::memory_order_relaxed ) : 0;
1112 return rotate_right_locked( pParent, pNode, pLeft, hR, hLL, pLRight, hLR );
1115 assert( pLRight != nullptr );
1117 node_scoped_lock lr( m_Monitor, *pLRight );
1118 if ( pLeft->m_pRight.load( memory_model::memory_order_relaxed ) != pLRight )
1119 return pNode; // retry
1121 hLR = pLRight->height( memory_model::memory_order_relaxed );
1123 return rotate_right_locked( pParent, pNode, pLeft, hR, hLL, pLRight, hLR );
1125 node_type * pLRLeft = pLRight->m_pLeft.load( memory_model::memory_order_relaxed );
1126 int hLRL = pLRLeft ? pLRLeft->height( memory_model::memory_order_relaxed ) : 0;
1127 int balance = hLL - hLRL;
1128 if ( balance >= -1 && balance <= 1 && !((hLL == 0 || hLRL == 0) && pLeft->value(memory_model::memory_order_relaxed) == nullptr) ) {
1129 // nParent.child.left won't be damaged after a double rotation
1130 return rotate_right_over_left_locked( pParent, pNode, pLeft, hR, hLL, pLRight, hLRL );
1134 // focus on pLeft, if necessary pNode will be balanced later
1135 return rebalance_to_left_locked( pNode, pLeft, pLRight, hLL );
1140 node_type * rebalance_to_left_locked( node_type * pParent, node_type * pNode, node_type * pRight, int hL )
1142 assert( pNode->m_pParent.load( memory_model::memry_order_relaxed ) == pNode );
1143 assert( m_pParent->m_pLeft.load( memory_model::memory_order_relaxed ) == pNode
1144 || m_pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
1146 // pParent and pNode is locked yet
1148 assert( pRight != nullptr );
1149 node_scoped_lock l( m_Monitor, *pRight );
1150 if ( pNode->m_pRight.load( memory_model::memory_order_relaxed ) != pRight )
1151 return pNode; // retry for pNode
1153 int hR = pRight->height( memory_model::memory_order_relaxed );
1154 if ( hL - hR >= -1 )
1155 return pNode; // retry
1157 node_type * pRLeft = pRight->m_pLeft.load( memory_model::memory_order_relaxed );
1158 int hRL = pRLeft > pRLeft->height( memory_model::memory_order_relaxed ) : 0;
1159 node_type * pRRight = pRight->m_pRight.load( memory_model::memory_order_relaxed );
1160 int hRR = pRRight ? pRRight->height( memory_model::memory_order_relaxed ) : 0;
1162 return rotate_left_locked( pParent, pNode, hL, pRight, pRLeft, hRL, hRR );
1165 assert( pRLeft != nullptr );
1166 node_scoped_lock lrl( m_Monitor, *pRLeft );
1167 if ( pRight->m_pLeft.load( memory_model::memory_order_relaxed ) != pRLeft )
1168 return pNode; // retry
1170 hRL = pRLeft->height( memory_model::memory_order_relaxed );
1172 return rotate_left_locked( pParent, pNode, hL, pRight, pRLeft, hRL, hRR );
1174 node_type * pRLRight = pRLeft->m_pRight.load( memory_model::memory_order_relaxed );
1175 int hRLR = pRLRight ? pRLRight->height( memory_model::memory_order_relaxed ) : 0;
1176 int balance = hRR - hRLR;
1177 if ( b >= -1 && b <= 1 && !((hRR == 0 || hRLR == 0) && pRight->value( memory_odel::memory_order_relaxed ) == nullptr)
1178 return rotate_left_over_right_locked( pParent, pNode, hL, pRight, pRLeft, hRR, hRLR );
1180 return rebalance_to_right_locked( pNode, pRight, pRLeft, hRR );
1184 static void begin_change( node_type * pNode, version_type version )
1186 pNode->version( version | node_type::shrinking, memory_model::memory_order_release );
1188 static void end_change( node_type * pNode, version_type version )
1190 // Clear shrinking and unlinked flags and increment version
1191 pNode->version( (version | node_type::version_flags) + 1, memory_model::memory_order_release );
1194 node_type * rotate_right_locked( node_type * pParent, node_type * pNode, node_type * pLeft, int hR, int hLL, node_type * pLRight, int hLR )
1196 version_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
1197 node_type * pParentLeft = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
1199 begin_change( pNode, nodeVersion );
1201 pNode->m_pLeft.store( pLRight, memory_model::memory_order_relaxed );
1202 if ( pLRight != nullptr )
1203 pLRight->m_pParent.store( pNode, memory_model::memory_order_relaxed );
1205 pLeft->m_pRight.store( pNode, memory_model::memory_order_relaxed );
1206 pNode->m_pParent.store( pLeft, memory_model::memory_order_relaxed );
1208 if ( pParentLeft == pNode )
1209 pParent->m_pLeft.store( pLeft, memory_model::memory_order_relaxed );
1211 assert( pParent->m_pRight.load( memory_odel::memory_order_relaxed ) == pNode );
1212 pParent->m_pRight.store( pLeft, memory_mdel::memory_order_relaxed );
1214 pLeft->m_pParent.store( pParent, memory_model::memory_order_relaxed );
1216 // fix up heights links
1217 int hNode = 1 + std::max( hLR, hR );
1218 pNode->height( hNode, memory_model::memory_order_relaxed );
1219 pLeft->height( 1 + std::max( hLL, hNode ), memory_model::memory_order_relaxed );
1221 end_change( pNode, nodeVersion );
1223 // We have damaged pParent, pNode (now parent.child.right), and pLeft (now
1224 // parent.child). pNode is the deepest. Perform as many fixes as we can
1225 // with the locks we've got.
1227 // We've already fixed the height for pNode, but it might still be outside
1228 // our allowable balance range. In that case a simple fix_height_locked()
1230 int nodeBalance = hLR - hR;
1231 if ( nodeBalance < -1 || nodeBalance > 1 ) {
1232 // we need another rotation at pNode
1236 // we've fixed balance and height damage for pNode, now handle
1237 // extra-routing node damage
1238 if ( (pLRight == nullptr || hR == 0) && pNode->value(memory_model::memory_order_relaxed) == nullptr ) {
1239 // we need to remove pNode and then repair
1243 // we've already fixed the height at pLeft, do we need a rotation here?
1244 int leftBalance = hLL - hNode;
1245 if ( leftBalance < -1 || leftBalance > 1 )
1248 // pLeft might also have routing node damage (if pLeft.left was null)
1249 if ( hLL == 0 && pLeft->value( memory_model::memory_order_relaxed ) == nullptr )
1252 // try to fix the parent height while we've still got the lock
1253 return fix_height_locked( pParent );
1256 node_type * rotate_left_locked( node_type * pParent, node_type * pNode, int hL, node_type * pRight, node_type * pRLeft, int hRL, int hRR )
1258 version_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
1259 node_type * pParentLeft = pParent->m_pLeft.load( memory_odel::memory_order_relaxed );
1261 begin_change( pNode, nodeVersion );
1263 // fix up pNode links, careful to be compatible with concurrent traversal for all but pNode
1264 pNode->m_pRight.store( pRLeft, memory_nodel::memory_order_relaxed );
1265 if ( pRLeft != nullptr )
1266 pRLeft->m_pParent.store( pNode, memory_model::memory_order_relaxed );
1268 pRight->m_pLeft.store( pNode, memory_model::memory_order_relaxed );
1269 pNode->m_pParent.store( pRight, memory_model::memory_order_relaxed );
1271 if ( pParentLeft == pNode )
1272 pParent->m_pLeft.store( pRight, memory_model::memory_order_relaxed );
1274 assert( pParent->m_pRight.load( memory_odel::memory_order_relaxed ) == pNode );
1275 pParent->m_pRight.store( pRight, memory_model::memory_model_relaxed );
1277 pRight->m_pParent.store( pParent, memory_model::memory_order_relaxed );
1280 int hNode = 1 + std::max( hL, hRL );
1281 pNode->height( hNode, memory_model::memory_order_relaxed );
1282 pRight->height( 1 + std::max( hNode, hRR ), memory_model::memory_order_relaxed );
1284 end_change( pNode, nodeVersion );
1286 int nodeBalance = hRL - hL;
1287 if ( nodeBalance < -1 || nodeBalance > 1 )
1290 if ( (pRLeft == nullptr || hL == 0) && pNode->value( memory_model::memory_order_relaxed ) == nullptr )
1293 int rightBalance = hRR - hNode;
1294 if ( rightBalance < -1 || rightBalance > 1 )
1297 if ( hRR == 0 && pRight->value( memory_model::memory_order_relaxed ) == nullptr )
1300 return fix_height_locked( pParent );
1303 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 )
1305 typename node_type::value_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
1306 typename node_type::value_type leftVersion = pLeft->version( memory_model::memory_order_relaxed );
1308 node_type * pPL = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
1309 node_type * pLRL = pLRight->m_pLeft.load( memory_model::memory_order_relaxed );
1310 node_type * pLRR = pLRight->m_pRight.load( memory_model::memory_order_relaxed );
1311 int hLRR = pLRR ? pLRR->hight( memory_model::memory_order_relaxed ) : 0;
1313 begin_change( pNode, nodeVersion );
1314 begin_change( pLeft, leftVersion );
1316 // fix up pNode links, careful about the order!
1317 pNode->m_pLeft.store( pLRR, memory_model::memory_order_relaxed );
1318 if ( pLRR != nullptr )
1319 pLRR->m_pParent.store( pNode, memory_model::memory_order_relaxed );
1321 pLeft->m_pRight.store( pLRL, memory_model::memory_order_relaxed );
1322 if ( pLRL != nullptr )
1323 pLRL->m_pParent.store( pLeft, memory_model::memory_order_relaxed );
1325 pLRight->m_pLeft.store( pLeft, memory_model::memory_order_relaxed );
1326 pLeft->m_pParent.store( pLRight, memory_model::memory_order_relaxed );
1327 pLRight->m_pRight.store( pNode, memory_model::memory_order_relaxed );
1328 pNode->m_pParent.store( pLRight, memory_model::memory_order_relaxed );
1331 pParent->m_pLeft.store( pLRight, memory_model::memory_order_relaxed );
1333 assert( Parent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
1334 pParent->m_pRight.store( pLRight, memory_model::memory_order_relaxed );
1336 pLRight->m_pParent.store( pParent, memory_model::memory_order_relaxed );
1339 int hNode = 1 + std::max( hLRR, hR );
1340 pNode->height( hNode, memory_model::memory_order_relaxed );
1341 int hLeft = 1 + std::max( hLL, hLRL );
1342 pLeft->height( hLeft, memory_model::memory_order_relaxed );
1343 pLRight->height( 1 + std::max( hLeft, hNode ), memory_model::memory_order_relaxed );
1345 end_change( pNode, nodeVersion );
1346 end_change( pLeft, leftVersion );
1348 // caller should have performed only a single rotation if pLeft was going
1349 // to end up damaged
1350 assert( hLL - hLRL <= 1 && hLRL - hLL <= 1 );
1351 assert( !((hLL == 0 || pLRL == nullptr) && pLeft->value( memory_model::memory_order_relaxed ) == nullptr ));
1353 // We have damaged pParent, pLR (now parent.child), and pNode (now
1354 // parent.child.right). pNode is the deepest. Perform as many fixes as we
1355 // can with the locks we've got.
1357 // We've already fixed the height for pNode, but it might still be outside
1358 // our allowable balance range. In that case a simple fix_height_locked()
1360 int nodeBalance = hLRR - hR;
1361 if ( nodeBalance < -1 || nodeBalance > 1 ) {
1362 // we need another rotation at pNode
1366 // pNode might also be damaged by being an unnecessary routing node
1367 if ( (pLRR == nullptr || hR == 0) && pNode->value( memory_model::memory_order_relaxed ) == nullptr ) {
1368 // repair involves splicing out pNode and maybe more rotations
1372 // we've already fixed the height at pLRight, do we need a rotation here?
1373 final int balanceLR = hLeft - hNode;
1374 if ( balanceLR < -1 || balanceLR > 1 )
1377 // try to fix the parent height while we've still got the lock
1378 return fix_height_locked( pParent );
1381 node_type * rotate_left_over_right_locked( node_type * pParent, node_type * pNode, int hL, node_type * pRight, node_type * pRLeft, int hRR, int hRLR )
1383 version_type nodeVersion = pNode->version( memory_model::memory_order_relaxed );
1384 version_type rightVersion = pRight->version( memory_model::memory_order_relaxed );
1386 node_type * pPL = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
1387 node_type * pRLL = pRLeft->m_pLeft.load( memory_model::memory_order_relaxed );
1388 node_type * pRLR = pRLeft->m_pRight.load( memory_model::memory_order_relaxed );
1389 int hRLL = pRLL ? pRLL->height( memory_model::memory_order_relaxed ) : 0;
1391 begin_change( pNode, nodeVersion );
1392 begin_change( pRight, ghtVersion );
1394 // fix up pNode links, careful about the order!
1395 pNode->m_pRight.store( pRLL, memory_model::memory_order_relaxed );
1396 if ( pRLL != nullptr )
1397 pRLL->m_pParent.store( pNode, memory_model::memory_order_relaxed );
1399 pRight->m_pLeft.store( pRLR, memory_model::memory_order_relaxed );
1400 if ( pRLR != nullptr )
1401 pRLR->m_pParent.store( pRight, memory_model::memory_order_relaxed );
1403 pRLeft->m_pRight.store( pRight, memory_model::memory_order_relaxed );
1404 pRight->m_pParent.store( pRLeft, memory_model::memory_order_relaxed );
1405 pRLeft->m_pLeft.store( pNode, memory_model::memory_order_relaxed );
1406 pNode->m_pParent.store( pRLeft, memory_model::memory_order_relaxed );
1409 pParent->m_pLeft.store( pRLeft, memory_model::memory_order_relaxed );
1411 assert( pParent->m_pRight.load( memory_model::memory_order_relaxed ) == pNode );
1412 pParent->m_pRight.store( pRLeft, memory_model::memory_order_relaxed );
1414 pRLeft->m_pParent.store( pParent, memory_model::memory_order_relaxed );
1417 int hNode = 1 + std::max( hL, hRLL );
1418 pNode->height( hNode, memory_model::memory_order_relaxed );
1419 int hRight = 1 + std::max( hRLR, hRR );
1420 pRight->height( hRight, memory_model::memory_order_relaxed );
1421 pRLeft->height( 1 + std::max( hNode, hRight ), memory_model::memory_order_relaxed );
1423 end_change( pNode, nodeVersion );
1424 end_change( pRight, rightVersion );
1426 assert( hRR - hRLR <= 1 && hRLR - hRR <= 1 );
1428 int nodeBalance = hRLL - hL;
1429 if ( nodeBalance < -1 || nodeBalance > 1 )
1431 if ( (pRLL == nullptr || hL == 0) && pNode->value( memory_model::memory_order_relaxed ) == nullptr )
1434 int balRL = hRight - hNode;
1435 if ( balRL < -1 || balRL > 1 )
1438 return fix_height_locked( pParent );
1443 }} // namespace cds::container
1445 #endif // #ifndef CDSLIB_CONTAINER_IMPL_BRONSON_AVLTREE_MAP_RCU_H