msqueue.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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_MSQUEUE_H
  6. #define CDSLIB_CONTAINER_MSQUEUE_H
  7. #include <memory>
  8. #include <cds/intrusive/msqueue.h>
  9. #include <cds/container/details/base.h>
  10. namespace cds { namespace container {
  11. /// MSQueue related definitions
  12. /** @ingroup cds_nonintrusive_helper
  13. */
  14. namespace msqueue {
  15. /// Internal statistics
  16. template <typename Counter = cds::intrusive::msqueue::stat<>::counter_type >
  17. using stat = cds::intrusive::msqueue::stat< Counter >;
  18. /// Dummy internal statistics
  19. typedef cds::intrusive::msqueue::empty_stat empty_stat;
  20. /// MSQueue default type traits
  21. struct traits
  22. {
  23. /// Node allocator
  24. typedef CDS_DEFAULT_ALLOCATOR allocator;
  25. /// Back-off strategy
  26. typedef cds::backoff::empty back_off;
  27. /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting
  28. typedef atomicity::empty_item_counter item_counter;
  29. /// Internal statistics (by default, disabled)
  30. /**
  31. Possible option value are: \p msqueue::stat, \p msqueue::empty_stat (the default),
  32. user-provided class that supports \p %msqueue::stat interface.
  33. */
  34. typedef msqueue::empty_stat stat;
  35. /// C++ memory ordering model
  36. /**
  37. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
  38. or \p opt::v::sequential_consistent (sequentially consisnent memory model).
  39. */
  40. typedef opt::v::relaxed_ordering memory_model;
  41. /// Padding for internal critical atomic data. Default is \p opt::cache_line_padding
  42. enum { padding = opt::cache_line_padding };
  43. };
  44. /// Metafunction converting option list to \p msqueue::traits
  45. /**
  46. Supported \p Options are:
  47. - \p opt::allocator - allocator (like \p std::allocator) used for allocating queue nodes. Default is \ref CDS_DEFAULT_ALLOCATOR
  48. - \p opt::back_off - back-off strategy used, default is \p cds::backoff::empty.
  49. - \p opt::item_counter - the type of item counting feature. Default is \p cds::atomicity::empty_item_counter (item counting disabled)
  50. To enable item counting use \p cds::atomicity::item_counter
  51. - \p opt::stat - the type to gather internal statistics.
  52. Possible statistics types are: \p msqueue::stat, \p msqueue::empty_stat, user-provided class that supports \p %msqueue::stat interface.
  53. Default is \p %msqueue::empty_stat.
  54. - \p opt::padding - padding for internal critical atomic data. Default is \p opt::cache_line_padding
  55. - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
  56. or \p opt::v::sequential_consistent (sequentially consisnent memory model).
  57. Example: declare \p %MSQueue with item counting and internal statistics
  58. \code
  59. typedef cds::container::MSQueue< cds::gc::HP, Foo,
  60. typename cds::container::msqueue::make_traits<
  61. cds::opt::item_counter< cds::atomicity::item_counter >,
  62. cds::opt::stat< cds::container::msqueue::stat<> >
  63. >::type
  64. > myQueue;
  65. \endcode
  66. */
  67. template <typename... Options>
  68. struct make_traits {
  69. # ifdef CDS_DOXYGEN_INVOKED
  70. typedef implementation_defined type; ///< Metafunction result
  71. # else
  72. typedef typename cds::opt::make_options<
  73. typename cds::opt::find_type_traits< traits, Options... >::type
  74. , Options...
  75. >::type type;
  76. # endif
  77. };
  78. } // namespace msqueue
  79. //@cond
  80. namespace details {
  81. template <typename GC, typename T, typename Traits>
  82. struct make_msqueue
  83. {
  84. typedef GC gc;
  85. typedef T value_type;
  86. typedef Traits traits;
  87. struct node_type : public intrusive::msqueue::node< gc >
  88. {
  89. value_type m_value;
  90. node_type( value_type const& val )
  91. : m_value( val )
  92. {}
  93. template <typename... Args>
  94. node_type( Args&&... args )
  95. : m_value( std::forward<Args>( args )... )
  96. {}
  97. };
  98. typedef typename std::allocator_traits<
  99. typename traits::allocator
  100. >::template rebind_alloc<node_type> allocator_type;
  101. typedef cds::details::Allocator< node_type, allocator_type > cxx_allocator;
  102. struct node_deallocator
  103. {
  104. void operator ()( node_type * pNode )
  105. {
  106. cxx_allocator().Delete( pNode );
  107. }
  108. };
  109. struct intrusive_traits : public traits
  110. {
  111. typedef cds::intrusive::msqueue::base_hook< cds::opt::gc<gc> > hook;
  112. typedef node_deallocator disposer;
  113. static constexpr const cds::intrusive::opt::link_check_type link_checker = cds::intrusive::msqueue::traits::link_checker;
  114. };
  115. typedef intrusive::MSQueue< gc, node_type, intrusive_traits > type;
  116. };
  117. }
  118. //@endcond
  119. /// Michael & Scott lock-free queue
  120. /** @ingroup cds_nonintrusive_queue
  121. It is non-intrusive version of Michael & Scott's queue algorithm based on intrusive implementation
  122. \p cds::intrusive::MSQueue.
  123. Template arguments:
  124. - \p GC - garbage collector type: \p gc::HP, \p gc::DHP
  125. - \p T is a type stored in the queue.
  126. - \p Traits - queue traits, default is \p msqueue::traits. You can use \p msqueue::make_traits
  127. metafunction to make your traits or just derive your traits from \p %msqueue::traits:
  128. \code
  129. struct myTraits: public cds::container::msqueue::traits {
  130. typedef cds::intrusive::msqueue::stat<> stat;
  131. typedef cds::atomicity::item_counter item_counter;
  132. };
  133. typedef cds::container::MSQueue< cds::gc::HP, Foo, myTraits > myQueue;
  134. // Equivalent make_traits example:
  135. typedef cds::container::MSQueue< cds::gc::HP, Foo,
  136. typename cds::container::msqueue::make_traits<
  137. cds::opt::stat< cds::container::msqueue::stat<> >,
  138. cds::opt::item_counter< cds::atomicity::item_counter >
  139. >::type
  140. > myQueue;
  141. \endcode
  142. */
  143. template <typename GC, typename T, typename Traits = cds::container::msqueue::traits>
  144. class MSQueue:
  145. #ifdef CDS_DOXYGEN_INVOKED
  146. private intrusive::MSQueue< GC, cds::intrusive::msqueue::node< T >, Traits >
  147. #else
  148. private details::make_msqueue< GC, T, Traits >::type
  149. #endif
  150. {
  151. //@cond
  152. typedef details::make_msqueue< GC, T, Traits > maker;
  153. typedef typename maker::type base_class;
  154. //@endcond
  155. public:
  156. /// Rebind template arguments
  157. template <typename GC2, typename T2, typename Traits2>
  158. struct rebind {
  159. typedef MSQueue< GC2, T2, Traits2> other ; ///< Rebinding result
  160. };
  161. public:
  162. typedef T value_type; ///< Value type stored in the queue
  163. typedef Traits traits; ///< Queue traits
  164. typedef typename base_class::gc gc; ///< Garbage collector used
  165. typedef typename base_class::back_off back_off; ///< Back-off strategy used
  166. typedef typename maker::allocator_type allocator_type; ///< Allocator type used for allocate/deallocate the nodes
  167. typedef typename base_class::item_counter item_counter; ///< Item counting policy used
  168. typedef typename base_class::stat stat; ///< Internal statistics policy used
  169. typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option
  170. static constexpr const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount; ///< Count of hazard pointer required for the algorithm
  171. protected:
  172. //@cond
  173. typedef typename maker::node_type node_type; ///< queue node type (derived from \p intrusive::msqueue::node)
  174. typedef typename maker::cxx_allocator cxx_allocator;
  175. typedef typename maker::node_deallocator node_deallocator; // deallocate node
  176. typedef typename base_class::node_traits node_traits;
  177. //@endcond
  178. protected:
  179. ///@cond
  180. static node_type * alloc_node()
  181. {
  182. return cxx_allocator().New();
  183. }
  184. static node_type * alloc_node( value_type const& val )
  185. {
  186. return cxx_allocator().New( val );
  187. }
  188. template <typename... Args>
  189. static node_type * alloc_node_move( Args&&... args )
  190. {
  191. return cxx_allocator().MoveNew( std::forward<Args>( args )... );
  192. }
  193. static void free_node( node_type * p )
  194. {
  195. node_deallocator()( p );
  196. }
  197. struct node_disposer {
  198. void operator()( node_type * pNode )
  199. {
  200. free_node( pNode );
  201. }
  202. };
  203. typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr;
  204. //@endcond
  205. public:
  206. /// Initializes empty queue
  207. MSQueue()
  208. {}
  209. /// Destructor clears the queue
  210. ~MSQueue()
  211. {}
  212. /// Enqueues \p val value into the queue.
  213. /**
  214. The function makes queue node in dynamic memory calling copy constructor for \p val
  215. and then it calls \p intrusive::MSQueue::enqueue.
  216. Returns \p true if success, \p false otherwise.
  217. */
  218. bool enqueue( value_type const& val )
  219. {
  220. scoped_node_ptr p( alloc_node(val));
  221. if ( base_class::enqueue( *p )) {
  222. p.release();
  223. return true;
  224. }
  225. return false;
  226. }
  227. /// Enqueues \p val in the queue, move semantics
  228. bool enqueue( value_type&& val )
  229. {
  230. scoped_node_ptr p( alloc_node_move( std::move( val )));
  231. if ( base_class::enqueue( *p )) {
  232. p.release();
  233. return true;
  234. }
  235. return false;
  236. }
  237. /// Enqueues data to the queue using a functor
  238. /**
  239. \p Func is a functor called to create node.
  240. The functor \p f takes one argument - a reference to a new node of type \ref value_type :
  241. \code
  242. cds::container::MSQueue< cds::gc::HP, Foo > myQueue;
  243. Bar bar;
  244. myQueue.enqueue_with( [&bar]( Foo& dest ) { dest = bar; } );
  245. \endcode
  246. */
  247. template <typename Func>
  248. bool enqueue_with( Func f )
  249. {
  250. scoped_node_ptr p( alloc_node());
  251. f( p->m_value );
  252. if ( base_class::enqueue( *p )) {
  253. p.release();
  254. return true;
  255. }
  256. return false;
  257. }
  258. /// Enqueues data of type \ref value_type constructed from <tt>std::forward<Args>(args)...</tt>
  259. template <typename... Args>
  260. bool emplace( Args&&... args )
  261. {
  262. scoped_node_ptr p( alloc_node_move( std::forward<Args>( args )... ));
  263. if ( base_class::enqueue( *p )) {
  264. p.release();
  265. return true;
  266. }
  267. return false;
  268. }
  269. /// Synonym for \p enqueue() function
  270. bool push( value_type const& val )
  271. {
  272. return enqueue( val );
  273. }
  274. /// Synonym for \p enqueue() function
  275. bool push( value_type&& val )
  276. {
  277. return enqueue( std::move( val ));
  278. }
  279. /// Synonym for \p enqueue_with() function
  280. template <typename Func>
  281. bool push_with( Func f )
  282. {
  283. return enqueue_with( f );
  284. }
  285. /// Dequeues a value from the queue
  286. /**
  287. If queue is not empty, the function returns \p true, \p dest contains copy of
  288. dequeued value. The assignment operator for type \ref value_type is invoked.
  289. If queue is empty, the function returns \p false, \p dest is unchanged.
  290. */
  291. bool dequeue( value_type& dest )
  292. {
  293. return dequeue_with( [&dest]( value_type& src ) {
  294. // TSan finds a race between this read of \p src and node_type constructor
  295. // I think, it is wrong
  296. CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
  297. dest = std::move( src );
  298. CDS_TSAN_ANNOTATE_IGNORE_READS_END;
  299. });
  300. }
  301. /// Dequeues a value using a functor
  302. /**
  303. \p Func is a functor called to copy dequeued value.
  304. The functor takes one argument - a reference to removed node:
  305. \code
  306. cds:container::MSQueue< cds::gc::HP, Foo > myQueue;
  307. Bar bar;
  308. myQueue.dequeue_with( [&bar]( Foo& src ) { bar = std::move( src );});
  309. \endcode
  310. The functor is called only if the queue is not empty.
  311. */
  312. template <typename Func>
  313. bool dequeue_with( Func f )
  314. {
  315. typename base_class::dequeue_result res;
  316. if ( base_class::do_dequeue( res )) {
  317. f( node_traits::to_value_ptr( *res.pNext )->m_value );
  318. base_class::dispose_result( res );
  319. return true;
  320. }
  321. return false;
  322. }
  323. /// Synonym for \p dequeue() function
  324. bool pop( value_type& dest )
  325. {
  326. return dequeue( dest );
  327. }
  328. /// Synonym for \p dequeue_with() function
  329. template <typename Func>
  330. bool pop_with( Func f )
  331. {
  332. return dequeue_with( f );
  333. }
  334. /// Clear the queue
  335. /**
  336. The function repeatedly calls \ref dequeue until it returns \p nullptr.
  337. */
  338. void clear()
  339. {
  340. base_class::clear();
  341. }
  342. /// Checks if the queue is empty
  343. bool empty() const
  344. {
  345. return base_class::empty();
  346. }
  347. /// Returns queue's item count (see \ref intrusive::MSQueue::size for explanation)
  348. /** \copydetails cds::intrusive::MSQueue::size()
  349. */
  350. size_t size() const
  351. {
  352. return base_class::size();
  353. }
  354. /// Returns reference to internal statistics
  355. const stat& statistics() const
  356. {
  357. return base_class::statistics();
  358. }
  359. };
  360. }} // namespace cds::container
  361. #endif // #ifndef CDSLIB_CONTAINER_MSQUEUE_H