Remove CDS_EMPLACE_SUPPORT macro and unused code
[libcds.git] / cds / container / segmented_queue.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_CONTAINER_SEGMENTED_QUEUE_H
4 #define __CDS_CONTAINER_SEGMENTED_QUEUE_H
5
6 #include <memory>
7 #include <cds/intrusive/segmented_queue.h>
8 #include <cds/details/trivial_assign.h>
9 #include <cds/ref.h>
10
11 namespace cds { namespace container {
12
13     /// SegmentedQueue -related declarations
14     namespace segmented_queue {
15
16 #   ifdef CDS_DOXYGEN_INVOKED
17         /// SegmentedQueue internal statistics
18         typedef cds::intrusive::segmented_queue::stat stat;
19 #   else
20         using cds::intrusive::segmented_queue::stat;
21 #   endif
22
23         /// SegmentedQueue empty internal statistics (no overhead)
24         typedef cds::intrusive::segmented_queue::empty_stat empty_stat;
25
26         /// SegmentedQueue default type traits
27         struct type_traits {
28
29             /// Item allocator. Default is \ref CDS_DEFAULT_ALLOCATOR
30             typedef CDS_DEFAULT_ALLOCATOR   node_allocator;
31
32             /// Item counter, default is atomicity::item_counter
33             /**
34                 The item counting is an essential part of segmented queue algorithm.
35                 The \p empty() member function is based on checking <tt>size() == 0</tt>.
36                 Therefore, dummy item counter like atomicity::empty_item_counter is not the proper counter.
37             */
38             typedef atomicity::item_counter item_counter;
39
40             /// Internal statistics, possible predefined types are \ref stat, \ref empty_stat (the default)
41             typedef segmented_queue::empty_stat        stat;
42
43             /// Memory model, default is opt::v::relaxed_ordering. See cds::opt::memory_model for the full list of possible types
44             typedef opt::v::relaxed_ordering  memory_model;
45
46             /// Alignment of critical data, default is cache line alignment. See cds::opt::alignment option specification
47             enum { alignment = opt::cache_line_alignment };
48
49             /// Segment allocator. Default is \ref CDS_DEFAULT_ALLOCATOR
50             typedef CDS_DEFAULT_ALLOCATOR allocator;
51
52             /// Lock type used to maintain an internal list of allocated segments
53             typedef cds::lock::Spin lock_type;
54
55             /// Random \ref cds::opt::permutation_generator "permutation generator" for sequence [0, quasi_factor)
56             typedef cds::opt::v::random2_permutation<int>    permutation_generator;
57         };
58
59          /// Metafunction converting option list to traits for SegmentedQueue
60         /**
61             The metafunction can be useful if a few fields in \ref type_traits should be changed.
62             For example:
63             \code
64             typedef cds::container::segmented_queue::make_traits<
65                 cds::opt::item_counter< cds::atomicity::item_counter >
66             >::type my_segmented_queue_traits;
67             \endcode
68             This code creates \p %SegmentedQueue type traits with item counting feature,
69             all other \p type_traits members left unchanged.
70
71             \p Options are:
72             - \p opt::node_allocator - node allocator.
73             - \p opt::stat - internal statistics, possible type: \ref stat, \ref empty_stat (the default)
74             - \p opt::item_counter - item counting feature. Note that atomicity::empty_item_counetr is not suitable
75                 for segmented queue.
76             - \p opt::memory_model - memory model, default is \p opt::v::relaxed_ordering.
77                 See option description for the full list of possible models
78             - \p opt::alignment - the alignmentfor critical data, see option description for explanation
79             - \p opt::allocator - the allocator used to maintain segments.
80             - \p opt::lock_type - a mutual exclusion lock type used to maintain internal list of allocated
81                 segments. Default is \p cds::opt::Spin, \p std::mutex is also suitable.
82             - \p opt::permutation_generator - a random permutation generator for sequence [0, quasi_factor),
83                 default is cds::opt::v::random2_permutation<int>
84         */
85         template <CDS_DECL_OPTIONS9>
86         struct make_traits {
87 #   ifdef CDS_DOXYGEN_INVOKED
88             typedef implementation_defined type ;   ///< Metafunction result
89 #   else
90             typedef typename cds::opt::make_options<
91                 typename cds::opt::find_type_traits< type_traits, CDS_OPTIONS9 >::type
92                 ,CDS_OPTIONS9
93             >::type   type;
94 #   endif
95         };
96
97     } // namespace segmented_queue
98
99     //@cond
100     namespace details {
101
102         template <typename GC, typename T, typename Traits>
103         struct make_segmented_queue
104         {
105             typedef GC      gc;
106             typedef T       value_type;
107             typedef Traits  original_type_traits;
108
109             typedef cds::details::Allocator< T, typename original_type_traits::node_allocator > cxx_node_allocator;
110             struct node_disposer {
111                 void operator()( T * p )
112                 {
113                     cxx_node_allocator().Delete( p );
114                 }
115             };
116
117             struct intrusive_type_traits: public original_type_traits {
118                 typedef node_disposer   disposer;
119             };
120
121             typedef cds::intrusive::SegmentedQueue< gc, value_type, intrusive_type_traits > type;
122         };
123
124     } // namespace details
125     //@endcond
126
127     /// Segmented queue
128     /** @ingroup cds_nonintrusive_queue
129
130         The queue is based on work
131         - [2010] Afek, Korland, Yanovsky "Quasi-Linearizability: relaxed consistency for improved concurrency"
132
133         In this paper the authors offer a relaxed version of linearizability, so-called quasi-linearizability,
134         that preserves some of the intuition, provides a flexible way to control the level of relaxation
135         and supports th implementation of more concurrent and scalable data structure.
136         Intuitively, the linearizability requires each run to be equivalent in some sense to a serial run
137         of the algorithm. This equivalence to some serial run imposes strong synchronization requirements
138         that in many cases results in limited scalability and synchronization bottleneck.
139
140         The general idea is that the queue maintains a linked list of segments, each segment is an array of
141         nodes in the size of the quasi factor, and each node has a deleted boolean marker, which states
142         if it has been dequeued. Each producer iterates over last segment in the linked list in some random
143         permutation order. Whet it finds an empty cell it performs a CAS operation attempting to enqueue its
144         new element. In case the entire segment has been scanned and no available cell is found (implying
145         that the segment is full), then it attempts to add a new segment to the list.
146
147         The dequeue operation is similar: the consumer iterates over the first segment in the linked list
148         in some random permutation order. When it finds an item which has not yet been dequeued, it performs
149         CAS on its deleted marker in order to "delete" it, if succeeded this item is considered dequeued.
150         In case the entire segment was scanned and all the nodes have already been dequeued (implying that
151         the segment is empty), then it attempts to remove this segment from the linked list and starts
152         the same process on the next segment. If there is no next segment, the queue is considered empty.
153
154         Based on the fact that most of the time threads do not add or remove segments, most of the work
155         is done in parallel on different cells in the segments. This ensures a controlled contention
156         depending on the segment size, which is quasi factor.
157
158         The segmented queue is an <i>unfair</i> queue since it violates the strong FIFO order but no more than
159         quasi factor. It means that the consumer dequeues any item from the current first segment.
160
161         Template parameters:
162         - \p GC - a garbage collector, possible types are cds::gc::HP, cds::gc::PTB
163         - \p T - the type of values stored in the queue
164         - \p Traits - queue type traits, default is segmented_queue::type_traits.
165             segmented_queue::make_traits metafunction can be used to construct your
166             type traits.
167     */
168     template <class GC, typename T, typename Traits = segmented_queue::type_traits >
169     class SegmentedQueue:
170 #ifdef CDS_DOXYGEN_INVOKED
171         public cds::intrusive::SegmentedQueue< GC, T, Traits >
172 #else
173         public details::make_segmented_queue< GC, T, Traits >::type
174 #endif
175     {
176         //@cond
177         typedef details::make_segmented_queue< GC, T, Traits > maker;
178         typedef typename maker::type base_class;
179         //@endcond
180     public:
181         typedef GC  gc          ;   ///< Garbage collector
182         typedef T   value_type  ;   ///< type of the value stored in the queue
183         typedef Traits options  ;   ///< Queue's traits
184
185         typedef typename options::node_allocator node_allocator;   ///< Node allocator
186         typedef typename base_class::memory_model  memory_model;   ///< Memory ordering. See cds::opt::memory_model option
187         typedef typename base_class::item_counter  item_counter;   ///< Item counting policy, see cds::opt::item_counter option setter
188         typedef typename base_class::stat          stat        ;   ///< Internal statistics policy
189         typedef typename base_class::lock_type     lock_type   ;   ///< Type of mutex for maintaining an internal list of allocated segments.
190         typedef typename base_class::permutation_generator permutation_generator; ///< Random permutation generator for sequence [0, quasi-factor)
191
192         static const size_t m_nHazardPtrCount = base_class::m_nHazardPtrCount ; ///< Count of hazard pointer required for the algorithm
193
194     protected:
195         //@cond
196         typedef typename maker::cxx_node_allocator  cxx_node_allocator;
197         typedef std::unique_ptr< value_type, typename maker::node_disposer >  scoped_node_ptr;
198
199         static value_type * alloc_node( value_type const& v )
200         {
201             return cxx_node_allocator().New( v );
202         }
203
204         static value_type * alloc_node()
205         {
206             return cxx_node_allocator().New();
207         }
208
209         template <typename... Args>
210         static value_type * alloc_node_move( Args&&... args )
211         {
212             return cxx_node_allocator().MoveNew( std::forward<Args>( args )... );
213         }
214
215         struct dummy_disposer {
216             void operator()( value_type * p )
217             {}
218         };
219         //@endcond
220
221     public:
222         /// Initializes the empty queue
223         SegmentedQueue(
224             size_t nQuasiFactor     ///< Quasi factor. If it is not a power of 2 it is rounded up to nearest power of 2. Minimum is 2.
225             )
226             : base_class( nQuasiFactor )
227         {}
228
229         /// Clears the queue and deletes all internal data
230         ~SegmentedQueue()
231         {}
232
233         /// Inserts a new element at last segment of the queue
234         /**
235             The function makes queue node in dynamic memory calling copy constructor for \p val
236             and then it calls intrusive::SEgmentedQueue::enqueue.
237             Returns \p true if success, \p false otherwise.
238         */
239         bool enqueue( value_type const& val )
240         {
241             scoped_node_ptr p( alloc_node(val) );
242             if ( base_class::enqueue( *p ) ) {
243                 p.release();
244                 return true;
245             }
246             return false;
247         }
248
249         /// Synonym for <tt>enqueue(value_type const&)</tt> function
250         bool push( value_type const& val )
251         {
252             return enqueue( val );
253         }
254
255         /// Inserts a new element at last segment of the queue using copy functor
256         /**
257             \p Func is a functor called to copy value \p data of type \p Q
258             which may be differ from type \ref value_type stored in the queue.
259             The functor's interface is:
260             \code
261             struct myFunctor {
262                 void operator()(value_type& dest, Q const& data)
263                 {
264                     // // Code to copy \p data to \p dest
265                     dest = data;
266                 }
267             };
268             \endcode
269             You may use \p boost:ref construction to pass functor \p f by reference.
270         */
271         template <typename Q, typename Func>
272         bool enqueue( Q const& data, Func f  )
273         {
274             scoped_node_ptr p( alloc_node() );
275             unref(f)( *p, data );
276             if ( base_class::enqueue( *p )) {
277                 p.release();
278                 return true;
279             }
280             return false;
281         }
282
283         /// Synonym for <tt>enqueue(Q const&, Func)</tt> function
284         template <typename Q, typename Func>
285         bool push( Q const& data, Func f )
286         {
287             return enqueue( data, f );
288         }
289
290         /// Enqueues data of type \ref value_type constructed with <tt>std::forward<Args>(args)...</tt>
291         template <typename... Args>
292         bool emplace( Args&&... args )
293         {
294             scoped_node_ptr p( alloc_node_move( std::forward<Args>(args)... ) );
295             if ( base_class::enqueue( *p )) {
296                 p.release();
297                 return true;
298             }
299             return false;
300         }
301
302         /// Removes an element from first segment of the queue
303         /**
304             \p Func is a functor called to copy dequeued value to \p dest of type \p Q
305             which may be differ from type \ref value_type stored in the queue.
306             The functor's interface is:
307             \code
308             struct myFunctor {
309                 void operator()(Q& dest, value_type const& data)
310                 {
311                     // Code to copy \p data to \p dest
312                     dest = data;
313                 }
314             };
315             \endcode
316             You may use \p boost:ref construction to pass functor \p f by reference.
317         */
318         template <typename Q, typename Func>
319         bool dequeue( Q& dest, Func f )
320         {
321             value_type * p = base_class::dequeue();
322             if ( p ) {
323                 unref(f)( dest, *p );
324                 gc::template retire< typename maker::node_disposer >( p );
325                 return true;
326             }
327             return false;
328         }
329
330         /// Synonym for <tt>dequeue( Q&, Func )</tt> function
331         template <typename Q, typename Func>
332         bool pop( Q& dest, Func f )
333         {
334             return dequeue( dest, f );
335         }
336
337         /// Dequeues a value from the queue
338         /**
339             If queue is not empty, the function returns \p true, \p dest contains copy of
340             dequeued value. The assignment operator for type \ref value_type is invoked.
341             If queue is empty, the function returns \p false, \p dest is unchanged.
342         */
343         bool dequeue( value_type& dest )
344         {
345             typedef cds::details::trivial_assign<value_type, value_type> functor;
346             return dequeue( dest, functor() );
347         }
348
349         /// Synonym for <tt>dequeue(value_type&)</tt> function
350         bool pop( value_type& dest )
351         {
352             return dequeue( dest );
353         }
354
355         /// Checks if the queue is empty
356         /**
357             The original segmented queue algorithm does not allow to check emptiness accurately
358             because \p empty() is unlinearizable.
359             This function tests queue's emptiness checking <tt>size() == 0</tt>,
360             so, the item counting feature is an essential part of queue's algorithm.
361         */
362         bool empty() const
363         {
364             return base_class::empty();
365         }
366
367         /// Clear the queue
368         /**
369             The function repeatedly calls \ref dequeue until it returns \p nullptr.
370             The disposer specified in \p Traits template argument is called for each removed item.
371         */
372         void clear()
373         {
374             base_class::clear();
375         }
376
377         /// Returns queue's item count
378         size_t size() const
379         {
380             return base_class::size();
381         }
382
383         /// Returns reference to internal statistics
384         /**
385             The type of internal statistics is specified by \p Traits template argument.
386         */
387         const stat& statistics() const
388         {
389             return base_class::statistics();
390         }
391
392         /// Returns quasi factor, a power-of-two number
393         size_t quasi_factor() const
394         {
395             return base_class::quasi_factor();
396         }
397     };
398
399 }} // namespace cds::container
400
401 #endif // #ifndef __CDS_CONTAINER_SEGMENTED_QUEUE_H