00ab867438ba46beeb16ae9f63b116048243508f
[libcds.git] / cds / intrusive / striped_set.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_INTRUSIVE_STRIPED_SET_H
32 #define CDSLIB_INTRUSIVE_STRIPED_SET_H
33
34 #include <cds/intrusive/details/base.h>
35 #include <cds/intrusive/striped_set/adapter.h>
36 #include <cds/intrusive/striped_set/striping_policy.h>
37
38 namespace cds { namespace intrusive {
39     /// StripedSet related definitions
40     namespace striped_set {
41
42         /** @defgroup cds_striped_resizing_policy Resizing policy for striped/refinable set/map
43
44             Resizing policy for \p intrusive::StripedSet, \p container::StripedSet and \p container::StripedMap.
45         */
46
47     }   // namespace striped_set
48
49     /// Striped hash set
50     /** @ingroup cds_intrusive_map
51
52         Source
53             - [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming"
54
55         Lock striping is very simple technique.
56         The set consists of the bucket table and the array of locks.
57         Initially, the capacity of lock array and bucket table is the same.
58         When set is resized, bucket table capacity will be doubled but lock array will not.
59         The lock \p i protects each bucket \p j, where <tt> j = i mod L </tt>,
60         where \p L - the size of lock array.
61
62         Template arguments:
63             - \p Container - the container class that is used as bucket table entry. The \p Container class should support
64                 an uniform interface described below.
65             - \p Options - options
66
67         The \p %StripedSet class does not exactly dictate the type of container that should be used as a \p Container bucket.
68         Instead, the class supports different intrusive container type for the bucket, for exampe,
69         \p boost::intrusive::list, \p boost::intrusive::set and others.
70
71         Remember that \p %StripedSet class algorithm ensures sequential blocking access to its bucket through the mutex type you specify
72         among \p Options template arguments.
73
74         The \p Options are:
75         - \p opt::mutex_policy - concurrent access policy.
76             Available policies: \p striped_set::striping, \p striped_set::refinable.
77             Default is \p %striped_set::striping.
78         - \p cds::opt::hash - hash functor. Default option value see <tt>opt::v::hash_selector <opt::none></tt>
79             which selects default hash functor for your compiler.
80         - \p cds::opt::compare - key comparison functor. No default functor is provided.
81             If the option is not specified, the \p opt::less is used.
82         - \p cds::opt::less - specifies binary predicate used for key comparison. Default is \p std::less<T>.
83         - \p cds::opt::item_counter - item counter type. Default is \p atomicity::item_counter since some operation on the counter is performed
84             without locks. Note that item counting is an essential part of the set algorithm, so dummy counter like \p atomicity::empty_item_counter
85             is not suitable.
86         - \p cds::opt::allocator - the allocator type using for memory allocation of bucket table and lock array. Default is \ref CDS_DEFAULT_ALLOCATOR.
87         - \p cds::opt::resizing_policy - the resizing policy - a functor that decides when to resize the hash set.
88             Default option value depends on bucket container type:
89                 for sequential containers like \p boost::intrusive::list the resizing policy is <tt>cds::container::striped_set::load_factor_resizing<4> </tt>;
90                 for other type of containers like \p boost::intrusive::set the resizing policy is cds::container::striped_set::no_resizing.
91             See \ref cds_striped_resizing_policy "available resizing policy".
92             Note that the choose of resizing policy depends of \p Container type:
93             for sequential containers like \p boost::intrusive::list the right policy can significantly improve performance.
94             For other, non-sequential types of \p Container (like a \p boost::intrusive::set) the resizing policy is not so important.
95         - \p cds::opt::buffer - an initialized buffer type used only for \p boost::intrusive::unordered_set.
96             Default is <tt>cds::opt::v::initialized_static_buffer< cds::any_type, 256 > </tt>.
97
98             \p opt::compare or \p opt::less options are used in some \p Container class for ordering.
99             \p %opt::compare option has the highest priority: if \p %opt::compare is specified, \p %opt::less is not used.
100
101             You can pass other option that would be passed to \p adapt metafunction, see below.
102
103         <b>Internal details</b>
104
105             The \p %StripedSet class cannot utilize the \p Container specified directly, but only its adapted variant which
106             supports an unified interface. Internally, the adaptation is made via \p intrusive::striped_set::adapt metafunction that wraps bucket container
107             and provides the unified bucket interface suitable for \p %StripedSet. Such adaptation is completely transparent for you -
108             you don't need to call \p adapt metafunction directly, \p %StripedSet class's internal machinery itself invokes appropriate
109             \p adapt metafunction specialization to adjust your \p Container container class to \p %StripedSet bucket's internal interface.
110             All you need is to include a right header before <tt>striped_set.h</tt>.
111
112             By default, <tt>intrusive::striped_set::adapt<AnyContainer, OptionPack> </tt> metafunction does not make any wrapping to \p AnyContainer,
113             so, the result <tt>intrusive::striped_set::adapt<AnyContainer, OptionPack>::type </tt> is the same as \p AnyContainer.
114             However, there are a lot of specializations of \p %intrusive::striped_set::adapt for \p boost::intrusive containers, see table below.
115             Any of this specialization wraps corresponding container making it suitable for the set's bucket.
116             Remember, you should include the proper header file for \p adapt <b>before</b> including <tt>striped_set.h</tt>.
117
118             \note It is important to specify <tt>boost::intrusive::constant_time_size<true></tt> option
119             for all \p boost::intrusive container that supports this option. Fast item counting feature is essential part of
120             \p %StripedSet resizing algorithm.
121
122             <table>
123                 <tr>
124                     <th>Container</th>
125                     <th>.h-file for \p adapt</th>
126                     <th>Example</th>
127                     <th>Notes</th>
128                 </tr>
129                 <tr>
130                     <td> \p boost::intrusive::list</td>
131                     <td><tt><cds/intrusive/striped_set/boost_list.h></tt></td>
132                     <td>\code
133                         #include <cds/intrusive/striped_set/boost_list.h>
134                         #include <cds/intrusive/striped_set.h>
135                         typedef cds::intrusive::StripedSet<
136                         boost::intrusive::list<T, boost::intrusive::constant_time_size<true> >,
137                             cds::opt::less< std::less<T> >
138                         > striped_set;
139                     \endcode
140                     </td>
141                     <td>
142                         The list is ordered.
143                         Template argument pack \p Options <b>must</b> contain cds::opt::less or cds::opt::compare for type \p T stored in the list
144                     </td>
145                 </tr>
146                 <tr>
147                     <td> \p boost::intrusive::slist</td>
148                     <td><tt><cds/intrusive/striped_set/boost_slist.h></tt></td>
149                     <td>\code
150                         #include <cds/intrusive/striped_set/boost_slist.h>
151                         #include <cds/intrusive/striped_set.h>
152                         typedef cds::intrusive::StripedSet<
153                             boost::intrusive::slist<T, boost::intrusive::constant_time_size<true> >,
154                             cds::opt::less< std::less<T> >
155                         > striped_set;
156                     \endcode
157                     </td>
158                     <td>
159                         The list is ordered.
160                         Template argument pack \p Options <b>must</b> contain \p cds::opt::less or \p cds::opt::compare for type \p T stored in the list
161                     </td>
162                 </tr>
163                 <tr>
164                     <td> \p boost::intrusive::set</td>
165                     <td><tt><cds/intrusive/striped_set/boost_set.h></tt></td>
166                     <td>\code
167                         #include <cds/intrusive/striped_set/boost_set.h>
168                         #include <cds/intrusive/striped_set.h>
169                         typedef cds::intrusive::StripedSet<
170                             boost::intrusive::set<T, boost::intrusive::constant_time_size<true> >
171                         > striped_set;
172                     \endcode
173                     </td>
174                     <td>
175                         Note that \p boost::intrusive::compare option using in \p boost::intrusive::set
176                         should support \p T type stored in the set and any type \p Q that you can use
177                         in \p erase() and \p find() member functions.
178                     </td>
179                 </tr>
180                 <tr>
181                     <td> \p boost::intrusive::unordered_set</td>
182                     <td><tt><cds/intrusive/striped_set/boost_unordered_set.h></tt></td>
183                     <td>\code
184                         #include <cds/intrusive/striped_set/boost_unordered_set.h>
185                         #include <cds/intrusive/striped_set.h>
186                         typedef cds::intrusive::StripedSet<
187                             boost::intrusive::unordered_set<T
188                                 ,boost::intrusive::constant_time_size<true>
189                                 ,boost::intrusive::hash< user_provided_hash_functor >
190                             >
191                         > striped_set;
192                     \endcode
193                     </td>
194                     <td>
195                         You should provide two different hash function \p h1 and \p h2 - one for \p boost::intrusive::unordered_set
196                         and other for \p %StripedSet. For the best result, \p h1 and \p h2 must be orthogonal i.e. <tt>h1(X) != h2(X)</tt> for any value \p X
197
198                         The option \p opt::buffer is used for \p boost::intrusive::bucket_traits.
199                         Default is <tt> cds::opt::v::initialized_static_buffer< cds::any_type, 256 > </tt>.
200                         The resizing policy should correlate with the buffer capacity.
201                         The default resizing policy is <tt>cds::container::striped_set::load_factor_resizing<256> </tt> what gives load factor 1 for
202                         default bucket buffer that is the best for \p boost::intrusive::unordered_set.
203                     </td>
204                 </tr>
205                 <tr>
206                     <td> \p boost::intrusive::avl_set</td>
207                     <td><tt><cds/intrusive/striped_set/boost_avl_set.h></tt></td>
208                     <td>\code
209                         #include <cds/intrusive/striped_set/boost_avl_set.h>
210                         #include <cds/intrusive/striped_set.h>
211                         typedef cds::intrusive::StripedSet<
212                             boost::intrusive::avl_set<T, boost::intrusive::constant_time_size<true> >
213                         > striped_set;
214                     \endcode
215                     </td>
216                     <td>
217                         Note that \p boost::intrusive::compare option using in \p boost::intrusive::avl_set
218                         should support \p T type stored in the set and any type \p Q that you can use
219                         in \p erase() and \p find() member functions.
220                     </td>
221                 </tr>
222                 <tr>
223                     <td> \p boost::intrusive::sg_set</td>
224                     <td><tt><cds/intrusive/striped_set/boost_sg_set.h></tt></td>
225                     <td>\code
226                         #include <cds/intrusive/striped_set/boost_sg_set.h>
227                         #include <cds/intrusive/striped_set.h>
228                         typedef cds::intrusive::StripedSet<
229                             boost::intrusive::sg_set<T, boost::intrusive::constant_time_size<true> >
230                         > striped_set;
231                     \endcode
232                     </td>
233                     <td>
234                         Note that \p boost::intrusive::compare option using in \p boost::intrusive::sg_set
235                         should support \p T type stored in the set and any type \p Q that you can use
236                         in \p erase() and \p find() member functions.
237                     </td>
238                 </tr>
239                 <tr>
240                     <td> \p boost::intrusive::splay_set</td>
241                     <td><tt><cds/intrusive/striped_set/boost_splay_set.h></tt></td>
242                     <td>\code
243                         #include <cds/intrusive/striped_set/boost_splay_set.h>
244                         #include <cds/intrusive/striped_set.h>
245                         typedef cds::intrusive::StripedSet<
246                             boost::intrusive::splay_set<T, boost::intrusive::constant_time_size<true> >
247                         > striped_set;
248                     \endcode
249                     </td>
250                     <td>
251                         Note that \p boost::intrusive::compare option using in \p boost::intrusive::splay_set
252                         should support \p T type stored in the set and any type \p Q that you can use
253                         in \p erase() and \p find() member functions.
254                     </td>
255                 </tr>
256                 <tr>
257                     <td> \p boost::intrusive::treap_set</td>
258                     <td><tt><cds/intrusive/striped_set/boost_treap_set.h></tt></td>
259                     <td>\code
260                         #include <cds/intrusive/striped_set/boost_treap_set.h>
261                         #include <cds/intrusive/striped_set.h>
262                         typedef cds::intrusive::StripedSet<
263                             boost::intrusive::treap_set<T, boost::intrusive::constant_time_size<true> >
264                         > striped_set;
265                     \endcode
266                     </td>
267                     <td>
268                         Note that \p boost::intrusive::compare option using in \p boost::intrusive::treap_set
269                         should support \p T type stored in the set and any type \p Q that you can use
270                         in \p erase() and \p find() member functions.
271                     </td>
272                 </tr>
273             </table>
274
275             You can use another intrusive container type as striped set's bucket.
276             Suppose, you have a container class \p MyBestContainer and you want to integrate it with \p StripedSet as bucket type.
277             There are two possibility:
278             - either your \p MyBestContainer class has native support of bucket's interface;
279                 in this case, you can use default \p intrusive::striped_set::adapt metafunction;
280             - or your \p MyBestContainer class does not support bucket's interface, which means, that you should create a specialization of
281                 <tt>cds::intrusive::striped_set::adapt<MyBestContainer> </tt> metafunction providing necessary interface.
282
283             The <tt>intrusive::striped_set::adapt< Container, OptionPack ></tt> metafunction has two template argument:
284             - \p Container is the class that should be used as the bucket, for example, <tt>boost::intrusive::list< T ></tt>.
285             - \p OptionPack is the packed options from \p %StripedSet declaration. The \p adapt metafunction can use
286                 any option from \p OptionPack for its internal use. For example, a \p compare option can be passed to \p adapt
287                 metafunction via \p OptionPack argument of \p %StripedSet declaration.
288
289             See \p intrusive::striped_set::adapt metafunction for the description of interface that the bucket container must provide
290             to be \p %StripedSet compatible.
291     */
292     template <class Container, typename... Options>
293     class StripedSet
294     {
295     public:
296         //@cond
297         struct default_options {
298             typedef striped_set::striping<>         mutex_policy;
299             typedef typename cds::opt::v::hash_selector< cds::opt::none >::type   hash;
300             typedef cds::atomicity::item_counter    item_counter;
301             typedef CDS_DEFAULT_ALLOCATOR           allocator;
302             typedef cds::opt::none                  resizing_policy;
303             typedef cds::opt::none                  compare;
304             typedef cds::opt::none                  less;
305         };
306
307         typedef typename cds::opt::make_options<
308             typename cds::opt::find_type_traits< default_options, Options... >::type
309             ,Options...
310         >::type   options;
311         //@endcond
312
313         typedef Container                           underlying_container_type   ;   ///< original intrusive container type for the bucket
314         typedef typename cds::intrusive::striped_set::adapt< underlying_container_type, Options... >::type   bucket_type ;   ///< container type adapted for hash set
315         typedef typename bucket_type::value_type    value_type  ;   ///< value type stored in the set
316
317         typedef typename options::hash              hash            ; ///< Hash functor
318         typedef typename options::item_counter      item_counter    ; ///< Item counter
319         typedef typename cds::opt::select_default<
320             typename options::resizing_policy,
321             typename bucket_type::default_resizing_policy
322         >::type                                     resizing_policy ; ///< Resizing policy
323         typedef typename options::allocator         allocator_type  ; ///< allocator type specified in options.
324         typedef typename options::mutex_policy      mutex_policy    ; ///< Mutex policy
325
326         typedef cds::details::Allocator< bucket_type, allocator_type > bucket_allocator;  ///< bucket allocator type based on allocator_type
327
328     protected:
329         bucket_type *           m_Buckets;      ///< Bucket table
330         atomics::atomic<size_t> m_nBucketMask;  ///< Bucket table size - 1. m_nBucketMask + 1 should be power of two.
331         item_counter            m_ItemCounter;  ///< Item counter
332         hash                    m_Hash;         ///< Hash functor
333
334         mutex_policy    m_MutexPolicy   ;   ///< Mutex policy
335         resizing_policy m_ResizingPolicy;   ///< Resizing policy
336
337         static const size_t c_nMinimalCapacity = 16 ;   ///< Minimal capacity
338
339     protected:
340         //@cond
341         typedef typename mutex_policy::scoped_cell_lock     scoped_cell_lock;
342         typedef typename mutex_policy::scoped_full_lock     scoped_full_lock;
343         typedef typename mutex_policy::scoped_resize_lock   scoped_resize_lock;
344         //@endcond
345
346     protected:
347         //@cond
348         static size_t calc_init_capacity( size_t nCapacity )
349         {
350             nCapacity = cds::beans::ceil2( nCapacity );
351             return nCapacity < c_nMinimalCapacity ? c_nMinimalCapacity : nCapacity;
352         }
353
354         void alloc_bucket_table( size_t nSize )
355         {
356             assert( cds::beans::is_power2( nSize ));
357             m_nBucketMask.store( nSize - 1, atomics::memory_order_release );
358             m_Buckets = bucket_allocator().NewArray( nSize );
359         }
360
361         static void free_bucket_table( bucket_type * pBuckets, size_t nSize )
362         {
363             bucket_allocator().Delete( pBuckets, nSize );
364         }
365
366         template <typename Q>
367         size_t hashing( Q const& v ) const
368         {
369             return m_Hash( v );
370         }
371
372         bucket_type * bucket( size_t nHash ) const CDS_NOEXCEPT
373         {
374             return m_Buckets + (nHash & m_nBucketMask.load( atomics::memory_order_relaxed ));
375         }
376
377         template <typename Q, typename Func>
378         bool find_( Q& val, Func f )
379         {
380             size_t nHash = hashing( val );
381
382             scoped_cell_lock sl( m_MutexPolicy, nHash );
383             return bucket( nHash )->find( val, f );
384         }
385
386         template <typename Q, typename Less, typename Func>
387         bool find_with_( Q& val, Less pred, Func f )
388         {
389             size_t nHash = hashing( val );
390             scoped_cell_lock sl( m_MutexPolicy, nHash );
391             return bucket( nHash )->find( val, pred, f );
392         }
393
394         void internal_resize( size_t nNewCapacity )
395         {
396             // All locks are already locked!
397             m_MutexPolicy.resize( nNewCapacity );
398
399             size_t nOldCapacity = bucket_count();
400             bucket_type * pOldBuckets = m_Buckets;
401
402             alloc_bucket_table( nNewCapacity );
403
404             typedef typename bucket_type::iterator bucket_iterator;
405             bucket_type * pEnd = pOldBuckets + nOldCapacity;
406             for ( bucket_type * pCur = pOldBuckets; pCur != pEnd; ++pCur ) {
407                 bucket_iterator itEnd = pCur->end();
408                 bucket_iterator itNext;
409                 for ( bucket_iterator it = pCur->begin(); it != itEnd; it = itNext ) {
410                     itNext = it;
411                     ++itNext;
412                     bucket( m_Hash( *it ))->move_item( *pCur, it );
413                 }
414                 pCur->clear();
415             }
416
417             free_bucket_table( pOldBuckets, nOldCapacity );
418
419             m_ResizingPolicy.reset();
420         }
421
422         void resize()
423         {
424             size_t nOldCapacity = bucket_count( atomics::memory_order_acquire );
425
426             scoped_resize_lock al( m_MutexPolicy );
427             if ( al.success()) {
428                 if ( nOldCapacity != bucket_count( atomics::memory_order_acquire ) ) {
429                     // someone resized already
430                     return;
431                 }
432
433                 internal_resize( nOldCapacity * 2 );
434             }
435         }
436
437         //@endcond
438
439     public:
440         /// Default ctor. The initial capacity is 16.
441         StripedSet()
442             : m_Buckets( nullptr )
443             , m_nBucketMask( c_nMinimalCapacity - 1 )
444             , m_MutexPolicy( c_nMinimalCapacity )
445         {
446             alloc_bucket_table( bucket_count() );
447         }
448
449         /// Ctor with initial capacity specified
450         StripedSet(
451             size_t nCapacity    ///< Initial size of bucket table and lock array. Must be power of two, the minimum is 16.
452         )
453             : m_Buckets( nullptr )
454             , m_nBucketMask( calc_init_capacity(nCapacity) - 1 )
455             , m_MutexPolicy( bucket_count() )
456         {
457             alloc_bucket_table( bucket_count() );
458         }
459
460         /// Ctor with resizing policy (copy semantics)
461         /**
462             This constructor initializes m_ResizingPolicy member with copy of \p resizingPolicy parameter
463         */
464         StripedSet(
465             size_t nCapacity    ///< Initial size of bucket table and lock array. Must be power of two, the minimum is 16.
466             ,resizing_policy const& resizingPolicy  ///< Resizing policy
467         )
468         : m_Buckets( nullptr )
469         , m_nBucketMask( ( nCapacity ? calc_init_capacity(nCapacity) : c_nMinimalCapacity ) - 1 )
470         , m_MutexPolicy( bucket_count() )
471         , m_ResizingPolicy( resizingPolicy )
472         {
473             alloc_bucket_table( bucket_count() );
474         }
475
476         /// Ctor with resizing policy (move semantics)
477         /**
478             This constructor initializes m_ResizingPolicy member moving \p resizingPolicy parameter
479             Move semantics is used.
480         */
481         StripedSet(
482             size_t nCapacity    ///< Initial size of bucket table and lock array. Must be power of two, the minimum is 16.
483             ,resizing_policy&& resizingPolicy  ///< Resizing policy
484         )
485         : m_Buckets( nullptr )
486         , m_nBucketMask( ( nCapacity ? calc_init_capacity(nCapacity) : c_nMinimalCapacity ) - 1 )
487         , m_MutexPolicy( bucket_count() )
488         , m_ResizingPolicy( std::forward<resizing_policy>( resizingPolicy ))
489         {
490             alloc_bucket_table( bucket_count() );
491         }
492
493         /// Destructor destroys internal data
494         ~StripedSet()
495         {
496             free_bucket_table( m_Buckets, bucket_count() );
497         }
498
499     public:
500         /// Inserts new node
501         /**
502             The function inserts \p val in the set if it does not contain
503             an item with key equal to \p val.
504
505             Returns \p true if \p val is placed into the set, \p false otherwise.
506         */
507         bool insert( value_type& val )
508         {
509             return insert( val, []( value_type& ) {} );
510         }
511
512         /// Inserts new node
513         /**
514             The function allows to split creating of new item into two part:
515             - create item with key only
516             - insert new item into the set
517             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
518
519             The functor signature is:
520             \code
521                 void func( value_type& val );
522             \endcode
523             where \p val is the item inserted.
524         */
525         template <typename Func>
526         bool insert( value_type& val, Func f )
527         {
528             bool bOk;
529             bool bResize;
530             size_t nHash = hashing( val );
531             bucket_type * pBucket;
532             {
533                 scoped_cell_lock sl( m_MutexPolicy, nHash );
534                 pBucket = bucket( nHash );
535                 bOk = pBucket->insert( val, f );
536                 bResize = bOk && m_ResizingPolicy( ++m_ItemCounter, *this, *pBucket );
537             }
538
539             if ( bResize )
540                 resize();
541             return bOk;
542         }
543
544         /// Updates the node
545         /**
546             The operation performs inserting or changing data with lock-free manner.
547
548             If the item \p val is not found in the set, then \p val is inserted
549             iff \p bAllowInsert is \p true.
550             Otherwise, the functor \p func is called with item found.
551             The functor signature is:
552             \code
553                 void func( bool bNew, value_type& item, value_type& val );
554             \endcode
555             with arguments:
556             - \p bNew - \p true if the item has been inserted, \p false otherwise
557             - \p item - item of the set
558             - \p val - argument \p val passed into the \p update() function
559             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
560             refers to the same thing.
561
562             The functor may change non-key fields of the \p item.
563
564             Returns std::pair<bool, bool> where \p first is \p true if operation is successful,
565             \p second is \p true if new item has been added or \p false if the item with \p val
566             already is in the set.
567         */
568         template <typename Func>
569         std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
570         {
571             std::pair<bool, bool> result;
572             bool bResize;
573             size_t nHash = hashing( val );
574             bucket_type * pBucket;
575             {
576                 scoped_cell_lock sl( m_MutexPolicy, nHash );
577                 pBucket = bucket( nHash );
578
579                 result = pBucket->update( val, func, bAllowInsert );
580                 bResize = result.first && result.second && m_ResizingPolicy( ++m_ItemCounter, *this, *pBucket );
581             }
582
583             if ( bResize )
584                 resize();
585             return result;
586         }
587         //@cond
588         template <typename Func>
589         std::pair<bool, bool> ensure( value_type& val, Func func )
590         {
591             return update( val, func, true );
592         }
593         //@endcond
594
595         /// Unlink the item \p val from the set
596         /**
597             The function searches the item \p val in the set and unlink it
598             if it is found and is equal to \p val (here, the equality means that
599             \p val belongs to the set: if \p item is an item found then
600             unlink is successful iif <tt>&val == &item</tt>)
601
602             The function returns \p true if success and \p false otherwise.
603         */
604         bool unlink( value_type& val )
605         {
606             bool bOk;
607             size_t nHash = hashing( val );
608             {
609                 scoped_cell_lock sl( m_MutexPolicy, nHash );
610                 bOk = bucket( nHash )->unlink( val );
611             }
612
613             if ( bOk )
614                 --m_ItemCounter;
615             return bOk;
616         }
617
618         /// Deletes the item from the set
619         /** \anchor cds_intrusive_StripedSet_erase
620             The function searches an item with key equal to \p val in the set,
621             unlinks it from the set, and returns a pointer to unlinked item.
622
623             If the item with key equal to \p val is not found the function return \p nullptr.
624
625             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
626         */
627         template <typename Q>
628         value_type * erase( Q const& val )
629         {
630             return erase( val, [](value_type const&) {} );
631         }
632
633         /// Deletes the item from the set using \p pred predicate for searching
634         /**
635             The function is an analog of \ref cds_intrusive_StripedSet_erase "erase(Q const&)"
636             but \p pred is used for key comparing
637             \p Less has the interface like \p std::less.
638             \p pred must imply the same element order as the comparator used for building the set.
639         */
640         template <typename Q, typename Less>
641         value_type * erase_with( Q const& val, Less pred )
642         {
643             return erase_with( val, pred, [](value_type const&) {} );
644         }
645
646         /// Deletes the item from the set
647         /** \anchor cds_intrusive_StripedSet_erase_func
648
649             The function searches an item with key equal to \p val in the set,
650             call \p f functor with item found, unlinks it from the set, and returns a pointer to unlinked item.
651
652             The \p Func interface is
653             \code
654             struct functor {
655                 void operator()( value_type const& item );
656             };
657             \endcode
658
659             If the item with key equal to \p val is not found the function return \p false.
660
661             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
662         */
663         template <typename Q, typename Func>
664         value_type * erase( Q const& val, Func f )
665         {
666             size_t nHash = hashing( val );
667             value_type * pVal;
668             {
669                 scoped_cell_lock sl( m_MutexPolicy, nHash );
670                 pVal = bucket( nHash )->erase( val, f );
671             }
672
673             if ( pVal )
674                 --m_ItemCounter;
675             return pVal;
676         }
677
678         /// Deletes the item from the set using \p pred predicate for searching
679         /**
680             The function is an analog of \ref cds_intrusive_StripedSet_erase_func "erase(Q const&, Func)"
681             but \p pred is used for key comparing
682             \p Less has the interface like \p std::less.
683             \p pred must imply the same element order as the comparator used for building the set.
684         */
685         template <typename Q, typename Less, typename Func>
686         value_type * erase_with( Q const& val, Less pred, Func f )
687         {
688             size_t nHash = hashing( val );
689             value_type * pVal;
690             {
691                 scoped_cell_lock sl( m_MutexPolicy, nHash );
692                 pVal = bucket( nHash )->erase( val, pred, f );
693             }
694
695             if ( pVal )
696                 --m_ItemCounter;
697             return pVal;
698         }
699
700         /// Find the key \p val
701         /** \anchor cds_intrusive_StripedSet_find_func
702             The function searches the item with key equal to \p val and calls the functor \p f for item found.
703             The interface of \p Func functor is:
704             \code
705             struct functor {
706                 void operator()( value_type& item, Q& val );
707             };
708             \endcode
709             where \p item is the item found, \p val is the <tt>find</tt> function argument.
710
711             The functor may change non-key fields of \p item.
712
713             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
714             may modify both arguments.
715
716             Note the hash functor specified for class \p Traits template parameter
717             should accept a parameter of type \p Q that can be not the same as \p value_type.
718
719             The function returns \p true if \p val is found, \p false otherwise.
720         */
721         template <typename Q, typename Func>
722         bool find( Q& val, Func f )
723         {
724             return find_( val, f );
725         }
726
727         /// Find the key \p val using \p pred predicate
728         /**
729             The function is an analog of \ref cds_intrusive_StripedSet_find_func "find(Q&, Func)"
730             but \p pred is used for key comparing
731             \p Less has the interface like \p std::less.
732             \p pred must imply the same element order as the comparator used for building the set.
733         */
734         template <typename Q, typename Less, typename Func>
735         bool find_with( Q& val, Less pred, Func f )
736         {
737             return find_with_( val, pred, f );
738         }
739
740         /// Find the key \p val
741         /** \anchor cds_intrusive_StripedSet_find_cfunc
742             The function searches the item with key equal to \p val and calls the functor \p f for item found.
743             The interface of \p Func functor is:
744             \code
745             struct functor {
746                 void operator()( value_type& item, Q const& val );
747             };
748             \endcode
749             where \p item is the item found, \p val is the <tt>find</tt> function argument.
750
751             The functor may change non-key fields of \p item.
752
753             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
754             may modify both arguments.
755
756             The function returns \p true if \p val is found, \p false otherwise.
757         */
758         template <typename Q, typename Func>
759         bool find( Q const& val, Func f )
760         {
761             return find_( val, f );
762         }
763
764         /// Find the key \p val using \p pred predicate
765         /**
766             The function is an analog of \ref cds_intrusive_StripedSet_find_cfunc "find(Q const&, Func)"
767             but \p pred is used for key comparing
768             \p Less has the interface like \p std::less.
769             \p pred must imply the same element order as the comparator used for building the set.
770         */
771         template <typename Q, typename Less, typename Func>
772         bool find_with( Q const& val, Less pred, Func f )
773         {
774             return find_with_( val, pred, f );
775         }
776
777         /// Checks whether the set contains \p key
778         /**
779             The function searches the item with key equal to \p key
780             and returns \p true if it is found, and \p false otherwise.
781
782             Note the hash functor specified for class \p Traits template parameter
783             should accept a parameter of type \p Q that can be not the same as \p value_type.
784             Otherwise, you may use \p contains( Q const&, Less pred ) functions with explicit predicate for key comparing.
785         */
786         template <typename Q>
787         bool contains( Q const& key )
788         {
789             return find( key, [](value_type&, Q const& ) {} );
790         }
791         //@cond
792         template <typename Q>
793         CDS_DEPRECATED("use contains()")
794         bool find( Q const& val )
795         {
796             return contains( val );
797         }
798         //@endcond
799
800         /// Checks whether the set contains \p key using \p pred predicate for searching
801         /**
802             The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
803             \p Less functor has the interface like \p std::less.
804             \p Less must imply the same element order as the comparator used for building the set.
805         */
806         template <typename Q, typename Less>
807         bool contains( Q const& key, Less pred )
808         {
809             return find_with( key, pred, [](value_type& , Q const& ) {} );
810         }
811         //@cond
812         template <typename Q, typename Less>
813         CDS_DEPRECATED("use contains()")
814         bool find_with( Q const& val, Less pred )
815         {
816             return contains( val, pred );
817         }
818         //@endcond
819
820         /// Clears the set
821         /**
822             The function unlinks all items from the set.
823         */
824         void clear()
825         {
826             // locks entire array
827             scoped_full_lock sl( m_MutexPolicy );
828
829             size_t nBucketCount = bucket_count();
830             bucket_type * pBucket = m_Buckets;
831             for ( size_t i = 0; i < nBucketCount; ++i, ++pBucket )
832                 pBucket->clear();
833             m_ItemCounter.reset();
834         }
835
836         /// Clears the set and calls \p disposer for each item
837         /**
838             The function unlinks all items from the set calling \p disposer for each item.
839             \p Disposer functor interface is:
840             \code
841             struct Disposer{
842                 void operator()( value_type * p );
843             };
844             \endcode
845         */
846         template <typename Disposer>
847         void clear_and_dispose( Disposer disposer )
848         {
849             // locks entire array
850             scoped_full_lock sl( m_MutexPolicy );
851
852             size_t nBucketCount = bucket_count();
853             bucket_type * pBucket = m_Buckets;
854             for ( size_t i = 0; i < nBucketCount; ++i, ++pBucket )
855                 pBucket->clear( disposer );
856             m_ItemCounter.reset();
857         }
858
859         /// Checks if the set is empty
860         /**
861             Emptiness is checked by item counting: if item count is zero then the set is empty.
862         */
863         bool empty() const
864         {
865             return size() == 0;
866         }
867
868         /// Returns item count in the set
869         size_t size() const
870         {
871             return m_ItemCounter;
872         }
873
874         /// Returns the size of hash table
875         /**
876             The hash table size is non-constant and can be increased via resizing.
877         */
878         size_t bucket_count() const
879         {
880             return m_nBucketMask.load( atomics::memory_order_relaxed ) + 1;
881         }
882         //@cond
883         size_t bucket_count( atomics::memory_order load_mo ) const
884         {
885             return m_nBucketMask.load( load_mo ) + 1;
886         }
887         //@endcond
888
889         /// Returns lock array size
890         size_t lock_count() const
891         {
892             return m_MutexPolicy.lock_count();
893         }
894
895         /// Returns resizing policy object
896         resizing_policy& get_resizing_policy()
897         {
898             return m_ResizingPolicy;
899         }
900
901         /// Returns resizing policy (const version)
902         resizing_policy const& get_resizing_policy() const
903         {
904             return m_ResizingPolicy;
905         }
906     };
907 }}  // namespace cds::itrusive
908
909 #endif // #ifndef CDSLIB_INTRUSIVE_STRIPED_SET_H