Updated copyright
[libcds.git] / cds / intrusive / lazy_list_nogc.h
index cd31be7b7809bb67cdfdf32364a1dbf551462bf9..01fc656e4528ce6a6827a844e32a8d1f95fa086d 100644 (file)
@@ -1,11 +1,11 @@
 /*
     This file is a part of libcds - Concurrent Data Structures library
 
-    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+    (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:
 
@@ -25,7 +25,7 @@
     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.     
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #ifndef CDSLIB_INTRUSIVE_LAZY_LIST_NOGC_H
@@ -119,10 +119,13 @@ namespace cds { namespace intrusive {
         typedef typename get_node_traits< value_type, node_type, hook>::type node_traits;    ///< node traits
         typedef typename lazy_list::get_link_checker< node_type, traits::link_checker >::type link_checker;   ///< link checker
 
-        typedef typename traits::item_counter item_counter;  ///< Item counting policy used
-        typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see lazy_list::traits::memory_model)
+        typedef typename traits::item_counter item_counter; ///< Item counting policy used
+        typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see \p lazy_list::traits::memory_model)
+        typedef typename traits::stat         stat;         ///< Internal statistics
 
         //@cond
+        static_assert((std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type");
+
         // Rebind traits (split-list support)
         template <typename... Options>
         struct rebind_traits {
@@ -132,6 +135,10 @@ namespace cds { namespace intrusive {
                 , typename cds::opt::make_options< traits, Options...>::type
             >   type;
         };
+
+        // Stat selector
+        template <typename Stat>
+        using select_stat_wrapper = lazy_list::select_stat_wrapper< Stat >;
         //@endcond
 
     protected:
@@ -141,6 +148,7 @@ namespace cds { namespace intrusive {
         node_type       m_Head;        ///< List head (dummy node)
         node_type       m_Tail;        ///< List tail (dummy node)
         item_counter    m_ItemCounter; ///< Item counter
+        mutable stat    m_Stat;        ///< Internal statistics
 
         //@cond
 
@@ -328,7 +336,7 @@ namespace cds { namespace intrusive {
         /// Returns a forward const iterator addressing the first element in a list
         const_iterator cbegin() const
         {
-            const_iterator it( const_cast<node_type *>(&m_Head) );
+            const_iterator it( const_cast<node_type *>(&m_Head));
             ++it;   // skip dummy head
             return it;
         }
@@ -341,17 +349,25 @@ namespace cds { namespace intrusive {
         /// Returns an const iterator that addresses the location succeeding the last element in a list
         const_iterator cend() const
         {
-            return const_iterator( const_cast<node_type *>(&m_Tail) );
+            return const_iterator( const_cast<node_type *>(&m_Tail));
         }
 
     public:
         /// Default constructor initializes empty list
         LazyList()
         {
-            static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" );
             m_Head.m_pNext.store( &m_Tail, memory_model::memory_order_relaxed );
         }
 
+        //@cond
+        template <typename Stat, typename = std::enable_if<std::is_same<stat, lazy_list::wrapped_stat<Stat>>::value >>
+        explicit LazyList( Stat& st )
+            : m_Stat( st )
+        {
+            m_Head.m_pNext.store( &m_Tail, memory_model::memory_order_relaxed );
+        }
+        //@endcond
+
         /// Destroys the list object
         ~LazyList()
         {
@@ -395,7 +411,7 @@ namespace cds { namespace intrusive {
             The functor may change non-key fields of the \p item.
             While the functor \p f is calling the item \p item is locked.
 
-            Returns <tt> std::pair<bool, bool>  </tt> where \p first is \p true if operation is successfull,
+            Returns <tt> std::pair<bool, bool>  </tt> where \p first is \p true if operation is successful,
             \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.
         */
