add "insert item troubleshooting" to docs
[libcds.git] / cds / intrusive / impl / lazy_list.h
index c0d64a96baa0425ced5454a27c561675680a246a..4e29b4cc056ea8d1029992e3265b71b6174c3f47 100644 (file)
@@ -3,10 +3,10 @@
 #ifndef __CDS_INTRUSIVE_IMPL_LAZY_LIST_H
 #define __CDS_INTRUSIVE_IMPL_LAZY_LIST_H
 
+#include <mutex>        // unique_lock
 #include <cds/intrusive/details/lazy_list_base.h>
 #include <cds/gc/guarded_ptr.h>
 
-
 namespace cds { namespace intrusive {
 
     /// Lazy ordered single-linked list
@@ -88,8 +88,7 @@ namespace cds { namespace intrusive {
         There are different specializations of this template for each garbage collecting schema used.
         You should select GC needed and include appropriate .h-file:
         - for gc::HP: \code #include <cds/intrusive/lazy_list_hp.h> \endcode
-        - for gc::PTB: \code #include <cds/intrusive/lazy_list_ptb.h> \endcode
-        - for gc::HRC: \code #include <cds/intrusive/lazy_list_hrc.h> \endcode
+        - for gc::DHP: \code #include <cds/intrusive/lazy_list_dhp.h> \endcode
         - for gc::nogc: \code #include <cds/intrusive/lazy_list_nogc.h> \endcode
         - for \ref cds_urcu_type "RCU" - see \ref cds_intrusive_LazyList_rcu "LazyList RCU specialization"
 
@@ -100,7 +99,7 @@ namespace cds { namespace intrusive {
         Example for gc::PTB and base hook:
         \code
         // Include GC-related lazy list specialization
-        #include <cds/intrusive/lazy_list_ptb.h>
+        #include <cds/intrusive/lazy_list_dhp.h>
 
         // Data stored in lazy list
         struct my_data: public cds::intrusive::lazy_list::node< cds::gc::PTB >
@@ -145,7 +144,7 @@ namespace cds { namespace intrusive {
         Equivalent option-based code:
         \code
         // GC-related specialization
-        #include <cds/intrusive/lazy_list_ptb.h>
+        #include <cds/intrusive/lazy_list_dhp.h>
 
         struct my_data {
             // see above
@@ -218,30 +217,8 @@ namespace cds { namespace intrusive {
 
     protected:
         //@cond
-        typedef lazy_list::boundary_nodes<
-            gc
-            ,typename opt::select_default< typename options::boundary_node_type, node_type >::type
-            ,typename options::allocator
-        >   boundary_nodes;
-        boundary_nodes  m_Boundary    ;   ///< Head & tail dummy nodes
-
-        node_type *     head()
-        {
-            return m_Boundary.head();
-        }
-        node_type const * head() const
-        {
-            return m_Boundary.head();
-        }
-        node_type * tail()
-        {
-            return m_Boundary.tail();
-        }
-        node_type const * tail() const
-        {
-            return m_Boundary.tail();
-        }
-        //@endcond
+        node_type   m_Head;
+        node_type   m_Tail;
 
         item_counter    m_ItemCounter   ;   ///< Item counter
 
@@ -440,7 +417,7 @@ namespace cds { namespace intrusive {
             The forward iterator for lazy list has some features:
             - it has no post-increment operator
             - to protect the value, the iterator contains a GC-specific guard + another guard is required locally for increment operator.
-              For some GC (gc::HP, gc::HRC), a guard is limited resource per thread, so an exception (or assertion) "no free guard"
+              For some GC (\p gc::HP), a guard is limited resource per thread, so an exception (or assertion) "no free guard"
               may be thrown if a limit of guard count per thread is exceeded.
             - The iterator cannot be moved across thread boundary since it contains GC's guard that is thread-private GC data.
             - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent
@@ -462,7 +439,7 @@ namespace cds { namespace intrusive {
         */
         iterator begin()
         {
-            iterator it( head() );
+            iterator it( &m_Head );
             ++it        ;   // skip dummy head
             return it;
         }
@@ -476,7 +453,7 @@ namespace cds { namespace intrusive {
         */
         iterator end()
         {
-            return iterator( tail() );
+            return iterator( &m_Tail );
         }
 
         /// Returns a forward const iterator addressing the first element in a list
@@ -507,13 +484,13 @@ namespace cds { namespace intrusive {
         //@cond
         const_iterator get_const_begin() const
         {
-            const_iterator it( const_cast<node_type *>( head() ));
+            const_iterator it( const_cast<node_type *>( &m_Head ));
             ++it        ;   // skip dummy head
             return it;
         }
         const_iterator get_const_end() const
         {
-            return const_iterator( const_cast<node_type *>( tail() ));
+            return const_iterator( const_cast<node_type *>(&m_Tail) );
         }
         //@endcond
 
@@ -522,17 +499,15 @@ namespace cds { namespace intrusive {
         LazyList()
         {
             static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" );
-
-            //m_pTail = cxx_allocator().New();
-            head()->m_pNext.store( marked_node_ptr( tail() ), memory_model::memory_order_relaxed );
+            m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed );
         }
 
         /// Destroys the list object
         ~LazyList()
         {
             clear();
-            assert( head()->m_pNext.load(memory_model::memory_order_relaxed).ptr() == tail() );
-            head()->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
+            assert( m_Head.m_pNext.load( memory_model::memory_order_relaxed ).ptr() == &m_Tail );
+            m_Head.m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
         }
 
         /// Inserts new node
@@ -544,7 +519,7 @@ namespace cds { namespace intrusive {
         */
         bool insert( value_type& val )
         {
-            return insert_at( head(), val );
+            return insert_at( &m_Head, val );
         }
 
         /// Inserts new node
@@ -562,13 +537,14 @@ namespace cds { namespace intrusive {
             \endcode
             where \p val is the item inserted.
             While the functor \p f is working the item \p val is locked.
-            The user-defined functor is called only if the inserting is success and may be passed by reference
-            using \p std::ref
+            The user-defined functor is called only if the inserting is success.
+
+            @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
         bool insert( value_type& val, Func f )
         {
-            return insert_at( head(), val, f );
+            return insert_at( &m_Head, val, f );
         }
 
         /// Ensures that the \p item exists in the list
@@ -591,16 +567,16 @@ namespace cds { namespace intrusive {
             The functor may change non-key fields of the \p item.
             While the functor \p f is working the item \p item is locked.
 
-            You may pass \p func argument by reference using \p std::ref.
-
             Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
             \p second is \p true if new item has been added or \p false if the item with \p key
             already is in the list.
+
+            @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
         std::pair<bool, bool> ensure( value_type& val, Func func )
         {
-            return ensure_at( head(), val, func );
+            return ensure_at( &m_Head, val, func );
         }
 
         /// Unlinks the item \p val from the list
@@ -617,7 +593,7 @@ namespace cds { namespace intrusive {
         */
         bool unlink( value_type& val )
         {
-            return unlink_at( head(), val );
+            return unlink_at( &m_Head, val );
         }
 
         /// Deletes the item from the list
@@ -629,7 +605,7 @@ namespace cds { namespace intrusive {
         template <typename Q>
         bool erase( Q const& val )
         {
-            return erase_at( head(), val, key_comparator() );
+            return erase_at( &m_Head, val, key_comparator() );
         }
 
         /// Deletes the item from the list using \p pred predicate for searching
@@ -642,7 +618,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less>
         bool erase_with( Q const& val, Less pred )
         {
-            return erase_at( head(), val, cds::opt::details::make_comparator_from_less<Less>() );
+            return erase_at( &m_Head, val, cds::opt::details::make_comparator_from_less<Less>() );
         }
 
         /// Deletes the item from the list
@@ -662,7 +638,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Func>
         bool erase( const Q& val, Func func )
         {
-            return erase_at( head(), val, key_comparator(), func );
+            return erase_at( &m_Head, val, key_comparator(), func );
         }
 
         /// Deletes the item from the list using \p pred predicate for searching
@@ -675,7 +651,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less, typename Func>
         bool erase_with( const Q& val, Less pred, Func func )
         {
-            return erase_at( head(), val, cds::opt::details::make_comparator_from_less<Less>(), func );
+            return erase_at( &m_Head, val, cds::opt::details::make_comparator_from_less<Less>(), func );
         }
 
         /// Extracts the item from the list with specified \p key
@@ -709,7 +685,7 @@ namespace cds { namespace intrusive {
         template <typename Q>
         bool extract( guarded_ptr& dest, Q const& key )
         {
-            return extract_at( head(), dest.guard(), key, key_comparator() );
+            return extract_at( &m_Head, dest.guard(), key, key_comparator() );
         }
 
         /// Extracts the item from the list with comparing functor \p pred
@@ -724,7 +700,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less>
         bool extract_with( guarded_ptr& dest, Q const& key, Less pred )
         {
-            return extract_at( head(), dest.guard(), key, cds::opt::details::make_comparator_from_less<Less>() );
+            return extract_at( &m_Head, dest.guard(), key, cds::opt::details::make_comparator_from_less<Less>() );
         }
 
         /// Finds the key \p val
@@ -751,7 +727,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Func>
         bool find( Q& val, Func f )
         {
-            return find_at( head(), val, key_comparator(), f );
+            return find_at( &m_Head, val, key_comparator(), f );
         }
 
         /// Finds the key \p val using \p pred predicate for searching
@@ -764,7 +740,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less, typename Func>
         bool find_with( Q& val, Less pred, Func f )
         {
-            return find_at( head(), val, cds::opt::details::make_comparator_from_less<Less>(), f );
+            return find_at( &m_Head, val, cds::opt::details::make_comparator_from_less<Less>(), f );
         }
 
         /// Finds the key \p val
@@ -788,7 +764,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Func>
         bool find( Q const& val, Func f )
         {
-            return find_at( head(), val, key_comparator(), f );
+            return find_at( &m_Head, val, key_comparator(), f );
         }
 
         /// Finds the key \p val using \p pred predicate for searching
@@ -801,7 +777,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less, typename Func>
         bool find_with( Q const& val, Less pred, Func f )
         {
-            return find_at( head(), val, cds::opt::details::make_comparator_from_less<Less>(), f );
+            return find_at( &m_Head, val, cds::opt::details::make_comparator_from_less<Less>(), f );
         }
 
         /// Finds the key \p val
@@ -812,7 +788,7 @@ namespace cds { namespace intrusive {
         template <typename Q>
         bool find( Q const & val )
         {
-            return find_at( head(), val, key_comparator() );
+            return find_at( &m_Head, val, key_comparator() );
         }
 
         /// Finds the key \p val using \p pred predicate for searching
@@ -825,7 +801,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less>
         bool find_with( Q const& val, Less pred )
         {
-            return find_at( head(), val, cds::opt::details::make_comparator_from_less<Less>() );
+            return find_at( &m_Head, val, cds::opt::details::make_comparator_from_less<Less>() );
         }
 
         /// Finds the key \p val and return the item found
@@ -861,7 +837,7 @@ namespace cds { namespace intrusive {
         template <typename Q>
         bool get( guarded_ptr& ptr, Q const& val )
         {
-            return get_at( head(), ptr.guard(), val, key_comparator() );
+            return get_at( &m_Head, ptr.guard(), val, key_comparator() );
         }
 
         /// Finds the key \p val and return the item found
@@ -876,7 +852,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less>
         bool get_with( guarded_ptr& ptr, Q const& val, Less pred )
         {
-            return get_at( head(), ptr.guard(), val, cds::opt::details::make_comparator_from_less<Less>() );
+            return get_at( &m_Head, ptr.guard(), val, cds::opt::details::make_comparator_from_less<Less>() );
         }
 
         /// Clears the list
@@ -888,16 +864,16 @@ namespace cds { namespace intrusive {
             typename gc::Guard guard;
             marked_node_ptr h;
             while ( !empty() ) {
-                h = head()->m_pNext.load(memory_model::memory_order_relaxed);
+                h = m_Head.m_pNext.load( memory_model::memory_order_relaxed );
                 guard.assign( node_traits::to_value_ptr( h.ptr() ));
-                if ( head()->m_pNext.load(memory_model::memory_order_acquire) == h ) {
-                    head()->m_Lock.lock();
+                if ( m_Head.m_pNext.load(memory_model::memory_order_acquire) == h ) {
+                    m_Head.m_Lock.lock();
                     h->m_Lock.lock();
 
-                    unlink_node( head(), h.ptr(), head() );
+                    unlink_node( &m_Head, h.ptr(), &m_Head );
 
                     h->m_Lock.unlock();
-                    head()->m_Lock.unlock();
+                    m_Head.m_Lock.unlock();
 
                     retire_node( h.ptr() ) ; // free node
                 }
@@ -907,7 +883,7 @@ namespace cds { namespace intrusive {
         /// Checks if the list is empty
         bool empty() const
         {
-            return head()->m_pNext.load(memory_model::memory_order_relaxed).ptr() == tail();
+            return m_Head.m_pNext.load( memory_model::memory_order_relaxed ).ptr() == &m_Tail;
         }
 
         /// Returns list's item count
@@ -928,7 +904,7 @@ namespace cds { namespace intrusive {
         // split-list support
         bool insert_aux_node( node_type * pNode )
         {
-            return insert_aux_node( head(), pNode );
+            return insert_aux_node( &m_Head, pNode );
         }
 
         // split-list support
@@ -953,7 +929,7 @@ namespace cds { namespace intrusive {
                 {
                     auto_lock_position alp( pos );
                     if ( validate( pos.pPred, pos.pCur )) {
-                        if ( pos.pCur != tail() && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
+                        if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
                             // failed: key already in list
                             return false;
                         }
@@ -979,7 +955,7 @@ namespace cds { namespace intrusive {
                 {
                     auto_lock_position alp( pos );
                     if ( validate( pos.pPred, pos.pCur )) {
-                        if ( pos.pCur != tail() && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
+                        if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
                             // failed: key already in list
                             return false;
                         }
@@ -1005,7 +981,7 @@ namespace cds { namespace intrusive {
                 {
                     auto_lock_position alp( pos );
                     if ( validate( pos.pPred, pos.pCur )) {
-                        if ( pos.pCur != tail() && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
+                        if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
                             // key already in the list
 
                             func( false, *node_traits::to_value_ptr( *pos.pCur ) , val );
@@ -1037,7 +1013,7 @@ namespace cds { namespace intrusive {
                     {
                         auto_lock_position alp( pos );
                         if ( validate( pos.pPred, pos.pCur ) ) {
-                            if ( pos.pCur != tail()
+                            if ( pos.pCur != &m_Tail
                                 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0
                                 && node_traits::to_value_ptr( pos.pCur ) == &val )
                             {
@@ -1071,7 +1047,7 @@ namespace cds { namespace intrusive {
                     {
                         auto_lock_position alp( pos );
                         if ( validate( pos.pPred, pos.pCur )) {
-                            if ( pos.pCur != tail() && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
+                            if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
                                 // key found
                                 unlink_node( pos.pPred, pos.pCur, pHead );
                                 f( *node_traits::to_value_ptr( *pos.pCur ) );
@@ -1125,8 +1101,8 @@ namespace cds { namespace intrusive {
             position pos;
 
             search( pHead, val, pos, cmp );
-            if ( pos.pCur != tail() ) {
-                cds::lock::scoped_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
+            if ( pos.pCur != &m_Tail ) {
+                std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
                 if ( !pos.pCur->is_marked()
                     && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
                 {
@@ -1143,7 +1119,7 @@ namespace cds { namespace intrusive {
             position pos;
 
             search( pHead, val, pos, cmp );
-            return pos.pCur != tail()
+            return pos.pCur != &m_Tail
                 && !pos.pCur->is_marked()
                 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0;
         }
@@ -1154,7 +1130,7 @@ namespace cds { namespace intrusive {
             position pos;
 
             search( pHead, val, pos, cmp );
-            if ( pos.pCur != tail()
+            if ( pos.pCur != &m_Tail
                 && !pos.pCur->is_marked()
                 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
             {
@@ -1171,7 +1147,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Compare>
         void search( node_type * pHead, const Q& key, position& pos, Compare cmp )
         {
-            const node_type * pTail = tail();
+            const node_type * pTail = &m_Tail;
 
             marked_node_ptr pCur( pHead );
             marked_node_ptr pPrev( pHead );