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