Merged branch 'master' of https://github.com/Nemo1369/libcds
[libcds.git] / cds / intrusive / ellen_bintree_rcu.h
index a6577dcdfd3bf079b75b1a2c8c765e278d87ed0d..50e9cf022fe144503ecb465ca8f39a0e9b728aaa 100644 (file)
@@ -1,4 +1,32 @@
-//$$CDS-header$$
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
+
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
 
 #ifndef CDSLIB_INTRUSIVE_ELLEN_BINTREE_RCU_H
 #define CDSLIB_INTRUSIVE_ELLEN_BINTREE_RCU_H
@@ -54,11 +82,10 @@ namespace cds { namespace intrusive {
         a <i>priority queue</i>. In this case you should provide unique compound key, for example,
         the priority value plus some uniformly distributed random value.
 
-        @warning Recall the tree is <b>unbalanced</b>. The complexity of operations is <tt>O(log N)</tt>
-        for uniformly distributed random keys, but in worst case the complexity is <tt>O(N)</tt>.
+        @attention Recall the tree is <b>unbalanced</b>. The complexity of operations is <tt>O(log N)</tt>
+        for uniformly distributed random keys, but in the worst case the complexity is <tt>O(N)</tt>.
 
         @note In the current implementation we do not use helping technique described in the original paper.
-        In Hazard Pointer schema helping is too complicated and does not give any observable benefits.
         Instead of helping, when a thread encounters a concurrent operation it just spins waiting for
         the operation done. Such solution allows greatly simplify the implementation of tree.
 
@@ -72,6 +99,9 @@ namespace cds { namespace intrusive {
             It is possible to declare option-based tree with \p ellen_bintree::make_traits metafunction
             instead of \p Traits template argument.
 
+        @note Before including <tt><cds/intrusive/ellen_bintree_rcu.h></tt> you should include appropriate RCU header file,
+        see \ref cds_urcu_gc "RCU type" for list of existing RCU class and corresponding header files.
+
         @anchor cds_intrusive_EllenBinTree_rcu_less
         <b>Predicate requirements</b>
 
@@ -110,9 +140,6 @@ namespace cds { namespace intrusive {
         };
         \endcode
 
-        @note Before including <tt><cds/intrusive/ellen_bintree_rcu.h></tt> you should include appropriate RCU header file,
-        see \ref cds_urcu_gc "RCU type" for list of existing RCU class and corresponding header files.
-
         @anchor cds_intrusive_EllenBinTree_usage
         <b>Usage</b>
 
@@ -322,8 +349,8 @@ namespace cds { namespace intrusive {
         // Foo struct is derived from two ellen_bintree::node class
         // with different tags
         struct Foo
-            : public cds::intrusive::ellen_bintree::node< gpb_rcu, cds::opt::tag< string_tag > >
-            , public cds::intrusive::ellen_bintree::node< gpb_rcu >, cds::opt::tag< int_tag >
+            : public cds::intrusive::ellen_bintree::node< gpb_rcu, cds::opt::tag< string_tag >>
+            , public cds::intrusive::ellen_bintree::node< gpb_rcu, cds::opt::tag< int_tag >>
         {
             std::string m_strKey    ;   // string key
             int         m_nKey      ;   // int key
@@ -460,13 +487,13 @@ namespace cds { namespace intrusive {
         {
             static internal_node const& to_internal_node( tree_node const& n )
             {
-                assert( n.is_internal() );
+                assert( n.is_internal());
                 return static_cast<internal_node const&>( n );
             }
 
             static leaf_node const& to_leaf_node( tree_node const& n )
             {
-                assert( n.is_leaf() );
+                assert( n.is_leaf());
                 return static_cast<leaf_node const&>( n );
             }
         };
@@ -589,20 +616,20 @@ namespace cds { namespace intrusive {
                 {
                     if ( m_pUpdate ) {
                         return cds::urcu::retired_ptr( reinterpret_cast<void *>( m_pUpdate ),
-                            reinterpret_cast<cds::urcu::free_retired_ptr_func>( free_update_desc ) );
+                            reinterpret_cast<cds::urcu::free_retired_ptr_func>( free_update_desc ));
                     }
                     if ( m_pNode ) {
-                        if ( m_pNode->is_leaf() ) {
+                        if ( m_pNode->is_leaf()) {
                             return cds::urcu::retired_ptr( reinterpret_cast<void *>( node_traits::to_value_ptr( static_cast<leaf_node *>( m_pNode ))),
-                                reinterpret_cast< cds::urcu::free_retired_ptr_func>( free_leaf_node ) );
+                                reinterpret_cast< cds::urcu::free_retired_ptr_func>( free_leaf_node ));
                         }
                         else {
-                            return cds::urcu::retired_ptr( reinterpret_cast<void *>( static_cast<internal_node *>( m_pNode ) ),
-                                reinterpret_cast<cds::urcu::free_retired_ptr_func>( free_internal_node ) );
+                            return cds::urcu::retired_ptr( reinterpret_cast<void *>( static_cast<internal_node *>( m_pNode )),
+                                reinterpret_cast<cds::urcu::free_retired_ptr_func>( free_internal_node ));
                         }
                     }
                     return cds::urcu::retired_ptr( nullptr,
-                        reinterpret_cast<cds::urcu::free_retired_ptr_func>( free_update_desc ) );
+                        reinterpret_cast<cds::urcu::free_retired_ptr_func>( free_update_desc ));
                 }
 
                 void operator ++()
@@ -633,7 +660,7 @@ namespace cds { namespace intrusive {
 
             ~retired_list()
             {
-                gc::batch_retire( forward_iterator(*this), forward_iterator() );
+                gc::batch_retire( forward_iterator(*this), forward_iterator());
             }
 
             void push( update_desc * p )
@@ -651,7 +678,7 @@ namespace cds { namespace intrusive {
 
         void retire_node( tree_node * pNode, retired_list& rl ) const
         {
-            if ( pNode->is_leaf() ) {
+            if ( pNode->is_leaf()) {
                 assert( static_cast<leaf_node *>( pNode ) != &m_LeafInf1 );
                 assert( static_cast<leaf_node *>( pNode ) != &m_LeafInf2 );
             }
@@ -742,18 +769,16 @@ namespace cds { namespace intrusive {
 
                 search_result res;
                 for ( ;; ) {
-                    if ( search( res, val, node_compare() )) {
-                        if ( pNewInternal.get() )
+                    if ( search( res, val, node_compare())) {
+                        if ( pNewInternal.get())
                             m_Stat.onInternalNodeDeleted() ;    // unique_internal_node_ptr deletes internal node
                         m_Stat.onInsertFailed();
                         return false;
                     }
 
-                    if ( res.updParent.bits() != update_desc::Clean )
-                        help( res.updParent, updRetire );
-                    else {
-                        if ( !pNewInternal.get() )
-                            pNewInternal.reset( alloc_internal_node() );
+                    if ( res.updGrandParent.bits() == update_desc::Clean && res.updParent.bits() == update_desc::Clean ) {
+                        if ( !pNewInternal.get())
+                            pNewInternal.reset( alloc_internal_node());
 
                         if ( try_insert( val, pNewInternal.get(), res, updRetire )) {
                             f( val );
@@ -761,6 +786,8 @@ namespace cds { namespace intrusive {
                             break;
                         }
                     }
+                    else
+                        help( res.updParent, updRetire );
 
                     bkoff();
                     m_Stat.onInsertRetry();
@@ -773,20 +800,21 @@ namespace cds { namespace intrusive {
             return true;
         }
 
-        /// Ensures that the \p val exists in the tree
+        /// Updates the node
         /**
             The operation performs inserting or changing data with lock-free manner.
 
-            If the item \p val is not found in the tree, then \p val is inserted into the tree.
+            If the item \p val is not found in the set, then \p val is inserted into the set
+            iff \p bAllowInsert is \p true.
             Otherwise, the functor \p func is called with item found.
-            The functor signature is:
+            The functor \p func signature is:
             \code
                 void func( bool bNew, value_type& item, value_type& val );
             \endcode
             with arguments:
             - \p bNew - \p true if the item has been inserted, \p false otherwise
-            - \p item - item of the tree
-            - \p val - argument \p val passed into the \p ensure function
+            - \p item - item of the set
+            - \p val - argument \p val passed into the \p %update() function
             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
             refer to the same thing.
 
@@ -795,14 +823,15 @@ namespace cds { namespace intrusive {
 
             RCU \p synchronize method can be called. RCU should not be locked.
 
-            Returns <tt>std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
+            Returns std::pair<bool, bool> where \p first is \p true if operation is successful,
+            i.e. the node has been inserted or updated,
             \p second is \p true if new item has been added or \p false if the item with \p key
-            already is in the tree.
+            already exists.
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
-        std::pair<bool, bool> ensure( value_type& val, Func func )
+        std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
         {
             check_deadlock_policy::check();
 
@@ -815,19 +844,20 @@ namespace cds { namespace intrusive {
 
                 search_result res;
                 for ( ;; ) {
-                    if ( search( res, val, node_compare() )) {
+                    if ( search( res, val, node_compare())) {
                         func( false, *node_traits::to_value_ptr( res.pLeaf ), val );
-                        if ( pNewInternal.get() )
+                        if ( pNewInternal.get())
                             m_Stat.onInternalNodeDeleted() ;    // unique_internal_node_ptr deletes internal node
-                        m_Stat.onEnsureExist();
+                        m_Stat.onUpdateExist();
                         return std::make_pair( true, false );
                     }
 
-                    if ( res.updParent.bits() != update_desc::Clean )
-                        help( res.updParent, updRetire );
-                    else {
-                        if ( !pNewInternal.get() )
-                            pNewInternal.reset( alloc_internal_node() );
+                    if ( res.updGrandParent.bits() == update_desc::Clean && res.updParent.bits() == update_desc::Clean ) {
+                        if ( !bAllowInsert )
+                            return std::make_pair( false, false );
+
+                        if ( !pNewInternal.get())
+                            pNewInternal.reset( alloc_internal_node());
 
                         if ( try_insert( val, pNewInternal.get(), res, updRetire )) {
                             func( true, val, val );
@@ -835,17 +865,27 @@ namespace cds { namespace intrusive {
                             break;
                         }
                     }
+                    else
+                        help( res.updParent, updRetire );
 
                     bkoff();
-                    m_Stat.onEnsureRetry();
+                    m_Stat.onUpdateRetry();
                 }
             }
 
             ++m_ItemCounter;
-            m_Stat.onEnsureNew();
+            m_Stat.onUpdateNew();
 
             return std::make_pair( true, true );
         }
+        //@cond
+        template <typename Func>
+        CDS_DEPRECATED("ensure() is deprecated, use update()")
+        std::pair<bool, bool> ensure( value_type& val, Func func )
+        {
+            return update( val, func, true );
+        }
+        //@endcond
 
         /// Unlinks the item \p val from the tree
         /**
@@ -877,7 +917,8 @@ namespace cds { namespace intrusive {
             unlinks it from the tree, and returns \p true.
             If the item with key equal to \p key is not found the function return \p false.
 
-            Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
+            Note the \p Traits::less and/or \p Traits::compare predicate should accept a parameter of type \p Q
+            that can be not the same as \p value_type.
 
             RCU \p synchronize method can be called. RCU should not be locked.
         */
@@ -929,7 +970,8 @@ namespace cds { namespace intrusive {
 
             If the item with key equal to \p key is not found the function return \p false.
 
-            Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
+            Note the \p Traits::less and/or \p Traits::compare predicate should accept a parameter of type \p Q
+            that can be not the same as \p value_type.
 
             RCU \p synchronize method can be called. RCU should not be locked.
         */
@@ -983,7 +1025,7 @@ namespace cds { namespace intrusive {
         */
         exempt_ptr extract_min()
         {
-            return exempt_ptr( extract_min_() );
+            return exempt_ptr( extract_min_());
         }
 
         /// Extracts an item with maximal key from the tree
@@ -1004,7 +1046,7 @@ namespace cds { namespace intrusive {
         */
         exempt_ptr extract_max()
         {
-            return exempt_ptr( extract_max_() );
+            return exempt_ptr( extract_max_());
         }
 
         /// Extracts an item from the tree
@@ -1021,13 +1063,12 @@ namespace cds { namespace intrusive {
         template <typename Q>
         exempt_ptr extract( Q const& key )
         {
-            return exempt_ptr( extract_( key, node_compare() ));
+            return exempt_ptr( extract_( key, node_compare()));
         }
 
         /// Extracts an item from the set using \p pred for searching
         /**
-            The function is an analog of \ref cds_intrusive_EllenBinTree_rcu_extract "extract(exempt_ptr&, Q const&)"
-            but \p pred is used for key compare.
+            The function is an analog of \p extract(Q const&) but \p pred is used for key compare.
             \p Less has the interface like \p std::less and should meet \ref cds_intrusive_EllenBinTree_rcu_less
             "predicate requirements".
             \p pred must imply the same element order as the comparator used for building the tree.
@@ -1038,22 +1079,19 @@ namespace cds { namespace intrusive {
             return exempt_ptr( extract_with_( key, pred ));
         }
 
-        /// Finds the key \p key
-        /** @anchor cds_intrusive_EllenBinTree_rcu_find_val
+        /// Checks whether the set contains \p key
+        /**
             The function searches the item with key equal to \p key
             and returns \p true if it is found, and \p false otherwise.
 
-            Note the hash functor specified for class \p Traits template parameter
-            should accept a parameter of type \p Q that can be not the same as \p value_type.
-
             The function applies RCU lock internally.
         */
         template <typename Q>
-        bool find( Q const& key ) const
+        bool contains( Q const& key ) const
         {
             rcu_lock l;
             search_result    res;
-            if ( search( res, key, node_compare() )) {
+            if ( search( res, key, node_compare())) {
                 m_Stat.onFindSuccess();
                 return true;
             }
@@ -1061,18 +1099,25 @@ namespace cds { namespace intrusive {
             m_Stat.onFindFailed();
             return false;
         }
+        //@cond
+        template <typename Q>
+        CDS_DEPRECATED("deprecated, use contains()")
+        bool find( Q const& key ) const
+        {
+            return contains( key );
+        }
+        //@endcond
 
-        /// Finds the key \p key with comparing functor \p pred
+        /// Checks whether the set contains \p key using \p pred predicate for searching
         /**
-            The function is an analog of \ref cds_intrusive_EllenBinTree_rcu_find_val "find(Q const&)"
-            but \p pred is used for key compare.
+            The function is similar to <tt>contains( key )</tt> but \p pred is used for key comparing.
             \p Less functor has the interface like \p std::less and should meet \ref cds_intrusive_EllenBinTree_rcu_less
             "Predicate requirements".
-            \p pred must imply the same element order as the comparator used for building the tree.
+            \p Less must imply the same element order as the comparator used for building the set.
             \p pred should accept arguments of type \p Q, \p key_type, \p value_type in any combination.
         */
         template <typename Q, typename Less>
-        bool find_with( Q const& key, Less pred ) const
+        bool contains( Q const& key, Less pred ) const
         {
             CDS_UNUSED( pred );
             typedef ellen_bintree::details::compare<
@@ -1084,13 +1129,21 @@ namespace cds { namespace intrusive {
 
             rcu_lock l;
             search_result    res;
-            if ( search( res, key, compare_functor() )) {
+            if ( search( res, key, compare_functor())) {
                 m_Stat.onFindSuccess();
                 return true;
             }
             m_Stat.onFindFailed();
             return false;
         }
+        //@cond
+        template <typename Q, typename Less>
+        CDS_DEPRECATED("deprecated, use contains()")
+        bool find_with( Q const& key, Less pred ) const
+        {
+            return contains( key, pred );
+        }
+        //@endcond
 
         /// Finds the key \p key
         /** @anchor cds_intrusive_EllenBinTree_rcu_find_func
@@ -1157,7 +1210,7 @@ namespace cds { namespace intrusive {
         template <typename Q>
         value_type * get( Q const& key ) const
         {
-            return get_( key, node_compare() );
+            return get_( key, node_compare());
         }
 
         /// Finds \p key with \p pred predicate and return the item found
@@ -1196,7 +1249,7 @@ namespace cds { namespace intrusive {
             this sequence
             \code
             set.clear();
-            assert( set.empty() );
+            assert( set.empty());
             \endcode
             the assertion could be raised.
 
@@ -1206,7 +1259,7 @@ namespace cds { namespace intrusive {
         */
         void clear()
         {
-            for ( exempt_ptr ep = extract_min(); !ep.empty(); ep = extract_min() )
+            for ( exempt_ptr ep = extract_min(); !ep.empty(); ep = extract_min())
                 ep.release();
         }
 
@@ -1225,7 +1278,7 @@ namespace cds { namespace intrusive {
                 tree_node *     pLeaf = const_cast<internal_node *>( &m_Root );
 
                 // Get leftmost leaf
-                while ( pLeaf->is_internal() ) {
+                while ( pLeaf->is_internal()) {
                     pGrandParent = pParent;
                     pParent = static_cast<internal_node *>( pLeaf );
                     pLeaf = pParent->m_pLeft.load( memory_model::memory_order_relaxed );
@@ -1239,10 +1292,10 @@ namespace cds { namespace intrusive {
                 // Remove leftmost leaf and its parent node
                 assert( pGrandParent );
                 assert( pParent );
-                assert( pLeaf->is_leaf() );
+                assert( pLeaf->is_leaf());
 
                 pGrandParent->m_pLeft.store( pParent->m_pRight.load( memory_model::memory_order_relaxed ), memory_model::memory_order_relaxed );
-                free_leaf_node( node_traits::to_value_ptr( static_cast<leaf_node *>( pLeaf ) ) );
+                free_leaf_node( node_traits::to_value_ptr( static_cast<leaf_node *>( pLeaf )));
                 free_internal_node( pParent );
             }
         }
@@ -1292,11 +1345,11 @@ namespace cds { namespace intrusive {
                 && node_compare()( *pLeft, *pRight ) < 0 )
             {
                 bool bRet = true;
-                if ( pLeft->is_internal() )
-                    bRet = check_consistency( static_cast<internal_node *>( pLeft ) );
+                if ( pLeft->is_internal())
+                    bRet = check_consistency( static_cast<internal_node *>( pLeft ));
                 assert( bRet );
 
-                if ( bRet && pRight->is_internal() )
+                if ( bRet && pRight->is_internal())
                     bRet = bRet && check_consistency( static_cast<internal_node *>( pRight ));
                 assert( bRet );
 
@@ -1308,9 +1361,9 @@ namespace cds { namespace intrusive {
         void help( update_ptr /*pUpdate*/, retired_list& /*rl*/ )
         {
             /*
-            switch ( pUpdate.bits() ) {
+            switch ( pUpdate.bits()) {
                 case update_desc::IFlag:
-                    help_insert( pUpdate.ptr() );
+                    help_insert( pUpdate.ptr());
                     m_Stat.onHelpInsert();
                     break;
                 case update_desc::DFlag:
@@ -1318,7 +1371,7 @@ namespace cds { namespace intrusive {
                     //m_Stat.onHelpDelete();
                     break;
                 case update_desc::Mark:
-                    //help_marked( pUpdate.ptr() );
+                    //help_marked( pUpdate.ptr());
                     //m_Stat.onHelpMark();
                     break;
             }
@@ -1327,16 +1380,16 @@ namespace cds { namespace intrusive {
 
         void help_insert( update_desc * pOp )
         {
-            assert( gc::is_locked() );
+            assert( gc::is_locked());
 
             tree_node * pLeaf = static_cast<tree_node *>( pOp->iInfo.pLeaf );
             if ( pOp->iInfo.bRightLeaf ) {
                 pOp->iInfo.pParent->m_pRight.compare_exchange_strong( pLeaf, static_cast<tree_node *>( pOp->iInfo.pNew ),
-                    memory_model::memory_order_release, atomics::memory_order_relaxed );
+                    memory_model::memory_order_relaxed, atomics::memory_order_relaxed );
             }
             else {
                 pOp->iInfo.pParent->m_pLeft.compare_exchange_strong( pLeaf, static_cast<tree_node *>( pOp->iInfo.pNew ),
-                    memory_model::memory_order_release, atomics::memory_order_relaxed );
+                    memory_model::memory_order_relaxed, atomics::memory_order_relaxed );
             }
 
             update_ptr cur( pOp, update_desc::IFlag );
@@ -1349,20 +1402,13 @@ namespace cds { namespace intrusive {
             assert( res.pGrandParent != nullptr );
 
             return
-                static_cast<internal_node *>( res.bRightParent
-                    ? res.pGrandParent->m_pRight.load(memory_model::memory_order_relaxed)
-                    : res.pGrandParent->m_pLeft.load(memory_model::memory_order_relaxed)
-                ) == res.pParent
-                &&
-                static_cast<leaf_node *>( res.bRightLeaf
-                    ? res.pParent->m_pRight.load(memory_model::memory_order_relaxed)
-                    : res.pParent->m_pLeft.load(memory_model::memory_order_relaxed)
-                ) == res.pLeaf;
+                static_cast<internal_node *>( res.pGrandParent->get_child( res.bRightParent, memory_model::memory_order_relaxed )) == res.pParent
+             && static_cast<leaf_node *>( res.pParent->get_child( res.bRightLeaf, memory_model::memory_order_relaxed )) == res.pLeaf;
         }
 
         bool help_delete( update_desc * pOp, retired_list& rl )
         {
-            assert( gc::is_locked() );
+            assert( gc::is_locked());
 
             update_ptr pUpdate( pOp->dInfo.pUpdateParent );
             update_ptr pMark( pOp, update_desc::Mark );
@@ -1401,21 +1447,17 @@ namespace cds { namespace intrusive {
 
         void help_marked( update_desc * pOp )
         {
-            assert( gc::is_locked() );
+            assert( gc::is_locked());
 
             tree_node * p = pOp->dInfo.pParent;
             if ( pOp->dInfo.bRightParent ) {
                 pOp->dInfo.pGrandParent->m_pRight.compare_exchange_strong( p,
-                    pOp->dInfo.bRightLeaf
-                        ? pOp->dInfo.pParent->m_pLeft.load( memory_model::memory_order_acquire )
-                        : pOp->dInfo.pParent->m_pRight.load( memory_model::memory_order_acquire ),
+                    pOp->dInfo.pParent->get_child( !pOp->dInfo.bRightLeaf, memory_model::memory_order_acquire ),
                     memory_model::memory_order_release, atomics::memory_order_relaxed );
             }
             else {
                 pOp->dInfo.pGrandParent->m_pLeft.compare_exchange_strong( p,
-                    pOp->dInfo.bRightLeaf
-                        ? pOp->dInfo.pParent->m_pLeft.load( memory_model::memory_order_acquire )
-                        : pOp->dInfo.pParent->m_pRight.load( memory_model::memory_order_acquire ),
+                    pOp->dInfo.pParent->get_child( !pOp->dInfo.bRightLeaf, memory_model::memory_order_acquire ),
                     memory_model::memory_order_release, atomics::memory_order_relaxed );
             }
 
@@ -1427,7 +1469,7 @@ namespace cds { namespace intrusive {
         template <typename KeyValue, typename Compare>
         bool search( search_result& res, KeyValue const& key, Compare cmp ) const
         {
-            assert( gc::is_locked() );
+            assert( gc::is_locked());
 
             internal_node * pParent;
             internal_node * pGrandParent = nullptr;
@@ -1444,14 +1486,14 @@ namespace cds { namespace intrusive {
             pLeaf = const_cast<internal_node *>( &m_Root );
             updParent = nullptr;
             bRightLeaf = false;
-            while ( pLeaf->is_internal() ) {
+            while ( pLeaf->is_internal()) {
                 pGrandParent = pParent;
                 pParent = static_cast<internal_node *>( pLeaf );
                 bRightParent = bRightLeaf;
                 updGrandParent = updParent;
                 updParent = pParent->m_pUpdate.load( memory_model::memory_order_acquire );
 
-                switch ( updParent.bits() ) {
+                switch ( updParent.bits()) {
                     case update_desc::DFlag:
                     case update_desc::Mark:
                         m_Stat.onSearchRetry();
@@ -1460,12 +1502,11 @@ namespace cds { namespace intrusive {
 
                 nCmp = cmp( key, *pParent );
                 bRightLeaf = nCmp >= 0;
-                pLeaf = nCmp < 0 ? pParent->m_pLeft.load( memory_model::memory_order_acquire )
-                                 : pParent->m_pRight.load( memory_model::memory_order_acquire );
+                pLeaf = pParent->get_child( nCmp >= 0, memory_model::memory_order_acquire );
             }
 
-            assert( pLeaf->is_leaf() );
-            nCmp = cmp( key, *static_cast<leaf_node *>(pLeaf) );
+            assert( pLeaf->is_leaf());
+            nCmp = cmp( key, *static_cast<leaf_node *>(pLeaf));
 
             res.pGrandParent    = pGrandParent;
             res.pParent         = pParent;
@@ -1480,7 +1521,7 @@ namespace cds { namespace intrusive {
 
         bool search_min( search_result& res ) const
         {
-            assert( gc::is_locked() );
+            assert( gc::is_locked());
 
             internal_node * pParent;
             internal_node * pGrandParent = nullptr;
@@ -1491,13 +1532,13 @@ namespace cds { namespace intrusive {
         retry:
             pParent = nullptr;
             pLeaf = const_cast<internal_node *>( &m_Root );
-            while ( pLeaf->is_internal() ) {
+            while ( pLeaf->is_internal()) {
                 pGrandParent = pParent;
                 pParent = static_cast<internal_node *>( pLeaf );
                 updGrandParent = updParent;
                 updParent = pParent->m_pUpdate.load( memory_model::memory_order_acquire );
 
-                switch ( updParent.bits() ) {
+                switch ( updParent.bits()) {
                     case update_desc::DFlag:
                     case update_desc::Mark:
                         m_Stat.onSearchRetry();
@@ -1512,7 +1553,7 @@ namespace cds { namespace intrusive {
 
             res.pGrandParent    = pGrandParent;
             res.pParent         = pParent;
-            assert( pLeaf->is_leaf() );
+            assert( pLeaf->is_leaf());
             res.pLeaf           = static_cast<leaf_node *>( pLeaf );
             res.updParent       = updParent;
             res.updGrandParent  = updGrandParent;
@@ -1524,7 +1565,7 @@ namespace cds { namespace intrusive {
 
         bool search_max( search_result& res ) const
         {
-            assert( gc::is_locked() );
+            assert( gc::is_locked());
 
             internal_node * pParent;
             internal_node * pGrandParent = nullptr;
@@ -1538,28 +1579,22 @@ namespace cds { namespace intrusive {
             pParent = nullptr;
             pLeaf = const_cast<internal_node *>( &m_Root );
             bRightLeaf = false;
-            while ( pLeaf->is_internal() ) {
+            while ( pLeaf->is_internal()) {
                 pGrandParent = pParent;
                 pParent = static_cast<internal_node *>( pLeaf );
                 bRightParent = bRightLeaf;
                 updGrandParent = updParent;
                 updParent = pParent->m_pUpdate.load( memory_model::memory_order_acquire );
 
-                switch ( updParent.bits() ) {
+                switch ( updParent.bits()) {
                     case update_desc::DFlag:
                     case update_desc::Mark:
                         m_Stat.onSearchRetry();
                         goto retry;
                 }
 
-                if ( pParent->infinite_key()) {
-                    pLeaf = pParent->m_pLeft.load( memory_model::memory_order_acquire );
-                    bRightLeaf = false;
-                }
-                else {
-                    pLeaf = pParent->m_pRight.load( memory_model::memory_order_acquire );
-                    bRightLeaf = true;
-                }
+                bRightLeaf = !pParent->infinite_key();
+                pLeaf = pParent->get_child( bRightLeaf, memory_model::memory_order_acquire );
             }
 
             if ( pLeaf->infinite_key())
@@ -1567,7 +1602,7 @@ namespace cds { namespace intrusive {
 
             res.pGrandParent    = pGrandParent;
             res.pParent         = pParent;
-            assert( pLeaf->is_leaf() );
+            assert( pLeaf->is_leaf());
             res.pLeaf           = static_cast<leaf_node *>( pLeaf );
             res.updParent       = updParent;
             res.updGrandParent  = updGrandParent;
@@ -1590,7 +1625,7 @@ namespace cds { namespace intrusive {
             {
                 rcu_lock l;
                 for ( ;; ) {
-                    if ( !search( res, val, cmp ) || !eq( val, *res.pLeaf ) ) {
+                    if ( !search( res, val, cmp ) || !eq( val, *res.pLeaf )) {
                         if ( pOp )
                             retire_update_desc( pOp, updRetire, false );
                         m_Stat.onEraseFailed();
@@ -1604,7 +1639,7 @@ namespace cds { namespace intrusive {
                     else {
                         if ( !pOp )
                             pOp = alloc_update_desc();
-                        if ( check_delete_precondition( res ) ) {
+                        if ( check_delete_precondition( res )) {
                             pOp->dInfo.pGrandParent = res.pGrandParent;
                             pOp->dInfo.pParent = res.pParent;
                             pOp->dInfo.pLeaf = res.pLeaf;
@@ -1613,7 +1648,7 @@ namespace cds { namespace intrusive {
                             pOp->dInfo.bRightParent = res.bRightParent;
                             pOp->dInfo.bRightLeaf = res.bRightLeaf;
 
-                            update_ptr updGP( res.updGrandParent.ptr() );
+                            update_ptr updGP( res.updGrandParent.ptr());
                             if ( res.pGrandParent->m_pUpdate.compare_exchange_strong( updGP, update_ptr( pOp, update_desc::DFlag ),
                                 memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
                             {
@@ -1651,7 +1686,7 @@ namespace cds { namespace intrusive {
                 node_traits
             > compare_functor;
 
-            return extract_( val, compare_functor() );
+            return extract_( val, compare_functor());
         }
 
         template <typename Q, typename Compare>
@@ -1668,7 +1703,7 @@ namespace cds { namespace intrusive {
             {
                 rcu_lock l;
                 for ( ;; ) {
-                    if ( !search( res, val, cmp ) ) {
+                    if ( !search( res, val, cmp )) {
                         if ( pOp )
                             retire_update_desc( pOp, updRetire, false );
                         m_Stat.onEraseFailed();
@@ -1691,7 +1726,7 @@ namespace cds { namespace intrusive {
                             pOp->dInfo.bRightParent = res.bRightParent;
                             pOp->dInfo.bRightLeaf = res.bRightLeaf;
 
-                            update_ptr updGP( res.updGrandParent.ptr() );
+                            update_ptr updGP( res.updGrandParent.ptr());
                             if ( res.pGrandParent->m_pUpdate.compare_exchange_strong( updGP, update_ptr( pOp, update_desc::DFlag ),
                                 memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
                             {
@@ -1747,7 +1782,7 @@ namespace cds { namespace intrusive {
                     else {
                         if ( !pOp )
                             pOp = alloc_update_desc();
-                        if ( check_delete_precondition( res ) ) {
+                        if ( check_delete_precondition( res )) {
                             pOp->dInfo.pGrandParent = res.pGrandParent;
                             pOp->dInfo.pParent = res.pParent;
                             pOp->dInfo.pLeaf = res.pLeaf;
@@ -1756,7 +1791,7 @@ namespace cds { namespace intrusive {
                             pOp->dInfo.bRightParent = res.bRightParent;
                             pOp->dInfo.bRightLeaf = res.bRightLeaf;
 
-                            update_ptr updGP( res.updGrandParent.ptr() );
+                            update_ptr updGP( res.updGrandParent.ptr());
                             if ( res.pGrandParent->m_pUpdate.compare_exchange_strong( updGP, update_ptr( pOp, update_desc::DFlag ),
                                 memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
                             {
@@ -1811,7 +1846,7 @@ namespace cds { namespace intrusive {
                     else {
                         if ( !pOp )
                             pOp = alloc_update_desc();
-                        if ( check_delete_precondition( res ) ) {
+                        if ( check_delete_precondition( res )) {
                             pOp->dInfo.pGrandParent = res.pGrandParent;
                             pOp->dInfo.pParent = res.pParent;
                             pOp->dInfo.pLeaf = res.pLeaf;
@@ -1820,7 +1855,7 @@ namespace cds { namespace intrusive {
                             pOp->dInfo.bRightParent = res.bRightParent;
                             pOp->dInfo.bRightLeaf = res.bRightLeaf;
 
-                            update_ptr updGP( res.updGrandParent.ptr() );
+                            update_ptr updGP( res.updGrandParent.ptr());
                             if ( res.pGrandParent->m_pUpdate.compare_exchange_strong( updGP, update_ptr( pOp, update_desc::DFlag ),
                                 memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
                             {
@@ -1859,7 +1894,7 @@ namespace cds { namespace intrusive {
 
             rcu_lock l;
             search_result    res;
-            if ( search( res, val, compare_functor() )) {
+            if ( search( res, val, compare_functor())) {
                 assert( res.pLeaf );
                 f( *node_traits::to_value_ptr( res.pLeaf ), val );
 
@@ -1876,7 +1911,7 @@ namespace cds { namespace intrusive {
         {
             rcu_lock l;
             search_result    res;
-            if ( search( res, key, node_compare() )) {
+            if ( search( res, key, node_compare())) {
                 assert( res.pLeaf );
                 f( *node_traits::to_value_ptr( res.pLeaf ), key );
 
@@ -1906,22 +1941,19 @@ namespace cds { namespace intrusive {
 
         bool try_insert( value_type& val, internal_node * pNewInternal, search_result& res, retired_list& updRetire )
         {
-            assert( gc::is_locked() );
+            assert( gc::is_locked());
             assert( res.updParent.bits() == update_desc::Clean );
 
             // check search result
-            if ( static_cast<leaf_node *>( res.bRightLeaf
-                ? res.pParent->m_pRight.load( memory_model::memory_order_relaxed )
-                : res.pParent->m_pLeft.load( memory_model::memory_order_relaxed ) ) == res.pLeaf )
-            {
+            if ( static_cast<leaf_node *>( res.pParent->get_child( res.bRightLeaf, memory_model::memory_order_relaxed )) == res.pLeaf ) {
                 leaf_node * pNewLeaf = node_traits::to_node_ptr( val );
 
                 int nCmp = node_compare()( val, *res.pLeaf );
                 if ( nCmp < 0 ) {
                     if ( res.pGrandParent ) {
                         pNewInternal->infinite_key( 0 );
-                        key_extractor()( pNewInternal->m_Key, *node_traits::to_value_ptr( res.pLeaf ) );
-                        assert( !res.pLeaf->infinite_key() );
+                        key_extractor()( pNewInternal->m_Key, *node_traits::to_value_ptr( res.pLeaf ));
+                        assert( !res.pLeaf->infinite_key());
                     }
                     else {
                         assert( res.pLeaf->infinite_key() == tree_node::key_infinite1 );
@@ -1931,7 +1963,7 @@ namespace cds { namespace intrusive {
                     pNewInternal->m_pRight.store( static_cast<tree_node *>(res.pLeaf), memory_model::memory_order_release );
                 }
                 else {
-                    assert( !res.pLeaf->is_internal() );
+                    assert( !res.pLeaf->is_internal());
                     pNewInternal->infinite_key( 0 );
 
                     key_extractor()( pNewInternal->m_Key, val );
@@ -1947,7 +1979,7 @@ namespace cds { namespace intrusive {
                 pOp->iInfo.pLeaf = res.pLeaf;
                 pOp->iInfo.bRightLeaf = res.bRightLeaf;
 
-                update_ptr updCur( res.updParent.ptr() );
+                update_ptr updCur( res.updParent.ptr());
                 if ( res.pParent->m_pUpdate.compare_exchange_strong( updCur, update_ptr( pOp, update_desc::IFlag ),
                     memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
                 {