fcstack.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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_FCSTACK_H
  6. #define CDSLIB_CONTAINER_FCSTACK_H
  7. #include <cds/algo/flat_combining.h>
  8. #include <cds/algo/elimination_opt.h>
  9. #include <stack>
  10. namespace cds { namespace container {
  11. /// FCStack related definitions
  12. /** @ingroup cds_nonintrusive_helper
  13. */
  14. namespace fcstack {
  15. /// FCStack internal statistics
  16. template <typename Counter = cds::atomicity::event_counter >
  17. struct stat: public cds::algo::flat_combining::stat<Counter>
  18. {
  19. typedef cds::algo::flat_combining::stat<Counter> flat_combining_stat; ///< Flat-combining statistics
  20. typedef typename flat_combining_stat::counter_type counter_type; ///< Counter type
  21. counter_type m_nPush ; ///< Count of push operations
  22. counter_type m_nPushMove ; ///< Count of push operations with move semantics
  23. counter_type m_nPop ; ///< Count of success pop operations
  24. counter_type m_nFailedPop; ///< Count of failed pop operations (pop from empty stack)
  25. counter_type m_nCollided ; ///< How many pairs of push/pop were collided, if elimination is enabled
  26. //@cond
  27. void onPush() { ++m_nPush; }
  28. void onPushMove() { ++m_nPushMove; }
  29. void onPop( bool bFailed ) { if ( bFailed ) ++m_nFailedPop; else ++m_nPop; }
  30. void onCollide() { ++m_nCollided; }
  31. //@endcond
  32. };
  33. /// FCStack dummy statistics, no overhead
  34. struct empty_stat: public cds::algo::flat_combining::empty_stat
  35. {
  36. //@cond
  37. void onPush() {}
  38. void onPushMove() {}
  39. void onPop(bool) {}
  40. void onCollide() {}
  41. //@endcond
  42. };
  43. /// FCStack type traits
  44. struct traits: public cds::algo::flat_combining::traits
  45. {
  46. typedef empty_stat stat; ///< Internal statistics
  47. static constexpr const bool enable_elimination = false; ///< Enable \ref cds_elimination_description "elimination"
  48. };
  49. /// Metafunction converting option list to traits
  50. /**
  51. \p Options are:
  52. - any \p cds::algo::flat_combining::make_traits options
  53. - \p opt::stat - internal statistics, possible type: \p fcstack::stat, \p fcstack::empty_stat (the default)
  54. - \p opt::enable_elimination - enable/disable operation \ref cds_elimination_description "elimination"
  55. By default, the elimination is disabled.
  56. */
  57. template <typename... Options>
  58. struct make_traits {
  59. # ifdef CDS_DOXYGEN_INVOKED
  60. typedef implementation_defined type ; ///< Metafunction result
  61. # else
  62. typedef typename cds::opt::make_options<
  63. typename cds::opt::find_type_traits< traits, Options... >::type
  64. ,Options...
  65. >::type type;
  66. # endif
  67. };
  68. } // namespace fcstack
  69. /// Flat-combining stack
  70. /**
  71. @ingroup cds_nonintrusive_stack
  72. @ingroup cds_flat_combining_container
  73. \ref cds_flat_combining_description "Flat combining" sequential stack.
  74. Template parameters:
  75. - \p T - a value type stored in the stack
  76. - \p Stack - sequential stack implementation, default is \p std::stack<T>
  77. - \p Trats - type traits of flat combining, default is \p fcstack::traits
  78. \p fcstack::make_traits metafunction can be used to construct specialized \p %fcstack::traits
  79. */
  80. template <typename T,
  81. class Stack = std::stack<T>,
  82. typename Traits = fcstack::traits
  83. >
  84. class FCStack
  85. #ifndef CDS_DOXYGEN_INVOKED
  86. : public cds::algo::flat_combining::container
  87. #endif
  88. {
  89. public:
  90. typedef T value_type; ///< Value type
  91. typedef Stack stack_type; ///< Sequential stack class
  92. typedef Traits traits; ///< Stack traits
  93. typedef typename traits::stat stat; ///< Internal statistics type
  94. static constexpr const bool c_bEliminationEnabled = traits::enable_elimination; ///< \p true if elimination is enabled
  95. protected:
  96. //@cond
  97. /// Stack operation IDs
  98. enum fc_operation {
  99. op_push = cds::algo::flat_combining::req_Operation, ///< Push
  100. op_push_move, ///< Push (move semantics)
  101. op_pop, ///< Pop
  102. op_clear, ///< Clear
  103. op_empty ///< Empty
  104. };
  105. /// Flat combining publication list record
  106. struct fc_record: public cds::algo::flat_combining::publication_record
  107. {
  108. union {
  109. value_type const * pValPush; ///< Value to push
  110. value_type * pValPop; ///< Pop destination
  111. };
  112. bool bEmpty; ///< \p true if the stack is empty
  113. };
  114. //@endcond
  115. /// Flat combining kernel
  116. typedef cds::algo::flat_combining::kernel< fc_record, traits > fc_kernel;
  117. protected:
  118. //@cond
  119. mutable fc_kernel m_FlatCombining;
  120. stack_type m_Stack;
  121. //@endcond
  122. public:
  123. /// Initializes empty stack object
  124. FCStack()
  125. {}
  126. /// Initializes empty stack object and gives flat combining parameters
  127. FCStack(
  128. unsigned int nCompactFactor ///< Flat combining: publication list compacting factor
  129. ,unsigned int nCombinePassCount ///< Flat combining: number of combining passes for combiner thread
  130. )
  131. : m_FlatCombining( nCompactFactor, nCombinePassCount )
  132. {}
  133. /// Inserts a new element at the top of stack
  134. /**
  135. The content of the new element initialized to a copy of \p val.
  136. */
  137. bool push( value_type const& val )
  138. {
  139. auto pRec = m_FlatCombining.acquire_record();
  140. pRec->pValPush = &val;
  141. constexpr_if ( c_bEliminationEnabled )
  142. m_FlatCombining.batch_combine( op_push, pRec, *this );
  143. else
  144. m_FlatCombining.combine( op_push, pRec, *this );
  145. assert( pRec->is_done());
  146. m_FlatCombining.release_record( pRec );
  147. m_FlatCombining.internal_statistics().onPush();
  148. return true;
  149. }
  150. /// Inserts a new element at the top of stack (move semantics)
  151. /**
  152. The content of the new element initialized to a copy of \p val.
  153. */
  154. bool push( value_type&& val )
  155. {
  156. auto pRec = m_FlatCombining.acquire_record();
  157. pRec->pValPush = &val;
  158. constexpr_if ( c_bEliminationEnabled )
  159. m_FlatCombining.batch_combine( op_push_move, pRec, *this );
  160. else
  161. m_FlatCombining.combine( op_push_move, pRec, *this );
  162. assert( pRec->is_done());
  163. m_FlatCombining.release_record( pRec );
  164. m_FlatCombining.internal_statistics().onPushMove();
  165. return true;
  166. }
  167. /// Removes the element on top of the stack
  168. /**
  169. \p val takes a copy of top element
  170. */
  171. bool pop( value_type& val )
  172. {
  173. auto pRec = m_FlatCombining.acquire_record();
  174. pRec->pValPop = &val;
  175. constexpr_if ( c_bEliminationEnabled )
  176. m_FlatCombining.batch_combine( op_pop, pRec, *this );
  177. else
  178. m_FlatCombining.combine( op_pop, pRec, *this );
  179. assert( pRec->is_done());
  180. m_FlatCombining.release_record( pRec );
  181. m_FlatCombining.internal_statistics().onPop( pRec->bEmpty );
  182. return !pRec->bEmpty;
  183. }
  184. /// Clears the stack
  185. void clear()
  186. {
  187. auto pRec = m_FlatCombining.acquire_record();
  188. constexpr_if ( c_bEliminationEnabled )
  189. m_FlatCombining.batch_combine( op_clear, pRec, *this );
  190. else
  191. m_FlatCombining.combine( op_clear, pRec, *this );
  192. assert( pRec->is_done());
  193. m_FlatCombining.release_record( pRec );
  194. }
  195. /// Exclusive access to underlying stack object
  196. /**
  197. The functor \p f can do any operation with underlying \p stack_type in exclusive mode.
  198. For example, you can iterate over the stack.
  199. \p Func signature is:
  200. \code
  201. void f( stack_type& stack );
  202. \endcode
  203. */
  204. template <typename Func>
  205. void apply( Func f )
  206. {
  207. auto& stack = m_Stack;
  208. m_FlatCombining.invoke_exclusive( [&stack, &f]() { f( stack ); } );
  209. }
  210. /// Exclusive access to underlying stack object
  211. /**
  212. The functor \p f can do any operation with underlying \p stack_type in exclusive mode.
  213. For example, you can iterate over the stack.
  214. \p Func signature is:
  215. \code
  216. void f( stack_type const& stack );
  217. \endcode
  218. */
  219. template <typename Func>
  220. void apply( Func f ) const
  221. {
  222. auto const& stack = m_Stack;
  223. m_FlatCombining.invoke_exclusive( [&stack, &f]() { f( stack ); } );
  224. }
  225. /// Returns the number of elements in the stack.
  226. /**
  227. Note that <tt>size() == 0</tt> is not mean that the stack is empty because
  228. combining record can be in process.
  229. To check emptiness use \ref empty() function.
  230. */
  231. size_t size() const
  232. {
  233. return m_Stack.size();
  234. }
  235. /// Checks if the stack is empty
  236. /**
  237. If the combining is in process the function waits while combining done.
  238. */
  239. bool empty()
  240. {
  241. auto pRec = m_FlatCombining.acquire_record();
  242. constexpr_if ( c_bEliminationEnabled )
  243. m_FlatCombining.batch_combine( op_empty, pRec, *this );
  244. else
  245. m_FlatCombining.combine( op_empty, pRec, *this );
  246. assert( pRec->is_done());
  247. m_FlatCombining.release_record( pRec );
  248. return pRec->bEmpty;
  249. }
  250. /// Internal statistics
  251. stat const& statistics() const
  252. {
  253. return m_FlatCombining.statistics();
  254. }
  255. public: // flat combining cooperation, not for direct use!
  256. //@cond
  257. /// Flat combining supporting function. Do not call it directly!
  258. /**
  259. The function is called by \ref cds::algo::flat_combining::kernel "flat combining kernel"
  260. object if the current thread becomes a combiner. Invocation of the function means that
  261. the stack should perform an action recorded in \p pRec.
  262. */
  263. void fc_apply( fc_record * pRec )
  264. {
  265. assert( pRec );
  266. switch ( pRec->op()) {
  267. case op_push:
  268. assert( pRec->pValPush );
  269. m_Stack.push( *(pRec->pValPush ));
  270. break;
  271. case op_push_move:
  272. assert( pRec->pValPush );
  273. m_Stack.push( std::move( *(pRec->pValPush )));
  274. break;
  275. case op_pop:
  276. assert( pRec->pValPop );
  277. pRec->bEmpty = m_Stack.empty();
  278. if ( !pRec->bEmpty ) {
  279. *(pRec->pValPop) = std::move( m_Stack.top());
  280. m_Stack.pop();
  281. }
  282. break;
  283. case op_clear:
  284. while ( !m_Stack.empty())
  285. m_Stack.pop();
  286. break;
  287. case op_empty:
  288. pRec->bEmpty = m_Stack.empty();
  289. break;
  290. default:
  291. assert(false);
  292. break;
  293. }
  294. }
  295. /// Batch-processing flat combining
  296. void fc_process( typename fc_kernel::iterator itBegin, typename fc_kernel::iterator itEnd )
  297. {
  298. typedef typename fc_kernel::iterator fc_iterator;
  299. for ( fc_iterator it = itBegin, itPrev = itEnd; it != itEnd; ++it ) {
  300. switch ( it->op( atomics::memory_order_acquire )) {
  301. case op_push:
  302. case op_push_move:
  303. case op_pop:
  304. if ( itPrev != itEnd && collide( *itPrev, *it ))
  305. itPrev = itEnd;
  306. else
  307. itPrev = it;
  308. break;
  309. }
  310. }
  311. }
  312. //@endcond
  313. private:
  314. //@cond
  315. bool collide( fc_record& rec1, fc_record& rec2 )
  316. {
  317. switch ( rec1.op()) {
  318. case op_push:
  319. if ( rec2.op() == op_pop ) {
  320. assert(rec1.pValPush);
  321. assert(rec2.pValPop);
  322. *rec2.pValPop = *rec1.pValPush;
  323. rec2.bEmpty = false;
  324. goto collided;
  325. }
  326. break;
  327. case op_push_move:
  328. if ( rec2.op() == op_pop ) {
  329. assert(rec1.pValPush);
  330. assert(rec2.pValPop);
  331. *rec2.pValPop = std::move( *rec1.pValPush );
  332. rec2.bEmpty = false;
  333. goto collided;
  334. }
  335. break;
  336. case op_pop:
  337. switch ( rec2.op()) {
  338. case op_push:
  339. case op_push_move:
  340. return collide( rec2, rec1 );
  341. }
  342. }
  343. return false;
  344. collided:
  345. m_FlatCombining.operation_done( rec1 );
  346. m_FlatCombining.operation_done( rec2 );
  347. m_FlatCombining.internal_statistics().onCollide();
  348. return true;
  349. }
  350. //@endcond
  351. };
  352. }} // namespace cds::container
  353. #endif // #ifndef CDSLIB_CONTAINER_FCSTACK_H