aaa7ee5b0351d7349bf30f6e9b9268d1ad7888f5
[libcds.git] / cds / container / michael_set_nogc.h
1 /*
2     This file is a part of libcds - Concurrent Data Structures library
3
4     (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
5
6     Source code repo: http://github.com/khizmax/libcds/
7     Download: http://sourceforge.net/projects/libcds/files/
8     
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions are met:
11
12     * Redistributions of source code must retain the above copyright notice, this
13       list of conditions and the following disclaimer.
14
15     * Redistributions in binary form must reproduce the above copyright notice,
16       this list of conditions and the following disclaimer in the documentation
17       and/or other materials provided with the distribution.
18
19     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.     
29 */
30
31 #ifndef CDSLIB_CONTAINER_MICHAEL_SET_NOGC_H
32 #define CDSLIB_CONTAINER_MICHAEL_SET_NOGC_H
33
34 #include <cds/container/details/michael_set_base.h>
35 #include <cds/gc/nogc.h>
36
37 namespace cds { namespace container {
38
39     /// Michael's hash set (template specialization for gc::nogc)
40     /** @ingroup cds_nonintrusive_set
41         \anchor cds_nonintrusive_MichaelHashSet_nogc
42
43         This specialization is so-called append-only when no item
44         reclamation may be performed. The class does not support deleting of list item.
45
46         See \ref cds_nonintrusive_MichaelHashSet_hp "MichaelHashSet" for description of template parameters.
47         The template parameter \p OrderedList should be any \p gc::nogc -derived ordered list, for example,
48         \ref cds_nonintrusive_MichaelList_nogc "append-only MichaelList".
49     */
50     template <
51         class OrderedList,
52 #ifdef CDS_DOXYGEN_INVOKED
53         class Traits = michael_set::traits
54 #else
55         class Traits
56 #endif
57     >
58     class MichaelHashSet< cds::gc::nogc, OrderedList, Traits >
59     {
60     public:
61         typedef cds::gc::nogc gc;         ///< Garbage collector
62         typedef OrderedList ordered_list; ///< type of ordered list to be used as a bucket implementation
63         typedef Traits      traits;       ///< Set traits
64
65         typedef typename ordered_list::value_type     value_type;     ///< type of value stored in the list
66         typedef typename ordered_list::key_comparator key_comparator; ///< key comparison functor
67         typedef typename ordered_list::stat           stat;           ///< Internal statistics
68
69         /// Hash functor for \ref value_type and all its derivatives that you use
70         typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash;
71         typedef typename traits::item_counter item_counter; ///< Item counter type
72         typedef typename traits::allocator    allocator;    ///< Bucket table allocator
73
74         // GC and OrderedList::gc must be the same
75         static_assert(std::is_same<gc, typename ordered_list::gc>::value, "GC and OrderedList::gc must be the same");
76
77         // atomicity::empty_item_counter is not allowed as a item counter
78         static_assert(!std::is_same<item_counter, atomicity::empty_item_counter>::value,
79             "cds::atomicity::empty_item_counter is not allowed as a item counter");
80
81     protected:
82         //@cond
83         typedef typename ordered_list::template select_stat_wrapper< typename ordered_list::stat > bucket_stat;
84
85         typedef typename ordered_list::template rebind_traits<
86             cds::opt::item_counter< cds::atomicity::empty_item_counter >
87             , cds::opt::stat< typename bucket_stat::wrapped_stat >
88         >::type internal_bucket_type_;
89
90         class internal_bucket_type: public internal_bucket_type_
91         {
92             typedef internal_bucket_type_ base_class;
93         public:
94             using base_class::base_class;
95             using base_class::node_type;
96             using base_class::alloc_node;
97             using base_class::insert_node;
98             using base_class::node_to_value;
99         };
100
101         /// Bucket table allocator
102         typedef typename allocator::template rebind< internal_bucket_type >::other bucket_table_allocator;
103
104         typedef typename internal_bucket_type::iterator        bucket_iterator;
105         typedef typename internal_bucket_type::const_iterator  bucket_const_iterator;
106         //@endcond
107
108     protected:
109         //@cond
110         const size_t    m_nHashBitmask;
111         item_counter    m_ItemCounter;      ///< Item counter
112         hash            m_HashFunctor;      ///< Hash functor
113         internal_bucket_type*   m_Buckets;  ///< bucket table
114         typename bucket_stat::stat  m_Stat; ///< Internal statistics
115         //@endcond
116
117     public:
118     ///@name Forward iterators
119     //@{
120         /// Forward iterator
121         /**
122             The forward iterator for Michael's set is based on \p OrderedList forward iterator and has some features:
123             - it has no post-increment operator
124             - it iterates items in unordered fashion
125
126             The iterator interface:
127             \code
128             class iterator {
129             public:
130                 // Default constructor
131                 iterator();
132
133                 // Copy construtor
134                 iterator( iterator const& src );
135
136                 // Dereference operator
137                 value_type * operator ->() const;
138
139                 // Dereference operator
140                 value_type& operator *() const;
141
142                 // Preincrement operator
143                 iterator& operator ++();
144
145                 // Assignment operator
146                 iterator& operator = (iterator const& src);
147
148                 // Equality operators
149                 bool operator ==(iterator const& i ) const;
150                 bool operator !=(iterator const& i ) const;
151             };
152             \endcode
153         */
154         typedef michael_set::details::iterator< internal_bucket_type, false >    iterator;
155
156         /// Const forward iterator
157         /**
158             For iterator's features and requirements see \ref iterator
159         */
160         typedef michael_set::details::iterator< internal_bucket_type, true >     const_iterator;
161
162         /// Returns a forward iterator addressing the first element in a set
163         /**
164             For empty set \code begin() == end() \endcode
165         */
166         iterator begin()
167         {
168             return iterator( m_Buckets[0].begin(), m_Buckets, m_Buckets + bucket_count() );
169         }
170
171         /// Returns an iterator that addresses the location succeeding the last element in a set
172         /**
173             Do not use the value returned by <tt>end</tt> function to access any item.
174             The returned value can be used only to control reaching the end of the set.
175             For empty set \code begin() == end() \endcode
176         */
177         iterator end()
178         {
179             return iterator( m_Buckets[bucket_count() - 1].end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
180         }
181
182         /// Returns a forward const iterator addressing the first element in a set
183         const_iterator begin() const
184         {
185             return get_const_begin();
186         }
187
188         /// Returns a forward const iterator addressing the first element in a set
189         const_iterator cbegin() const
190         {
191             return get_const_begin();
192         }
193
194         /// Returns an const iterator that addresses the location succeeding the last element in a set
195         const_iterator end() const
196         {
197             return get_const_end();
198         }
199
200         /// Returns an const iterator that addresses the location succeeding the last element in a set
201         const_iterator cend() const
202         {
203             return get_const_end();
204         }
205     //@}
206
207     public:
208         /// Initialize hash set
209         /**
210             The Michael's hash set is non-expandable container. You should point the average count of items \p nMaxItemCount
211             when you create an object.
212             \p nLoadFactor parameter defines average count of items per bucket and it should be small number between 1 and 10.
213             Remember, since the bucket implementation is an ordered list, searching in the bucket is linear [<tt>O(nLoadFactor)</tt>].
214
215             The ctor defines hash table size as rounding <tt>nMaxItemCount / nLoadFactor</tt> up to nearest power of two.
216         */
217         MichaelHashSet(
218             size_t nMaxItemCount,   ///< estimation of max item count in the hash set
219             size_t nLoadFactor      ///< load factor: estimation of max number of items in the bucket
220         ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor ))
221           , m_Buckets( bucket_table_allocator().allocate( bucket_count() ) )
222         {
223             for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it )
224                 construct_bucket<bucket_stat>( it );
225         }
226
227         /// Clears hash set and destroys it
228         ~MichaelHashSet()
229         {
230             clear();
231             for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it )
232                 it->~internal_bucket_type();
233             bucket_table_allocator().deallocate( m_Buckets, bucket_count() );
234         }
235
236         /// Inserts new node
237         /**
238             The function inserts \p val in the set if it does not contain
239             an item with key equal to \p val.
240
241             Return an iterator pointing to inserted item if success, otherwise \ref end()
242         */
243         template <typename Q>
244         iterator insert( const Q& val )
245         {
246             internal_bucket_type& refBucket = bucket( val );
247             bucket_iterator it = refBucket.insert( val );
248
249             if ( it != refBucket.end() ) {
250                 ++m_ItemCounter;
251                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
252             }
253
254             return end();
255         }
256
257         /// Inserts data of type \ref value_type constructed with <tt>std::forward<Args>(args)...</tt>
258         /**
259             Return an iterator pointing to inserted item if success \ref end() otherwise
260         */
261         template <typename... Args>
262         iterator emplace( Args&&... args )
263         {
264             typename internal_bucket_type::node_type * pNode = internal_bucket_type::alloc_node( std::forward<Args>( args )... );
265             internal_bucket_type& refBucket = bucket( internal_bucket_type::node_to_value( *pNode ));
266             bucket_iterator it = refBucket.insert_node( pNode );
267             if ( it != refBucket.end() ) {
268                 ++m_ItemCounter;
269                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
270             }
271
272             return end();
273         }
274
275         /// Updates the element
276         /**
277             The operation performs inserting or changing data with lock-free manner.
278
279             If the item \p val not found in the set, then \p val is inserted iff \p bAllowInsert is \p true.
280
281             Returns <tt> std::pair<iterator, bool> </tt> where \p first is an iterator pointing to
282             item found or inserted, or \p end() if \p bAllowInsert is \p false,
283
284             \p second is true if new item has been added or \p false if the item is already in the set.
285
286             @warning For \ref cds_intrusive_MichaelList_hp "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
287             \ref cds_intrusive_LazyList_hp "LazyList" provides exclusive access to inserted item and does not require any node-level
288             synchronization.
289         */
290         template <typename Q>
291         std::pair<iterator, bool> update( Q const& val, bool bAllowInsert = true )
292         {
293             internal_bucket_type& refBucket = bucket( val );
294             std::pair<bucket_iterator, bool> ret = refBucket.update( val, bAllowInsert );
295
296             if ( ret.first != refBucket.end() ) {
297                 if ( ret.second )
298                     ++m_ItemCounter;
299                 return std::make_pair( iterator( ret.first, &refBucket, m_Buckets + bucket_count() ), ret.second );
300             }
301             return std::make_pair( end(), ret.second );
302         }
303         //@cond
304         template <typename Q>
305         CDS_DEPRECATED("ensure() is deprecated, use update()")
306         std::pair<iterator, bool> ensure( Q const& val )
307         {
308             return update( val, true );
309         }
310         //@endcond
311
312         /// Checks whether the set contains \p key
313         /**
314             The function searches the item with key equal to \p key
315             and returns an iterator pointed to item found if the key is found,
316             or \ref end() otherwise.
317
318             Note the hash functor specified for class \p Traits template parameter
319             should accept a parameter of type \p Q that can be not the same as \p value_type.
320         */
321         template <typename Q>
322         iterator contains( Q const& key )
323         {
324             internal_bucket_type& refBucket = bucket( key );
325             bucket_iterator it = refBucket.contains( key );
326             if ( it != refBucket.end() )
327                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
328
329             return end();
330         }
331         //@cond
332         template <typename Q>
333         CDS_DEPRECATED("use contains()")
334         iterator find( Q const& key )
335         {
336             return contains( key );
337         }
338         //@endcond
339
340         /// Checks whether the set contains \p key using \p pred predicate for searching
341         /**
342             The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
343             \p Less functor has the interface like \p std::less.
344             \p Less must imply the same element order as the comparator used for building the set.
345         */
346         template <typename Q, typename Less>
347         iterator contains( Q const& key, Less pred )
348         {
349             internal_bucket_type& refBucket = bucket( key );
350             bucket_iterator it = refBucket.contains( key, pred );
351             if ( it != refBucket.end() )
352                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
353
354             return end();
355         }
356         //@cond
357         template <typename Q, typename Less>
358         CDS_DEPRECATED("use contains()")
359         iterator find_with( Q const& key, Less pred )
360         {
361             return contains( key, pred );
362         }
363         //@endcond
364
365         /// Clears the set (not atomic)
366         void clear()
367         {
368             for ( size_t i = 0; i < bucket_count(); ++i )
369                 m_Buckets[i].clear();
370             m_ItemCounter.reset();
371         }
372
373         /// Checks if the set is empty
374         /**
375             The emptiness is checked by the item counting: if item count is zero then the set is empty.
376             Thus, the correct item counting feature is an important part of Michael's set implementation.
377         */
378         bool empty() const
379         {
380             return size() == 0;
381         }
382
383         /// Returns item count in the set
384         size_t size() const
385         {
386             return m_ItemCounter;
387         }
388
389         /// Returns const reference to internal statistics
390         stat const& statistics() const
391         {
392             return m_Stat;
393         }
394
395         /// Returns the size of hash table
396         /**
397             Since \p %MichaelHashSet cannot dynamically extend the hash table size,
398             the value returned is an constant depending on object initialization parameters;
399             see MichaelHashSet::MichaelHashSet for explanation.
400         */
401         size_t bucket_count() const
402         {
403             return m_nHashBitmask + 1;
404         }
405
406     protected:
407         //@cond
408         /// Calculates hash value of \p key
409         template <typename Q>
410         size_t hash_value( const Q& key ) const
411         {
412             return m_HashFunctor( key ) & m_nHashBitmask;
413         }
414
415         /// Returns the bucket (ordered list) for \p key
416         template <typename Q>
417         internal_bucket_type& bucket( const Q& key )
418         {
419             return m_Buckets[hash_value( key )];
420         }
421         //@endcond
422
423     private:
424         //@cond
425         template <typename Stat>
426         typename std::enable_if< Stat::empty >::type construct_bucket( internal_bucket_type* bucket )
427         {
428             new (bucket) internal_bucket_type;
429         }
430
431         template <typename Stat>
432         typename std::enable_if< !Stat::empty >::type construct_bucket( internal_bucket_type* bucket )
433         {
434             new (bucket) internal_bucket_type( m_Stat );
435         }
436
437         const_iterator get_const_begin() const
438         {
439             return const_iterator( const_cast<internal_bucket_type const&>(m_Buckets[0]).begin(), m_Buckets, m_Buckets + bucket_count() );
440         }
441         const_iterator get_const_end() const
442         {
443             return const_iterator( const_cast<internal_bucket_type const&>(m_Buckets[bucket_count() - 1]).end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
444         }
445         //@endcond
446     };
447
448 }} // cds::container
449
450 #endif // ifndef CDSLIB_CONTAINER_MICHAEL_SET_NOGC_H