segmented_queue.h 17 KB

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