1 // Copyright (C) 2008, 2009, 2010, 2011 Tim Blechmann
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 #ifndef BOOST_LOCKFREE_STACK_HPP_INCLUDED
8 #define BOOST_LOCKFREE_STACK_HPP_INCLUDED
10 #include <boost/checked_delete.hpp>
11 #include <boost/integer_traits.hpp>
12 #include <boost/noncopyable.hpp>
13 #include <boost/static_assert.hpp>
14 #include <boost/tuple/tuple.hpp>
15 #include <boost/type_traits/has_trivial_assign.hpp>
16 #include <boost/type_traits/has_trivial_destructor.hpp>
18 #include <boost/lockfree/detail/atomic.hpp>
19 #include <boost/lockfree/detail/copy_payload.hpp>
20 #include <boost/lockfree/detail/freelist.hpp>
21 #include <boost/lockfree/detail/parameter.hpp>
22 #include <boost/lockfree/detail/tagged_ptr.hpp>
28 typedef parameter::parameters
<boost::parameter::optional
<tag::allocator
>,
29 boost::parameter::optional
<tag::capacity
>
34 /** The stack class provides a multi-writer/multi-reader stack, pushing and popping is lock-free,
35 * construction/destruction has to be synchronized. It uses a freelist for memory management,
36 * freed nodes are pushed to the freelist and not returned to the OS before the stack is destroyed.
40 * - \c boost::lockfree::fixed_sized<>, defaults to \c boost::lockfree::fixed_sized<false> <br>
41 * Can be used to completely disable dynamic memory allocations during push in order to ensure lockfree behavior.<br>
42 * If the data structure is configured as fixed-sized, the internal nodes are stored inside an array and they are addressed
43 * by array indexing. This limits the possible size of the stack to the number of elements that can be addressed by the index
44 * type (usually 2**16-2), but on platforms that lack double-width compare-and-exchange instructions, this is the best way
45 * to achieve lock-freedom.
47 * - \c boost::lockfree::capacity<>, optional <br>
48 * If this template argument is passed to the options, the size of the stack is set at compile-time. <br>
49 * It this option implies \c fixed_sized<true>
51 * - \c boost::lockfree::allocator<>, defaults to \c boost::lockfree::allocator<std::allocator<void>> <br>
52 * Specifies the allocator that is used for the internal freelist
55 * - T must have a copy constructor
57 #ifndef BOOST_DOXYGEN_INVOKED
59 class A0
= boost::parameter::void_
,
60 class A1
= boost::parameter::void_
,
61 class A2
= boost::parameter::void_
>
63 template <typename T
, ...Options
>
69 #ifndef BOOST_DOXYGEN_INVOKED
70 BOOST_STATIC_ASSERT(boost::has_trivial_assign
<T
>::value
);
71 BOOST_STATIC_ASSERT(boost::has_trivial_destructor
<T
>::value
);
73 typedef typename
detail::stack_signature::bind
<A0
, A1
, A2
>::type bound_args
;
75 static const bool has_capacity
= detail::extract_capacity
<bound_args
>::has_capacity
;
76 static const size_t capacity
= detail::extract_capacity
<bound_args
>::capacity
;
77 static const bool fixed_sized
= detail::extract_fixed_sized
<bound_args
>::value
;
78 static const bool node_based
= !(has_capacity
|| fixed_sized
);
79 static const bool compile_time_sized
= has_capacity
;
87 typedef typename
detail::select_tagged_handle
<node
, node_based
>::handle_type handle_t
;
92 typedef typename
detail::extract_allocator
<bound_args
, node
>::type node_allocator
;
93 typedef typename
detail::select_freelist
<node
, node_allocator
, compile_time_sized
, fixed_sized
, capacity
>::type pool_t
;
94 typedef typename
pool_t::tagged_node_handle tagged_node_handle
;
96 // check compile-time capacity
97 BOOST_STATIC_ASSERT((mpl::if_c
<has_capacity
,
98 mpl::bool_
<capacity
- 1 < boost::integer_traits
<boost::uint16_t>::const_max
>,
102 struct implementation_defined
104 typedef node_allocator allocator
;
105 typedef std::size_t size_type
;
111 typedef T value_type
;
112 typedef typename
implementation_defined::allocator allocator
;
113 typedef typename
implementation_defined::size_type size_type
;
116 * \return true, if implementation is lock-free.
118 * \warning It only checks, if the top stack node and the freelist can be modified in a lock-free manner.
119 * On most platforms, the whole implementation is lock-free, if this is true. Using c++0x-style atomics,
120 * there is no possibility to provide a completely accurate implementation, because one would need to test
121 * every internal node, which is impossible if further nodes will be allocated from the operating system.
124 bool is_lock_free (void) const
126 return tos
.is_lock_free() && pool
.is_lock_free();
132 pool(node_allocator(), has_capacity
? capacity
: 0)
134 BOOST_STATIC_ASSERT(has_capacity
);
138 template <typename U
>
139 explicit stack(typename
node_allocator::template rebind
<U
>::other
const & alloc
):
140 pool(alloc
, has_capacity
? capacity
: 0)
142 BOOST_STATIC_ASSERT(has_capacity
);
146 explicit stack(allocator
const & alloc
):
147 pool(alloc
, has_capacity
? capacity
: 0)
149 BOOST_STATIC_ASSERT(has_capacity
);
154 //! Construct stack, allocate n nodes for the freelist.
156 explicit stack(size_type n
):
157 pool(node_allocator(), n
)
159 BOOST_STATIC_ASSERT(!has_capacity
);
163 template <typename U
>
164 stack(size_type n
, typename
node_allocator::template rebind
<U
>::other
const & alloc
):
167 BOOST_STATIC_ASSERT(!has_capacity
);
172 /** Allocate n nodes for freelist
174 * \pre only valid if no capacity<> argument given
175 * \note thread-safe, may block if memory allocator blocks
178 void reserve(size_type n
)
180 BOOST_STATIC_ASSERT(!has_capacity
);
184 /** Allocate n nodes for freelist
186 * \pre only valid if no capacity<> argument given
187 * \note not thread-safe, may block if memory allocator blocks
190 void reserve_unsafe(size_type n
)
192 BOOST_STATIC_ASSERT(!has_capacity
);
193 pool
.reserve_unsafe(n
);
196 /** Destroys stack, free all nodes from freelist.
198 * \note not thread-safe
204 while(unsynchronized_pop(dummy
))
209 #ifndef BOOST_DOXYGEN_INVOKED
210 void initialize(void)
212 tos
.store(tagged_node_handle(pool
.null_handle(), 0));
215 void link_nodes_atomic(node
* new_top_node
, node
* end_node
)
217 tagged_node_handle old_tos
= tos
.load(detail::memory_order_relaxed
);
219 tagged_node_handle
new_tos (pool
.get_handle(new_top_node
), old_tos
.get_tag());
220 end_node
->next
= pool
.get_handle(old_tos
);
222 if (tos
.compare_exchange_weak(old_tos
, new_tos
))
227 void link_nodes_unsafe(node
* new_top_node
, node
* end_node
)
229 tagged_node_handle old_tos
= tos
.load(detail::memory_order_relaxed
);
231 tagged_node_handle
new_tos (pool
.get_handle(new_top_node
), old_tos
.get_tag());
232 end_node
->next
= pool
.get_pointer(old_tos
);
234 tos
.store(new_tos
, memory_order_relaxed
);
237 template <bool Threadsafe
, bool Bounded
, typename ConstIterator
>
238 tuple
<node
*, node
*> prepare_node_list(ConstIterator begin
, ConstIterator end
, ConstIterator
& ret
)
240 ConstIterator it
= begin
;
241 node
* end_node
= pool
.template construct
<Threadsafe
, Bounded
>(*it
++);
242 if (end_node
== NULL
) {
244 return make_tuple
<node
*, node
*>(NULL
, NULL
);
247 node
* new_top_node
= end_node
;
248 end_node
->next
= NULL
;
252 for (; it
!= end
; ++it
) {
253 node
* newnode
= pool
.template construct
<Threadsafe
, Bounded
>(*it
);
256 newnode
->next
= new_top_node
;
257 new_top_node
= newnode
;
260 for (node
* current_node
= new_top_node
; current_node
!= NULL
;) {
261 node
* next
= current_node
->next
;
262 pool
.template destruct
<Threadsafe
>(current_node
);
268 return make_tuple(new_top_node
, end_node
);
273 /** Pushes object t to the stack.
275 * \post object will be pushed to the stack, if internal node can be allocated
276 * \returns true, if the push operation is successful.
278 * \note Thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated
279 * from the OS. This may not be lock-free.
280 * \throws if memory allocator throws
282 bool push(T
const & v
)
284 return do_push
<false>(v
);
287 /** Pushes object t to the stack.
289 * \post object will be pushed to the stack, if internal node can be allocated
290 * \returns true, if the push operation is successful.
292 * \note Thread-safe and non-blocking. If internal memory pool is exhausted, the push operation will fail
294 bool bounded_push(T
const & v
)
296 return do_push
<true>(v
);
299 #ifndef BOOST_DOXYGEN_INVOKED
301 template <bool Bounded
>
302 bool do_push(T
const & v
)
304 node
* newnode
= pool
.template construct
<true, Bounded
>(v
);
308 link_nodes_atomic(newnode
, newnode
);
312 template <bool Bounded
, typename ConstIterator
>
313 ConstIterator
do_push(ConstIterator begin
, ConstIterator end
)
319 tie(new_top_node
, end_node
) = prepare_node_list
<true, Bounded
>(begin
, end
, ret
);
321 link_nodes_atomic(new_top_node
, end_node
);
329 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated.
331 * \return iterator to the first element, which has not been pushed
333 * \note Operation is applied atomically
334 * \note Thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated
335 * from the OS. This may not be lock-free.
336 * \throws if memory allocator throws
338 template <typename ConstIterator
>
339 ConstIterator
push(ConstIterator begin
, ConstIterator end
)
341 return do_push
<false, ConstIterator
>(begin
, end
);
344 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated.
346 * \return iterator to the first element, which has not been pushed
348 * \note Operation is applied atomically
349 * \note Thread-safe and non-blocking. If internal memory pool is exhausted, the push operation will fail
350 * \throws if memory allocator throws
352 template <typename ConstIterator
>
353 ConstIterator
bounded_push(ConstIterator begin
, ConstIterator end
)
355 return do_push
<true, ConstIterator
>(begin
, end
);
359 /** Pushes object t to the stack.
361 * \post object will be pushed to the stack, if internal node can be allocated
362 * \returns true, if the push operation is successful.
364 * \note Not thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated
365 * from the OS. This may not be lock-free.
366 * \throws if memory allocator throws
368 bool unsynchronized_push(T
const & v
)
370 node
* newnode
= pool
.template construct
<false, false>(v
);
374 link_nodes_unsafe(newnode
, newnode
);
378 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated.
380 * \return iterator to the first element, which has not been pushed
382 * \note Not thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated
383 * from the OS. This may not be lock-free.
384 * \throws if memory allocator throws
386 template <typename ConstIterator
>
387 ConstIterator
unsynchronized_push(ConstIterator begin
, ConstIterator end
)
393 tie(new_top_node
, end_node
) = prepare_node_list
<false, false>(begin
, end
, ret
);
395 link_nodes_unsafe(new_top_node
, end_node
);
401 /** Pops object from stack.
403 * \post if pop operation is successful, object will be copied to ret.
404 * \returns true, if the pop operation is successful, false if stack was empty.
406 * \note Thread-safe and non-blocking
414 /** Pops object from stack.
416 * \pre type T must be convertible to U
417 * \post if pop operation is successful, object will be copied to ret.
418 * \returns true, if the pop operation is successful, false if stack was empty.
420 * \note Thread-safe and non-blocking
423 template <typename U
>
426 BOOST_STATIC_ASSERT((boost::is_convertible
<T
, U
>::value
));
427 tagged_node_handle old_tos
= tos
.load(detail::memory_order_consume
);
430 node
* old_tos_pointer
= pool
.get_pointer(old_tos
);
431 if (!old_tos_pointer
)
434 tagged_node_handle
new_tos(old_tos_pointer
->next
, old_tos
.get_tag() + 1);
436 if (tos
.compare_exchange_weak(old_tos
, new_tos
)) {
437 detail::copy_payload(old_tos_pointer
->v
, ret
);
438 pool
.template destruct
<true>(old_tos
);
445 /** Pops object from stack.
447 * \post if pop operation is successful, object will be copied to ret.
448 * \returns true, if the pop operation is successful, false if stack was empty.
450 * \note Not thread-safe, but non-blocking
453 bool unsynchronized_pop(T
& ret
)
455 return unsynchronized_pop
<T
>(ret
);
458 /** Pops object from stack.
460 * \pre type T must be convertible to U
461 * \post if pop operation is successful, object will be copied to ret.
462 * \returns true, if the pop operation is successful, false if stack was empty.
464 * \note Not thread-safe, but non-blocking
467 template <typename U
>
468 bool unsynchronized_pop(U
& ret
)
470 BOOST_STATIC_ASSERT((boost::is_convertible
<T
, U
>::value
));
471 tagged_node_handle old_tos
= tos
.load(detail::memory_order_relaxed
);
472 node
* old_tos_pointer
= pool
.get_pointer(old_tos
);
474 if (!pool
.get_pointer(old_tos
))
477 node
* new_tos_ptr
= pool
.get_pointer(old_tos_pointer
->next
);
478 tagged_node_handle
new_tos(pool
.get_handle(new_tos_ptr
), old_tos
.get_tag() + 1);
480 tos
.store(new_tos
, memory_order_relaxed
);
481 detail::copy_payload(old_tos_pointer
->v
, ret
);
482 pool
.template destruct
<false>(old_tos
);
487 * \return true, if stack is empty.
489 * \note It only guarantees that at some point during the execution of the function the stack has been empty.
490 * It is rarely practical to use this value in program logic, because the the stack can be modified by other threads.
492 bool empty(void) const
494 return pool
.get_pointer(tos
.load()) == NULL
;
498 #ifndef BOOST_DOXYGEN_INVOKED
499 detail::atomic
<tagged_node_handle
> tos
;
501 static const int padding_size
= BOOST_LOCKFREE_CACHELINE_BYTES
- sizeof(tagged_node_handle
);
502 char padding
[padding_size
];
508 } /* namespace lockfree */
509 } /* namespace boost */
511 #endif /* BOOST_LOCKFREE_STACK_HPP_INCLUDED */