@@ -493,7 +509,7 @@ namespace cds { namespace intrusive {
         template <typename Q>
         value_type * contains( Q const& key )
         {
-            return find_at( &m_Head, key, key_comparator() );
+            return find_at( &m_Head, key, key_comparator());
         }
         //@cond
         template <typename Q>
@@ -514,7 +530,7 @@ namespace cds { namespace intrusive {
         typename std::enable_if<Sort, value_type *>::type contains( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
-            return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>() );
+            return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>());
         }
         //@cond
         template <typename Q, typename Less, bool Sort = c_bSort>
@@ -570,7 +586,7 @@ namespace cds { namespace intrusive {
         */
         void clear()
         {
-            clear( disposer() );
+            clear( disposer());
         }
 
         /// Checks if the list is empty
@@ -592,6 +608,12 @@ namespace cds { namespace intrusive {
             return m_ItemCounter.value();
         }
 
+        /// Returns const reference to internal statistics
+        stat const& statistics() const
+        {
+            return m_Stat;
+        }
+
     protected:
         //@cond
         // split-list support
@@ -609,7 +631,7 @@ namespace cds { namespace intrusive {
             // Hack: convert node_type to value_type.
             // In principle, auxiliary node can be non-reducible to value_type
             // We assume that comparator can correctly distinguish aux and regular node.
-            return insert_at( pHead, *node_traits::to_value_ptr( pNode ) );
+            return insert_at( pHead, *node_traits::to_value_ptr( pNode ));
         }
 
         bool insert_at( node_type * pHead, value_type& val )
@@ -622,18 +644,24 @@ namespace cds { namespace intrusive {
                 {
                     auto_lock_position alp( pos );
                     if ( validate( pos.pPred, pos.pCur )) {
-                        if ( pos.pCur != &m_Tail && equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) ) {
+                        if ( pos.pCur != &m_Tail && equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred )) {
                             // failed: key already in list
+                            m_Stat.onInsertFailed();
                             return false;
                         }
                         else {
                             link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
-                            ++m_ItemCounter;
-                            return true;
+                            break;
                         }
                     }
                 }
+
+                m_Stat.onInsertRetry();
             }
+
+            ++m_ItemCounter;
+            m_Stat.onInsertSuccess();
+            return true;
         }
 
         iterator insert_at_( node_type * pHead, value_type& val )
@@ -659,21 +687,29 @@ namespace cds { namespace intrusive {
                             // key already in the list
 
                             func( false, *node_traits::to_value_ptr( *pos.pCur ) , val );
+                            m_Stat.onUpdateExisting();
                             return std::make_pair( iterator( pos.pCur ), false );
                         }
                         else {
                             // new key
-                            if ( !bAllowInsert )
+                            if ( !bAllowInsert ) {
+                                m_Stat.onUpdateFailed();
                                 return std::make_pair( end(), false );
+                            }
 
                             link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
                             func( true, val, val );
-                            ++m_ItemCounter;
-                            return std::make_pair( iterator( node_traits::to_node_ptr( val )), true );
+                            break;
                         }
                     }
+
+                    m_Stat.onUpdateRetry();
                 }
             }
+
+            ++m_ItemCounter;
+            m_Stat.onUpdateNew();
+            return std::make_pair( iterator( node_traits::to_node_ptr( val )), true );
         }
 
         template <typename Func>
@@ -691,12 +727,15 @@ namespace cds { namespace intrusive {
             search( pHead, val, pos, pred );
             if ( pos.pCur != &m_Tail ) {
                 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
-                if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) )
+                if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ))
                 {
                     f( *node_traits::to_value_ptr( *pos.pCur ), val );
+                    m_Stat.onFindSuccess();
                     return true;
                 }
             }
+
+            m_Stat.onFindFailed();
             return false;
         }
 
@@ -704,7 +743,7 @@ namespace cds { namespace intrusive {
         value_type * find_at( node_type * pHead, Q& val, Pred pred)
         {
             iterator it = find_at_( pHead, val, pred );
-            if ( it != end() )
+            if ( it != end())
                 return &*it;
             return nullptr;
         }
@@ -716,9 +755,13 @@ namespace cds { namespace intrusive {
 
             search( pHead, val, pos, pred );
             if ( pos.pCur != &m_Tail ) {
-                if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ))
+                if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred )) {
+                    m_Stat.onFindSuccess();
                     return iterator( pos.pCur );
+                }
             }
+
+            m_Stat.onFindFailed();
             return end();
         }
 
@@ -734,7 +777,7 @@ namespace cds { namespace intrusive {
             node_type * pCur = pHead;
             node_type * pPrev = pHead;
 
-            while ( pCur != pTail && ( pCur == pHead || !equal( *node_traits::to_value_ptr( *pCur ), key, eq ) )) {
+            while ( pCur != pTail && ( pCur == pHead || !equal( *node_traits::to_value_ptr( *pCur ), key, eq ))) {
                 pPrev = pCur;
                 pCur = pCur->m_pNext.load(memory_model::memory_order_acquire);
             }
@@ -772,9 +815,15 @@ namespace cds { namespace intrusive {
             return cmp(l, r) == 0;
         }
 
-        static bool validate( node_type * pPred, node_type * pCur )
+        bool validate( node_type * pPred, node_type * pCur )
         {
-            return pPred->m_pNext.load(memory_model::memory_order_acquire) == pCur;
+            if ( pPred->m_pNext.load(memory_model::memory_order_acquire) == pCur ) {
+                m_Stat.onValidationSuccess();
+                return true;
+            }
+
+            m_Stat.onValidationFailed();
+            return false;
         }
 
         // for split-list
@@ -789,7 +838,7 @@ namespace cds { namespace intrusive {
                 if ( pred( *node_traits::to_value_ptr( pHead ))) {
                     assert( pPred != nullptr );
                     pPred->m_pNext.store( p, memory_model::memory_order_relaxed );
-                    dispose_node( pHead, disposer() );
+                    dispose_node( pHead, disposer());
                 }
                 else
                     pPred = pHead;