efb6901b38174d9bf9cdf453969013b95cbb2e40
[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-2017
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 #ifdef CDS_DOXYGEN_INVOKED
68         typedef typename ordered_list::stat           stat;           ///< Internal statistics
69 #endif
70
71         /// Hash functor for \ref value_type and all its derivatives that you use
72         typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash;
73         typedef typename traits::item_counter item_counter; ///< Item counter type
74         typedef typename traits::allocator    allocator;    ///< Bucket table allocator
75
76         // GC and OrderedList::gc must be the same
77         static_assert(std::is_same<gc, typename ordered_list::gc>::value, "GC and OrderedList::gc must be the same");
78
79     protected:
80         //@cond
81         typedef typename ordered_list::template select_stat_wrapper< typename ordered_list::stat > bucket_stat;
82
83         typedef typename ordered_list::template rebind_traits<
84             cds::opt::item_counter< cds::atomicity::empty_item_counter >
85             , cds::opt::stat< typename bucket_stat::wrapped_stat >
86         >::type internal_bucket_type_;
87
88         class internal_bucket_type: public internal_bucket_type_
89         {
90             typedef internal_bucket_type_ base_class;
91         public:
92             using base_class::base_class;
93             using typename base_class::node_type;
94             using base_class::alloc_node;
95             using base_class::insert_node;
96             using base_class::node_to_value;
97         };
98
99         /// Bucket table allocator
100         typedef typename allocator::template rebind< internal_bucket_type >::other bucket_table_allocator;
101
102         typedef typename internal_bucket_type::iterator        bucket_iterator;
103         typedef typename internal_bucket_type::const_iterator  bucket_const_iterator;
104         //@endcond
105
106     public:
107         //@cond
108         typedef typename bucket_stat::stat stat;
109         //@endcond
110
111     protected:
112         //@cond
113         const size_t    m_nHashBitmask;
114         hash            m_HashFunctor;      ///< Hash functor
115         internal_bucket_type*   m_Buckets;  ///< bucket table
116         item_counter    m_ItemCounter;      ///< Item counter
117         stat            m_Stat;             ///< Internal statistics
118         //@endcond
119
120     public:
121     ///@name Forward iterators
122     //@{
123         /// Forward iterator
124         /**
125             The forward iterator for Michael's set is based on \p OrderedList forward iterator and has some features:
126             - it has no post-increment operator
127             - it iterates items in unordered fashion
128
129             The iterator interface:
130             \code
131             class iterator {
132             public:
133                 // Default constructor
134                 iterator();
135
136                 // Copy construtor
137                 iterator( iterator const& src );
138
139                 // Dereference operator
140                 value_type * operator ->() const;
141
142                 // Dereference operator
143                 value_type& operator *() const;
144
145                 // Preincrement operator
146                 iterator& operator ++();
147
148                 // Assignment operator
149                 iterator& operator = (iterator const& src);
150
151                 // Equality operators
152                 bool operator ==(iterator const& i ) const;
153                 bool operator !=(iterator const& i ) const;
154             };
155             \endcode
156         */
157         typedef michael_set::details::iterator< internal_bucket_type, false >    iterator;
158
159         /// Const forward iterator
160         /**
161             For iterator's features and requirements see \ref iterator
162         */
163         typedef michael_set::details::iterator< internal_bucket_type, true >     const_iterator;
164
165         /// Returns a forward iterator addressing the first element in a set
166         /**
167             For empty set \code begin() == end() \endcode
168         */
169         iterator begin()
170         {
171             return iterator( m_Buckets[0].begin(), m_Buckets, m_Buckets + bucket_count());
172         }
173
174         /// Returns an iterator that addresses the location succeeding the last element in a set
175         /**
176             Do not use the value returned by <tt>end</tt> function to access any item.
177             The returned value can be used only to control reaching the end of the set.
178             For empty set \code begin() == end() \endcode
179         */
180         iterator end()
181         {
182             return iterator( m_Buckets[bucket_count() - 1].end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count());
183         }
184
185         /// Returns a forward const iterator addressing the first element in a set
186         const_iterator begin() const
187         {
188             return get_const_begin();
189         }
190
191         /// Returns a forward const iterator addressing the first element in a set
192         const_iterator cbegin() const
193         {
194             return get_const_begin();
195         }
196
197         /// Returns an const iterator that addresses the location succeeding the last element in a set
198         const_iterator end() const
199         {
200             return get_const_end();
201         }
202
203         /// Returns an const iterator that addresses the location succeeding the last element in a set
204         const_iterator cend() const
205         {
206             return get_const_end();
207         }
208     //@}
209
210     public:
211         /// Initialize hash set
212         /**
213             The Michael's hash set is non-expandable container. You should point the average count of items \p nMaxItemCount
214             when you create an object.
215             \p nLoadFactor parameter defines average count of items per bucket and it should be small number between 1 and 10.
216             Remember, since the bucket implementation is an ordered list, searching in the bucket is linear [<tt>O(nLoadFactor)</tt>].
217
218             The ctor defines hash table size as rounding <tt>nMaxItemCount / nLoadFactor</tt> up to nearest power of two.
219         */
220         MichaelHashSet(
221             size_t nMaxItemCount,   ///< estimation of max item count in the hash set
222             size_t nLoadFactor      ///< load factor: estimation of max number of items in the bucket
223         ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor ))
224           , m_Buckets( bucket_table_allocator().allocate( bucket_count()))
225         {
226             for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it )
227                 construct_bucket<bucket_stat>( it );
228         }
229
230         /// Clears hash set and destroys it
231         ~MichaelHashSet()
232         {
233             clear();
234             for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it )
235                 it->~internal_bucket_type();
236             bucket_table_allocator().deallocate( m_Buckets, bucket_count());
237         }
238
239         /// Inserts new node
240         /**
241             The function inserts \p val in the set if it does not contain
242             an item with key equal to \p val.
243
244             Return an iterator pointing to inserted item if success, otherwise \ref end()
245         */
246         template <typename Q>
247         iterator insert( const Q& val )
248         {
249             internal_bucket_type& refBucket = bucket( val );
250             bucket_iterator it = refBucket.insert( val );
251
252             if ( it != refBucket.end()) {
253                 ++m_ItemCounter;
254                 return iterator( it, &refBucket, m_Buckets + bucket_count());
255             }
256
257             return end();
258         }
259
260         /// Inserts data of type \ref value_type constructed with <tt>std::forward<Args>(args)...</tt>
261         /**
262             Return an iterator pointing to inserted item if success \ref end() otherwise
263         */
264         template <typename... Args>
265         iterator emplace( Args&&... args )
266         {
267             typename internal_bucket_type::node_type * pNode = internal_bucket_type::alloc_node( std::forward<Args>( args )... );
268             internal_bucket_type& refBucket = bucket( internal_bucket_type::node_to_value( *pNode ));
269             bucket_iterator it = refBucket.insert_node( pNode );
270             if ( it != refBucket.end()) {
271                 ++m_ItemCounter;
272                 return iterator( it, &refBucket, m_Buckets + bucket_count());
273             }
274
275             return end();
276         }
277
278         /// Updates the element
279         /**
280             The operation performs inserting or changing data with lock-free manner.
281
282             If the item \p val not found in the set, then \p val is inserted iff \p bAllowInsert is \p true.
283
284             Returns <tt> std::pair<iterator, bool> </tt> where \p first is an iterator pointing to
285             item found or inserted, or \p end() if \p bAllowInsert is \p false,
286
287             \p second is true if new item has been added or \p false if the item is already in the set.
288
289             @warning For \ref cds_intrusive_MichaelList_hp "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
290             \ref cds_intrusive_LazyList_hp "LazyList" provides exclusive access to inserted item and does not require any node-level
291             synchronization.
292         */
293         template <typename Q>
294         std::pair<iterator, bool> update( Q const& val, bool bAllowInsert = true )
295         {
296             internal_bucket_type& refBucket = bucket( val );
297             std::pair<bucket_iterator, bool> ret = refBucket.update( val, bAllowInsert );
298
299             if ( ret.first != refBucket.end()) {
300                 if ( ret.second )
301                     ++m_ItemCounter;
302                 return std::make_pair( iterator( ret.first, &refBucket, m_Buckets + bucket_count()), ret.second );
303             }
304             return std::make_pair( end(), ret.second );
305         }
306         //@cond
307         template <typename Q>
308         CDS_DEPRECATED("ensure() is deprecated, use update()")
309         std::pair<iterator, bool> ensure( Q const& val )
310         {
311             return update( val, true );
312         }
313         //@endcond
314
315         /// Checks whether the set contains \p key
316         /**
317             The function searches the item with key equal to \p key
318             and returns an iterator pointed to item found if the key is found,
319             or \ref end() otherwise.
320
321             Note the hash functor specified for class \p Traits template parameter
322             should accept a parameter of type \p Q that can be not the same as \p value_type.
323         */
324         template <typename Q>
325         iterator contains( Q const& key )
326         {
327             internal_bucket_type& refBucket = bucket( key );
328             bucket_iterator it = refBucket.contains( key );
329             if ( it != refBucket.end())
330                 return iterator( it, &refBucket, m_Buckets + bucket_count());
331
332             return end();
333         }
334         //@cond
335         template <typename Q>
336         CDS_DEPRECATED("use contains()")
337         iterator find( Q const& key )
338         {
339             return contains( key );
340         }
341         //@endcond
342
343         /// Checks whether the set contains \p key using \p pred predicate for searching
344         /**
345             The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
346             \p Less functor has the interface like \p std::less.
347             \p Less must imply the same element order as the comparator used for building the set.
348         */
349         template <typename Q, typename Less>
350         iterator contains( Q const& key, Less pred )
351         {
352             internal_bucket_type& refBucket = bucket( key );
353             bucket_iterator it = refBucket.contains( key, pred );
354             if ( it != refBucket.end())
355                 return iterator( it, &refBucket, m_Buckets + bucket_count());
356
357             return end();
358         }
359         //@cond
360         template <typename Q, typename Less>
361         CDS_DEPRECATED("use contains()")
362         iterator find_with( Q const& key, Less pred )
363         {
364             return contains( key, pred );
365         }
366         //@endcond
367
368         /// Clears the set (not atomic)
369         void clear()
370         {
371             for ( size_t i = 0; i < bucket_count(); ++i )
372                 m_Buckets[i].clear();
373             m_ItemCounter.reset();
374         }
375
376         /// Checks if the set is empty
377         /**
378             @warning If you use \p atomicity::empty_item_counter in \p traits::item_counter,
379             the function always returns \p true.
380         */
381         bool empty() const
382         {
383             return size() == 0;
384         }
385
386         /// Returns item count in the set
387         /**
388             @warning If you use \p atomicity::empty_item_counter in \p traits::item_counter,
389             the function always returns 0.
390         */
391         size_t size() const
392         {
393             return m_ItemCounter;
394         }
395
396         /// Returns const reference to internal statistics
397         stat const& statistics() const
398         {
399             return m_Stat;
400         }
401
402         /// Returns the size of hash table
403         /**
404             Since \p %MichaelHashSet cannot dynamically extend the hash table size,
405             the value returned is an constant depending on object initialization parameters;
406             see MichaelHashSet::MichaelHashSet for explanation.
407         */
408         size_t bucket_count() const
409         {
410             return m_nHashBitmask + 1;
411         }
412
413     protected:
414         //@cond
415         /// Calculates hash value of \p key
416         template <typename Q>
417         size_t hash_value( const Q& key ) const
418         {
419             return m_HashFunctor( key ) & m_nHashBitmask;
420         }
421
422         /// Returns the bucket (ordered list) for \p key
423         template <typename Q>
424         internal_bucket_type& bucket( const Q& key )
425         {
426             return m_Buckets[hash_value( key )];
427         }
428         //@endcond
429
430     private:
431         //@cond
432         template <typename Stat>
433         typename std::enable_if< Stat::empty >::type construct_bucket( internal_bucket_type* bucket )
434         {
435             new (bucket) internal_bucket_type;
436         }
437
438         template <typename Stat>
439         typename std::enable_if< !Stat::empty >::type construct_bucket( internal_bucket_type* bucket )
440         {
441             new (bucket) internal_bucket_type( m_Stat );
442         }
443
444         const_iterator get_const_begin() const
445         {
446             return const_iterator( const_cast<internal_bucket_type const&>(m_Buckets[0]).begin(), m_Buckets, m_Buckets + bucket_count());
447         }
448         const_iterator get_const_end() const
449         {
450             return const_iterator( const_cast<internal_bucket_type const&>(m_Buckets[bucket_count() - 1]).end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count());
451         }
452         //@endcond
453     };
454
455 }} // cds::container
456
457 #endif // ifndef CDSLIB_CONTAINER_MICHAEL_SET_NOGC_H