3 #ifndef __CDS_CONTAINER_TREIBER_STACK_H
4 #define __CDS_CONTAINER_TREIBER_STACK_H
6 #include <memory> // unique_ptr
7 #include <cds/intrusive/treiber_stack.h>
8 #include <cds/container/details/base.h>
10 namespace cds { namespace container {
12 namespace treiber_stack {
13 /// Internal statistics
14 template <typename Counter = cds::atomicity::event_counter>
15 using stat = cds::intrusive::treiber_stack::stat < Counter >;
17 /// Dummy internal statistics
18 typedef cds::intrusive::treiber_stack::empty_stat empty_stat;
20 /// TreiberStack default type traits
24 typedef cds::backoff::Default back_off;
27 typedef CDS_DEFAULT_ALLOCATOR allocator;
29 /// C++ memory ordering model
31 Can be opt::v::relaxed_ordering (relaxed memory model, the default)
32 or opt::v::sequential_consistent (sequentially consisnent memory model).
34 typedef opt::v::relaxed_ordering memory_model;
36 /// Item counting feature; by default, disabled
37 typedef cds::atomicity::empty_item_counter item_counter;
39 /// Internal statistics (by default, no internal statistics)
41 Possible option value are: \ref treiber_stack::stat, \ref treiber_stack::empty_stat (the default),
42 user-provided class that supports treiber_stack::stat interface.
44 typedef empty_stat stat;
46 /** @name Elimination back-off traits
47 The following traits is used only if elimination enabled
51 /// Enable elimination back-off; by default, it is disabled
52 static CDS_CONSTEXPR_CONST bool enable_elimination = false;
54 /// Back-off strategy to wait for elimination, default is cds::backoff::delay<>
55 typedef cds::backoff::delay<> elimination_backoff;
57 /// Buffer type for elimination array
59 Possible types are \p opt::v::static_buffer, \p opt::v::dynamic_buffer.
60 The buffer can be any size: \p Exp2 template parameter of those classes can be \p false.
61 The size should be selected empirically for your application and hardware, there are no common rules for that.
62 Default is <tt> %opt::v::static_buffer< any_type, 4 > </tt>.
64 typedef opt::v::static_buffer< int, 4 > buffer;
66 /// Random engine to generate a random position in elimination array
67 typedef opt::v::c_rand random_engine;
69 /// Lock type used in elimination, default is cds::lock::Spin
70 typedef cds::lock::Spin lock_type;
75 /// Metafunction converting option list to \p TreiberStack traits
77 This is a wrapper for <tt> cds::opt::make_options< type_traits, Options...> </tt>
78 Supported \p Options are:
79 - opt::allocator - allocator (like \p std::allocator) used for allocating stack nodes. Default is \ref CDS_DEFAULT_ALLOCATOR
80 - opt::back_off - back-off strategy used. If the option is not specified, the \p cds::backoff::Default is used.
81 - opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
82 or \p opt::v::sequential_consistent (sequentially consisnent memory model).
83 - opt::item_counter - the type of item counting feature. Default is \p cds::atomicity::empty_item_counter
84 - opt::stat - the type to gather internal statistics.
85 Possible option value are: \p treiber_stack::stat, \p treiber_stack::empty_stat (the default),
86 user-provided class that supports \p treiber_stack::stat interface.
87 - opt::enable_elimination - enable elimination back-off for the stack. Default value is \p false.
89 If elimination back-off is enabled, additional options can be specified:
90 - opt::buffer - a buffer type for elimination array, see \p opt::v::static_buffer, \p opt::v::dynamic_buffer.
91 The buffer can be any size: \p Exp2 template parameter of those classes can be \p false.
92 The size should be selected empirically for your application and hardware, there are no common rules for that.
93 Default is <tt> %opt::v::static_buffer< any_type, 4 > </tt>.
94 - opt::random_engine - a random engine to generate a random position in elimination array.
95 Default is \p opt::v::c_rand.
96 - opt::elimination_backoff - back-off strategy to wait for elimination, default is \p cds::backoff::delay<>
97 - opt::lock_type - a lock type used in elimination back-off, default is \p cds::lock::Spin.
99 template <typename... Options>
101 # ifdef CDS_DOXYGEN_INVOKED
102 typedef implementation_defined type; ///< Metafunction result
104 typedef typename cds::opt::make_options<
105 typename cds::opt::find_type_traits< traits, Options... >::type
110 } // namespace treiber_stack
114 template <typename GC, typename T, typename Traits>
115 struct make_treiber_stack
118 typedef T value_type;
119 typedef Traits traits;
121 struct node_type: public cds::intrusive::treiber_stack::node< gc >
125 node_type( const value_type& val )
129 template <typename... Args>
130 node_type( Args&&... args )
131 : m_value( std::forward<Args>( args )... )
135 typedef typename traits::allocator::template rebind<node_type>::other allocator_type;
136 typedef cds::details::Allocator< node_type, allocator_type > cxx_allocator;
138 struct node_deallocator
140 void operator ()( node_type * pNode )
142 cxx_allocator().Delete( pNode );
146 struct intrusive_traits: public traits
148 typedef cds::intrusive::treiber_stack::base_hook< cds::opt::gc<gc> > hook;
149 typedef node_deallocator disposer;
150 static CDS_CONSTEXPR_CONST opt::link_check_type link_checker = cds::intrusive::treiber_stack::traits::link_checker;
153 // Result of metafunction
154 typedef intrusive::TreiberStack< gc, node_type, intrusive_traits > type;
156 } // namespace details
159 /// Treiber's stack algorithm
160 /** @ingroup cds_nonintrusive_stack
161 It is non-intrusive version of Treiber's stack algorithm based on intrusive implementation
162 intrusive::TreiberStack.
165 - \p GC - garbage collector type: gc::HP, gc::PTB
166 - \p T - type stored in the stack. It should be default-constructible, copy-constructible, assignable type.
167 - \p Traits - stack traits, default is \p treiber_stack::traits. You can use \p treiber_stack::make_traits
168 metafunction to make your traits or just derive your traits from \p %treiber_stack::traits:
170 struct myTraits: public cds::container::treiber_stack::traits {
171 typedef cds::container::treiber_stack::stat<> stat;
173 typedef cds::container::TreiberStack< cds::gc::HP, Foo, myTraits > myStack;
179 typename Traits = treiber_stack::traits
183 #ifdef CDS_DOXYGEN_INVOKED
184 intrusive::TreiberStack< GC, cds::intrusive::treiber_stack::node< T >, Traits >
186 details::make_treiber_stack< GC, T, Traits >::type
190 typedef details::make_treiber_stack< GC, T, Traits > maker;
191 typedef typename maker::type base_class;
195 /// Rebind template arguments
196 template <typename GC2, typename T2, typename Traits2>
198 typedef TreiberStack< GC2, T2, Traits2> other ; ///< Rebinding result
202 typedef T value_type ; ///< Value type stored in the stack
203 typedef typename base_class::gc gc ; ///< Garbage collector used
204 typedef typename base_class::back_off back_off ; ///< Back-off strategy used
205 typedef typename maker::allocator_type allocator_type ; ///< Allocator type used for allocating/deallocating the nodes
206 typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_order option
207 typedef typename base_class::stat stat ; ///< Internal statistics policy used
210 typedef typename maker::node_type node_type ; ///< stack node type (derived from intrusive::treiber_stack::node)
213 typedef typename maker::cxx_allocator cxx_allocator;
214 typedef typename maker::node_deallocator node_deallocator;
219 static node_type * alloc_node( const value_type& val )
221 return cxx_allocator().New( val );
223 template <typename... Args>
224 static node_type * alloc_node_move( Args&&... args )
226 return cxx_allocator().MoveNew( std::forward<Args>( args )... );
229 static void free_node( node_type * p )
231 node_deallocator()( p );
233 static void retire_node( node_type * p )
235 gc::template retire<typename base_class::disposer>( p );
238 struct node_disposer {
239 void operator()( node_type * pNode )
244 typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr;
248 /// Constructs empty stack
252 /// Constructs empty stack and initializes elimination back-off data
254 This form should be used if you use elimination back-off with dynamically allocated collision array, i.e
255 \p Options... contains cds::opt::buffer< cds::opt::v::dynamic_buffer >.
256 \p nCollisionCapacity parameter specifies the capacity of collision array.
258 TreiberStack( size_t nCollisionCapacity )
259 : base_class( nCollisionCapacity )
262 /// \p %TreiberStack is not copy-constructible
263 TreiberStack( TreiberStack const& ) = delete;
265 /// Clears the stack on destruction
269 /// Push the item \p val on the stack
270 bool push( value_type const& val )
272 scoped_node_ptr p( alloc_node(val));
273 if ( base_class::push( *p )) {
280 /// Pushes data of type \ref value_type created from <tt>std::forward<Args>(args)...</tt>
281 template <typename... Args>
282 bool emplace( Args&&... args )
284 scoped_node_ptr p( alloc_node_move( std::forward<Args>(args)...));
285 if ( base_class::push( *p )) {
292 /// Pop an item from the stack
294 The value of popped item is stored in \p val.
295 On success functions returns \p true, \p val contains value popped from the stack.
296 If stack is empty the function returns \p false, \p val is unchanged.
298 bool pop( value_type& val )
300 node_type * p = base_class::pop();
310 /// Check if stack is empty
313 return base_class::empty();
322 /// Returns stack's item count
324 The value returned depends on opt::item_counter option. For atomicity::empty_item_counter,
325 this function always returns 0.
327 <b>Warning</b>: even if you use real item counter and it returns 0, this fact is not mean that the stack
328 is empty. To check emptyness use \ref empty() method.
332 return base_class::size();
335 /// Returns reference to internal statistics
336 stat const& statistics() const
338 return base_class::statistics();
342 }} // namespace cds::container
345 #endif // #ifndef __CDS_CONTAINER_TREIBER_STACK_H