Adds sequential barrier driver
[libcds.git] / cds / container / lazy_kvlist_nogc.h
index 73f572d2ceb9ee7156ce9041a4ced5f890d232e3..2bb6be3107feeca508b5c8b096f34e25df0d010d 100644 (file)
@@ -1,54 +1,57 @@
-//$$CDS-header$$
+/*
+    This file is a part of libcds - Concurrent Data Structures library
 
-#ifndef __CDS_CONTAINER_LAZY_KVLIST_NOGC_H
-#define __CDS_CONTAINER_LAZY_KVLIST_NOGC_H
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
 
-#include <cds/container/lazy_list_base.h>
-#include <cds/intrusive/lazy_list_nogc.h>
-#include <cds/container/details/make_lazy_kvlist.h>
-#include <cds/details/functor_wrapper.h>
-#include <cds/details/std/memory.h>
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
 
-namespace cds { namespace container {
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
 
-    //@cond
-    namespace details {
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
 
-        template <typename K, typename T, class Traits>
-        struct make_lazy_kvlist_nogc: public make_lazy_kvlist<gc::nogc, K, T, Traits>
-        {
-            typedef make_lazy_kvlist<cds::gc::nogc, K, T, Traits>  base_maker;
-            typedef typename base_maker::node_type node_type;
+    * 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.
 
-            struct type_traits: public base_maker::type_traits
-            {
-                typedef typename base_maker::node_deallocator    disposer;
-            };
+    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.
+*/
 
-            typedef intrusive::LazyList<cds::gc::nogc, node_type, type_traits>  type;
-        };
+#ifndef CDSLIB_CONTAINER_LAZY_KVLIST_NOGC_H
+#define CDSLIB_CONTAINER_LAZY_KVLIST_NOGC_H
 
