Added internal statistics to MichaelSet/Map
[libcds.git] / cds / container / michael_kvlist_nogc.h
index 755d7c534d2fbb2524413defbf9465c7e5ed7fe9..f1b20003a42359fa5fd9441055712d24b62fcc93 100644 (file)
@@ -1,7 +1,35 @@
-//$$CDS-header$$
-
-#ifndef __CDS_CONTAINER_MICHAEL_KVLIST_NOGC_H
-#define __CDS_CONTAINER_MICHAEL_KVLIST_NOGC_H
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+
+    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_CONTAINER_MICHAEL_KVLIST_NOGC_H
+#define CDSLIB_CONTAINER_MICHAEL_KVLIST_NOGC_H
 
 #include <memory>
 #include <cds/container/details/michael_list_base.h>
@@ -84,6 +112,23 @@ namespace cds { namespace container {
         typedef typename base_class::item_counter item_counter;   ///< Item counting policy used
         typedef typename maker::key_comparator    key_comparator; ///< key comparison functor
         typedef typename base_class::memory_model memory_model;   ///< Memory ordering. See cds::opt::memory_model option
+        typedef typename base_class::stat         stat;           ///< Internal statistics
+
+        //@cond
+        // Rebind traits (split-list support)
+        template <typename... Options>
+        struct rebind_traits {
+            typedef MichaelKVList<
+                gc
+                , key_type, mapped_type
+                , typename cds::opt::make_options< traits, Options...>::type
+            > type;
+        };
+
+        // Stat selector
+        template <typename Stat>
+        using select_stat_wrapper = typename base_class::template select_stat_wrapper< Stat >;
+        //@endcond
 
     protected:
         //@cond
@@ -93,32 +138,6 @@ namespace cds { namespace container {
         typedef typename maker::intrusive_traits::compare  intrusive_key_comparator;
 
         typedef typename base_class::atomic_node_ptr head_type;
-        //@endcond
-
-    protected:
-        //@cond
-        template <typename K>
-        static node_type * alloc_node(const K& key)
-        {
-            return cxx_allocator().New( key );
-        }
-
-        template <typename K, typename V>
-        static node_type * alloc_node( const K& key, const V& val )
-        {
-            return cxx_allocator().New( key, val );
-        }
-
-        template <typename K, typename... Args>
-        static node_type * alloc_node( K&& key, Args&&... args )
-        {
-            return cxx_allocator().MoveNew( std::forward<K>(key), std::forward<Args>(args)... );
-        }
-
-        static void free_node( node_type * pNode )
-        {
-            cxx_allocator().Delete( pNode );
-        }
 
         struct node_disposer {
             void operator()( node_type * pNode )
@@ -127,16 +146,6 @@ namespace cds { namespace container {
             }
         };
         typedef std::unique_ptr< node_type, node_disposer >     scoped_node_ptr;
-
-        head_type& head()
-        {
-            return base_class::m_pHead;
-        }
-
-        head_type const& head() const
-        {
-            return base_class::m_pHead;
-        }
         //@endcond
 
     protected:
@@ -229,17 +238,23 @@ namespace cds { namespace container {
         //@endcond
 
     public:
+    ///@name Forward iterators
+    //@{
         /// Forward iterator
         /**
-            The forward iterator for Michael's list based on gc::nogc has pre- and post-increment operators.
+            The forward iterator is safe: you may use it in multi-threaded enviromnent without any synchronization.
+
+            The forward iterator for Michael's list based on \p gc::nogc has pre- and post-increment operators.
 
             The iterator interface to access item data:
-            - <tt> operator -> </tt> - returns a pointer to \ref value_type for iterator
-            - <tt> operator *</tt> - returns a reference (a const reference for \p const_iterator) to \ref value_type for iterator
+            - <tt> operator -> </tt> - returns a pointer to \p value_type
+            - <tt> operator *</tt> - returns a reference (a const reference for \p const_iterator) to \p value_type
             - <tt> const key_type& key() </tt> - returns a key reference for iterator
             - <tt> mapped_type& val() </tt> - retuns a value reference for iterator (const reference for \p const_iterator)
 
-            For both functions the iterator should not be equal to <tt> end() </tt>
+            For both functions the iterator should not be equal to \p end().
+
+            @note \p end() iterator is not dereferenceable
         */
         typedef iterator_type<false>    iterator;
 
@@ -272,38 +287,27 @@ namespace cds { namespace container {
         }
 
         /// Returns a forward const iterator addressing the first element in a list
-        //@{
         const_iterator begin() const
         {
             return const_iterator( head() );
         }
+        /// Returns a forward const iterator addressing the first element in a list
         const_iterator cbegin() const
         {
             return const_iterator( head() );
         }
-        //@}
 
         /// Returns an const iterator that addresses the location succeeding the last element in a list
-        //@{
         const_iterator end() const
         {
             return const_iterator();
         }
+        /// Returns an const iterator that addresses the location succeeding the last element in a list
         const_iterator cend() const
         {
             return const_iterator();
         }
-        //@}
-
-    protected:
-        //@cond
-        iterator node_to_iterator( node_type * pNode )
-        {
-            if ( pNode )
-                return iterator( *pNode );
-            return end();
-        }
-        //@endcond
+    //@}
 
     public:
         /// Default constructor
@@ -313,7 +317,14 @@ namespace cds { namespace container {
         MichaelKVList()
         {}
 
-        /// List desctructor
+        //@cond
+        template <typename Stat, typename = std::enable_if<std::is_same<stat, michael_list::wrapped_stat<Stat>>::value >>
+        explicit MichaelKVList( Stat& st )
+            : base_class( st )
+        {}
+        //@endcond
+
+        /// List destructor
         /**
             Clears the list
         */
@@ -386,26 +397,36 @@ namespace cds { namespace container {
             Returns an iterator pointed to inserted value, or \p end() if inserting is failed
         */
         template <typename K, typename Func>
-        iterator insert_key( const K& key, Func func )
+        iterator insert_with( const K& key, Func func )
         {
-            return node_to_iterator( insert_key_at( head(), key, func ));
+            return node_to_iterator( insert_with_at( head(), key, func ));
         }
 
-        /// Ensures that the key \p key exists in the list
+        /// Updates the item
         /**
-            The operation inserts new item if the key \p key is not found in the list.
-            Otherwise, the function returns an iterator that points to item found.
+            If \p key is not in the list and \p bAllowInsert is \p true,
 
-            Returns <tt> std::pair<iterator, bool>  </tt> where \p first is an iterator pointing to
+            the function inserts a new item.
+            Otherwise, the function returns an iterator pointing to the item found.
+
+            Returns <tt> std::pair<iterator, bool> </tt> where \p first is an iterator pointing to
             item found or inserted, \p second is true if new item has been added or \p false if the item
             already is in the list.
         */
         template <typename K>
-        std::pair<iterator, bool> ensure( const K& key )
+        std::pair<iterator, bool> update( K const& key, bool bAllowInsert = true )
         {
-            std::pair< node_type *, bool > ret = ensure_at( head(), key );
+            std::pair< node_type *, bool > ret = update_at( head(), key, bAllowInsert );
             return std::make_pair( node_to_iterator( ret.first ), ret.second );
         }
+        //@cond
+        template <typename K>
+        CDS_DEPRECATED("ensure() is deprecated, use update()")
+        std::pair<iterator, bool> ensure( K const& key )
+        {
+            return update( key );
+        }
+        //@endcond
 
         /// Inserts data of type \ref mapped_type constructed with <tt>std::forward<Args>(args)...</tt>
         /**
@@ -417,31 +438,45 @@ namespace cds { namespace container {
             return node_to_iterator( emplace_at( head(), std::forward<K>(key), std::forward<Args>(args)... ));
         }
 
-        /// Find the key \p key
-        /** \anchor cds_nonintrusive_MichaelKVList_nogc_find
-
+        /// Checks whether the list contains \p key
+        /**
             The function searches the item with key equal to \p key
-            and returns an iterator pointed to item found if the key is found,
-            and \ref end() otherwise
+            and returns an iterator pointed to item found and \ref end() otherwise
         */
         template <typename Q>
-        iterator find( Q const& key )
+        iterator contains( Q const& key )
         {
             return node_to_iterator( find_at( head(), key, intrusive_key_comparator() ) );
         }
+        //@cond
+        template <typename Q>
+        CDS_DEPRECATED("deprecated, use contains()")
+        iterator find( Q const& key )
+        {
+            return contains( key );
+        }
+        //@endcond
 
-        /// Finds the key \p key using \p pred predicate for searching
+        /// Checks whether the list contains \p key using \p pred predicate for searching
         /**
-            The function is an analog of \ref cds_nonintrusive_MichaelKVList_nogc_find "find(Q const&)"
-            but \p pred is used for key comparing.
+            The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
             \p Less functor has the interface like \p std::less.
             \p pred must imply the same element order as the comparator used for building the list.
         */
         template <typename Q, typename Less>
-        iterator find_with( Q const& key, Less pred )
+        iterator contains( Q const& key, Less pred )
         {
+            CDS_UNUSED( pred );
             return node_to_iterator( find_at( head(), key, typename maker::template less_wrapper<Less>::type() ) );
         }
+        //@cond
+        template <typename Q, typename Less>
+        CDS_DEPRECATED("deprecated, use contains()")
+        iterator find_with( Q const& key, Less pred )
+        {
+            return contains( key, pred );
+        }
+        //@endcond
 
         /// Check if the list is empty
         bool empty() const
@@ -462,6 +497,12 @@ namespace cds { namespace container {
             return base_class::size();
         }
 
+        /// Returns const reference to internal statistics
+        stat const& statistics() const
+        {
+            return base_class::statistics();
+        }
+
         /// Clears the list
         void clear()
         {
@@ -492,7 +533,7 @@ namespace cds { namespace container {
         }
 
         template <typename K, typename Func>
-        node_type * insert_key_at( head_type& refHead, const K& key, Func f )
+        node_type * insert_with_at( head_type& refHead, const K& key, Func f )
         {
             scoped_node_ptr pNode( alloc_node( key ));
 
@@ -504,15 +545,17 @@ namespace cds { namespace container {
         }
 
         template <typename K>
-        std::pair< node_type *, bool > ensure_at( head_type& refHead, const K& key )
+        std::pair< node_type *, bool > update_at( head_type& refHead, const K& key, bool bAllowInsert )
         {
             scoped_node_ptr pNode( alloc_node( key ));
             node_type * pItemFound = nullptr;
 
-            std::pair<bool, bool> ret = base_class::ensure_at( refHead, *pNode, [&pItemFound](bool, node_type& item, node_type&){ pItemFound = &item; });
-            assert( pItemFound != nullptr );
+            std::pair<bool, bool> ret = base_class::update_at( refHead, *pNode,
 
-            if ( ret.first && ret.second )
+                [&pItemFound](bool, node_type& item, node_type&){ pItemFound = &item; },
+                bAllowInsert );
+
+            if ( ret.second )
                 pNode.release();
             return std::make_pair( pItemFound, ret.second );
         }
@@ -528,9 +571,49 @@ namespace cds { namespace container {
         {
             return base_class::find_at( refHead, key, cmp );
         }
+
+        template <typename K>
+        static node_type * alloc_node( const K& key )
+        {
+            return cxx_allocator().New( key );
+        }
+
+        template <typename K, typename V>
+        static node_type * alloc_node( const K& key, const V& val )
+        {
+            return cxx_allocator().New( key, val );
+        }
+
+        template <typename K, typename... Args>
+        static node_type * alloc_node( K&& key, Args&&... args )
+        {
+            return cxx_allocator().MoveNew( std::forward<K>( key ), std::forward<Args>( args )... );
+        }
+
+        static void free_node( node_type * pNode )
+        {
+            cxx_allocator().Delete( pNode );
+        }
+
+        head_type& head()
+        {
+            return base_class::m_pHead;
+        }
+
+        head_type const& head() const
+        {
+            return base_class::m_pHead;
+        }
+
+        iterator node_to_iterator( node_type * pNode )
+        {
+            if ( pNode )
+                return iterator( *pNode );
+            return end();
+        }
         //@endcond
     };
 
 }} // namespace cds::container
 
-#endif // #ifndef __CDS_CONTAINER_MICHAEL_KVLIST_NOGC_H
+#endif // #ifndef CDSLIB_CONTAINER_MICHAEL_KVLIST_NOGC_H