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