-    }   // namespace details
-    //@endcond
+#include <memory>
+#include <cds/container/details/lazy_list_base.h>
+#include <cds/intrusive/lazy_list_nogc.h>
+#include <cds/container/details/make_lazy_kvlist.h>
+
+namespace cds { namespace container {
 
     /// Lazy ordered list (key-value pair, template specialization for gc::nogc)
     /** @ingroup cds_nonintrusive_list
+        @anchor cds_nonintrusive_LazyKVList_nogc
 
-        This specialization is intended for so-called persistent usage when no item
-        reclamation may be performed. The class does not support deleting of list item.
+        This specialization is append-only list when no item
+        reclamation may be performed. The class does not support deleting of list's item.
 
-        Usually, ordered single-linked list is used as a building block for the hash table implementation.
-        The complexity of searching is <tt>O(N)</tt>.
-
-        See \ref cds_nonintrusive_LazyList_gc "LazyList" for description of template parameters.
-
-        The interface of the specialization is a little different.
+        See @ref cds_nonintrusive_LazyList_gc "cds::container::LazyList<cds::gc::nogc, T, Traits>"
     */
     template <
         typename Key,
         typename Value,
 #ifdef CDS_DOXYGEN_INVOKED
-        typename Traits = lazy_list::type_traits
+        typename Traits = lazy_list::traits
 #else
         typename Traits
 #endif
@@ -57,74 +60,57 @@ namespace cds { namespace container {
 #ifdef CDS_DOXYGEN_INVOKED
         protected intrusive::LazyList< gc::nogc, implementation_defined, Traits >
 #else
-        protected details::make_lazy_kvlist_nogc< Key, Value, Traits >::type
+        protected details::make_lazy_kvlist< cds::gc::nogc, Key, Value, Traits >::type
 #endif
     {
         //@cond
-        typedef details::make_lazy_kvlist_nogc< Key, Value, Traits > options;
-        typedef typename options::type  base_class;
+        typedef details::make_lazy_kvlist< cds::gc::nogc, Key, Value, Traits > maker;
+        typedef typename maker::type  base_class;
         //@endcond
 
     public:
+        typedef Traits traits;    ///< List traits
+        typedef cds::gc::nogc gc; ///< Garbage collector
 #ifdef CDS_DOXYGEN_INVOKED
         typedef Key                                 key_type        ;   ///< Key type
         typedef Value                               mapped_type     ;   ///< Type of value stored in the list
         typedef std::pair<key_type const, mapped_type> value_type   ;   ///< key/value pair stored in the list
 #else
-        typedef typename options::key_type          key_type;
-        typedef typename options::value_type        mapped_type;
-        typedef typename options::pair_type         value_type;
+        typedef typename maker::key_type    key_type;
+        typedef typename maker::mapped_type mapped_type;
+        typedef typename maker::value_type  value_type;
 #endif
-        typedef typename base_class::gc             gc              ;   ///< Garbage collector used
-        typedef typename base_class::back_off       back_off        ;   ///< Back-off strategy used
-        typedef typename options::allocator_type    allocator_type  ;   ///< Allocator type used for allocate/deallocate the nodes
-        typedef typename base_class::item_counter   item_counter    ;   ///< Item counting policy used
-        typedef typename options::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::back_off     back_off;       ///< Back-off strategy used
+        typedef typename maker::allocator_type    allocator_type; ///< Allocator type used for allocate/deallocate the nodes
+        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
+        static CDS_CONSTEXPR bool const c_bSort = base_class::c_bSort; ///< List type: ordered (\p true) or unordered (\p false)
 
-    protected:
         //@cond
-        typedef typename base_class::value_type     node_type;
-        typedef typename options::cxx_allocator     cxx_allocator;
-        typedef typename options::node_deallocator  node_deallocator;
-        typedef typename options::type_traits::compare  intrusive_key_comparator;
+        // Rebind traits (split-list support)
+        template <typename... Options>
+        struct rebind_traits {
+            typedef LazyKVList<
+                gc
+                , key_type, mapped_type
+                , typename cds::opt::make_options< traits, Options...>::type
+            > type;
+        };
 
-        typedef typename base_class::node_type      head_type;
+        // Stat selector
+        template <typename Stat>
+        using select_stat_wrapper = typename base_class::template select_stat_wrapper< Stat >;
         //@endcond
 
-    private:
+    protected:
         //@cond
-#   ifndef CDS_CXX11_LAMBDA_SUPPORT
-        struct ensure_functor
-        {
-            node_type * m_pItemFound;
-
-            ensure_functor()
-                : m_pItemFound( null_ptr<node_type *>() )
-            {}
-
-            void operator ()(bool, node_type& item, node_type& )
-            {
-                m_pItemFound = &item;
-            }
-        };
-
-        template <typename Func>
-        class find_functor: protected cds::details::functor_wrapper<Func>
-        {
-            typedef cds::details::functor_wrapper<Func> base_class;
-        public:
-            find_functor( Func f )
-                : base_class(f)
-            {}
-
-            template <typename Q>
-            void operator ()( node_type& node, Q&  )
-            {
-                base_class::get()( node.m_Data );
-            }
-        };
-#   endif   // ifndef CDS_CXX11_LAMBDA_SUPPORT
+        typedef typename base_class::value_type     node_type;
+        typedef typename maker::cxx_allocator       cxx_allocator;
+        typedef typename maker::node_deallocator    node_deallocator;
+        typedef typename base_class::key_comparator intrusive_key_comparator;
+        typedef typename base_class::node_type      head_type;
         //@endcond
 
     protected:
@@ -141,13 +127,11 @@ namespace cds { namespace container {
             return cxx_allocator().New( key, val );
         }
 
-#ifdef CDS_EMPLACE_SUPPORT
         template <typename... Args>
         static node_type * alloc_node( Args&&... args )
         {
             return cxx_allocator().MoveNew( std::forward<Args>(args)... );
         }
-#endif
 
         static void free_node( node_type * pNode )
         {
@@ -223,21 +207,21 @@ namespace cds { namespace container {
             key_type const& key() const
             {
                 typename iterator_base::value_ptr p = iterator_base::operator ->();
-                assert( p != null_ptr<typename iterator_base::value_ptr>() );
+                assert( p != nullptr );
                 return p->m_Data.first;
             }
 
             value_ref val() const
             {
                 typename iterator_base::value_ptr p = iterator_base::operator ->();
-                assert( p != null_ptr<typename iterator_base::value_ptr>() );
+                assert( p != nullptr );
                 return p->m_Data.second;
             }
 
             pair_ptr operator ->() const
             {
                 typename iterator_base::value_ptr p = iterator_base::operator ->();
-                return p ? &(p->m_Data) : null_ptr<pair_ptr>();
+                return p ? &(p->m_Data) : nullptr;
             }
 
             pair_ref operator *() const
@@ -273,17 +257,21 @@ namespace cds { namespace container {
         //@endcond
 
     public:
+    ///@name Forward iterators
+    //@{
         /// Forward iterator
         /**
-            The forward iterator for lazy 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 lazy 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()
         */
         typedef iterator_type<false>    iterator;
 
@@ -299,7 +287,7 @@ namespace cds { namespace container {
         */
         iterator begin()
         {
-            iterator it( head() );
+            iterator it( head());
             ++it ;  // skip dummy head
             return it;
         }
@@ -307,7 +295,7 @@ namespace cds { namespace container {
         /// Returns an iterator that addresses the location succeeding the last element in a list
         /**
             Do not use the value returned by <tt>end</tt> function to access any item.
-            Internally, <tt>end</tt> returning value equals to <tt>NULL</tt>.
+            Internally, <tt>end</tt> returning value equals to nullptr.
 
             The returned value can be used only to control reaching the end of the list.
             For empty list \code begin() == end() \endcode
@@ -318,32 +306,31 @@ namespace cds { namespace container {
         }
 
         /// Returns a forward const iterator addressing the first element in a list
-        //@{
         const_iterator begin() const
         {
-            const_iterator it( head() );
+            const_iterator it( head());
             ++it ;  // skip dummy head
             return it;
         }
-        const_iterator cbegin()
+        /// Returns a forward const iterator addressing the first element in a list
+        const_iterator cbegin() const
         {
-            const_iterator it( head() );
+            const_iterator it( head());
             ++it ;  // skip dummy head
             return it;
         }
-        //@}
 
         /// Returns an const iterator that addresses the location succeeding the last element in a list
-        //@{
         const_iterator end() const
         {
             return const_iterator( tail());
         }
-        const_iterator cend()
+        /// Returns an const iterator that addresses the location succeeding the last element in a list
+        const_iterator cend() const
         {
             return const_iterator( tail());
         }
-        //@}
+    //@}
 
     protected:
         //@cond
@@ -357,16 +344,17 @@ namespace cds { namespace container {
 
     public:
         /// Default constructor
-        /**
-            Initialize empty list
-        */
         LazyKVList()
         {}
 
-        /// List desctructor
-        /**
-            Clears the list
-        */
+        //@cond
+        template <typename Stat, typename = std::enable_if<std::is_same<stat, lazy_list::wrapped_stat<Stat>>::value >>
+        explicit LazyKVList( Stat& st )
+            : base_class( st )
+        {}
+        //@endcond
+
+        /// Desctructor clears the list
         ~LazyKVList()
         {
             clear();
@@ -422,10 +410,7 @@ namespace cds { namespace container {
 
             The argument \p item of user-defined functor \p func is the reference
             to the list's item inserted. <tt>item.second</tt> is a reference to item's value that may be changed.
-            User-defined functor \p func should guarantee that during changing item's value no any other changes
-            could be made on this list's item by concurrent threads.
-            The user-defined functor can be passed by reference using <tt>boost::ref</tt>
-            and it is called only if the inserting is successful.
+            The user-defined functor is called only if the inserting is successful.
 
             The key_type should be constructible from value of type \p K.
 
@@ -440,66 +425,107 @@ 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,
+
+            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( const K& 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( const K& key )
+        {
+            return update( key, true );
+        }
+        //@endcond
 
-#   ifdef CDS_EMPLACE_SUPPORT
         /// Inserts data of type \ref mapped_type constructed with <tt>std::forward<Args>(args)...</tt>
         /**
             Returns an iterator pointed to inserted value, or \p end() if inserting is failed
-
-            This function is available only for compiler that supports
-            variadic template and move semantics
         */
         template <typename... Args>
         iterator emplace( Args&&... args )
         {
             return node_to_iterator( emplace_at( head(), std::forward<Args>(args)... ));
         }
-#   endif
 
-        /// Find the key \p key
-        /** \anchor cds_nonintrusive_LazyKVList_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
         */
         template <typename Q>
+        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 node_to_iterator( find_at( head(), key, intrusive_key_comparator() ) );
+            return contains( key );
         }
+        //@endcond
 
-        /// Finds the key \p val using \p pred predicate for searching
+        /// Checks whether the map contains \p key using \p pred predicate for searching (ordered list version)
         /**
-            The function is an analog of \ref cds_nonintrusive_LazyKVList_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.
+            \p Less must imply the same element order as the comparator used for building the list.
+        */
+        template <typename Q, typename Less, bool Sort = c_bSort>
+        typename std::enable_if<Sort, iterator>::type 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, bool Sort = c_bSort>
+        CDS_DEPRECATED("deprecated, use contains()")
+        typename std::enable_if<Sort, iterator>::type find_with( Q const& key, Less pred )
+        {
+            return contains( key, pred );
+        }
+        //@endcond
+
+        /// Finds the key \p val using \p equal predicate for searching (unordered list version)
+        /**
+            The function is an analog of <tt>contains( key )</tt> but \p equal is used for key comparing.
+            \p Equal functor has the interface like \p std::equal_to.
         */
-        template <typename Q, typename Less>
-        iterator find_with( Q const& key, Less pred )
+        template <typename Q, typename Equal, bool Sort = c_bSort>
+        typename std::enable_if<!Sort, iterator>::type contains( Q const& key, Equal equal )
         {
-            return node_to_iterator( find_at( head(), key, typename options::template less_wrapper<Less>::type() ) );
+            CDS_UNUSED( equal );
+            return node_to_iterator( find_at( head(), key, typename maker::template equal_to_wrapper<Equal>::type()));
         }
+        //@cond
+        template <typename Q, typename Equal, bool Sort = c_bSort>
+        CDS_DEPRECATED("deprecated, use contains()")
+        typename std::enable_if<!Sort, iterator>::type find_with( Q const& key, Equal equal )
+        {
+            return contains( key, equal );
+        }
+        //@endcond
 
         /// Check if the list is empty
         bool empty() const
@@ -512,7 +538,7 @@ namespace cds { namespace container {
             The value returned depends on opt::item_counter option. For atomicity::empty_item_counter,
             this function always returns 0.
 
-            <b>Warning</b>: even if you use real item counter and it returns 0, this fact is not mean that the list
+            @note Even if you use real item counter and it returns 0, this fact is not mean that the list
             is empty. To check list emptyness use \ref empty() method.
         */
         size_t size() const
@@ -520,6 +546,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
         /**
             Post-condition: the list is empty
@@ -533,12 +565,12 @@ namespace cds { namespace container {
         //@cond
         node_type * insert_node_at( head_type& refHead, node_type * pNode )
         {
-            assert( pNode != null_ptr<node_type *>() );
+            assert( pNode != nullptr );
             scoped_node_ptr p( pNode );
             if ( base_class::insert_at( &refHead, *p ))
                 return p.release();
 
-            return null_ptr<node_type *>();
+            return nullptr;
         }
 
         template <typename K>
@@ -554,46 +586,40 @@ 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 ));
 
             if ( base_class::insert_at( &refHead, *pNode )) {
-                cds::unref(f)( pNode->m_Data );
+                f( pNode->m_Data );
                 return pNode.release();
             }
 
-            return null_ptr<node_type *>();
+            return nullptr;
         }
 
 
         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 = null_ptr<node_type *>();
-
-#       ifdef CDS_CXX11_LAMBDA_SUPPORT
-            std::pair<bool, bool> ret = base_class::ensure_at( &refHead, *pNode, [&pItemFound](bool, node_type& item, node_type&){ pItemFound = &item; } );
-#       else
-            ensure_functor func;
-            std::pair<bool, bool> ret = base_class::ensure_at( &refHead, *pNode, boost::ref(func) );
-            pItemFound = func.m_pItemFound;
-#       endif
-            if ( ret.first && ret.second )
+            node_type * pItemFound = nullptr;
+
+            std::pair<bool, bool> ret = base_class::update_at( &refHead, *pNode,
+                [&pItemFound](bool, node_type& item, node_type&){ pItemFound = &item; },
+                bAllowInsert );
+
+            if ( ret.second )
                 pNode.release();
 
-            assert( pItemFound != null_ptr<node_type *>() );
             return std::make_pair( pItemFound, ret.second );
         }
 
-#   ifdef CDS_EMPLACE_SUPPORT
         template <typename... Args>
         node_type * emplace_at( head_type& refHead, Args&&... args )
         {
             return insert_node_at( refHead, alloc_node( std::forward<Args>(args)... ));
         }
-#endif
 
         template <typename K, typename Compare>
         node_type * find_at( head_type& refHead, const K& key, Compare cmp )
@@ -605,12 +631,7 @@ namespace cds { namespace container {
         template <typename K, typenam Compare, typename Func>
         bool find_at( head_type& refHead, K& key, Compare cmp, Func f )
         {
-#       ifdef CDS_CXX11_LAMBDA_SUPPORT
-            return base_class::find_at( &refHead, key, cmp, [&f]( node_type& node, K const& ){ cds::unref(f)( node.m_Data ); });
-#       else
-            find_functor<Func>  wrapper( f );
-            return base_class::find_at( &refHead, key, cmp, cds::ref(wrapper) );
-#       endif
+            return base_class::find_at( &refHead, key, cmp, [&f]( node_type& node, K const& ){ f( node.m_Data ); });
         }
         */
         //@endcond
@@ -618,4 +639,4 @@ namespace cds { namespace container {
 
 }} // namespace cds::container
 
-#endif // #ifndef __CDS_CONTAINER_LAZY_KVLIST_NOGC_H
+#endif // #ifndef CDSLIB_CONTAINER_LAZY_KVLIST_NOGC_H