scide: implement selectionLength for openDocument
[supercollider.git] / external_libraries / boost-lockfree / boost / lockfree / stack.hpp
blob09a788a038ee8354133e9cb33d04c34a3628b169
1 // Copyright (C) 2008, 2009, 2010, 2011 Tim Blechmann
2 //
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>
24 namespace boost {
25 namespace lockfree {
26 namespace detail {
28 typedef parameter::parameters<boost::parameter::optional<tag::allocator>,
29 boost::parameter::optional<tag::capacity>
30 > stack_signature;
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.
38 * \b Policies:
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
54 * \b Requirements:
55 * - T must have a copy constructor
56 * */
57 #ifndef BOOST_DOXYGEN_INVOKED
58 template <typename T,
59 class A0 = boost::parameter::void_,
60 class A1 = boost::parameter::void_,
61 class A2 = boost::parameter::void_>
62 #else
63 template <typename T, ...Options>
64 #endif
65 class stack:
66 boost::noncopyable
68 private:
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;
81 struct node
83 node(T const & val):
84 v(val)
87 typedef typename detail::select_tagged_handle<node, node_based>::handle_type handle_t;
88 handle_t next;
89 const T v;
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>,
99 mpl::true_
100 >::type::value));
102 struct implementation_defined
104 typedef node_allocator allocator;
105 typedef std::size_t size_type;
108 #endif
110 public:
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.
123 * */
124 bool is_lock_free (void) const
126 return tos.is_lock_free() && pool.is_lock_free();
129 //! Construct stack
130 // @{
131 stack(void):
132 pool(node_allocator(), has_capacity ? capacity : 0)
134 BOOST_STATIC_ASSERT(has_capacity);
135 initialize();
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);
143 initialize();
146 explicit stack(allocator const & alloc):
147 pool(alloc, has_capacity ? capacity : 0)
149 BOOST_STATIC_ASSERT(has_capacity);
150 initialize();
152 // @}
154 //! Construct stack, allocate n nodes for the freelist.
155 // @{
156 explicit stack(size_type n):
157 pool(node_allocator(), n)
159 BOOST_STATIC_ASSERT(!has_capacity);
160 initialize();
163 template <typename U>
164 stack(size_type n, typename node_allocator::template rebind<U>::other const & alloc):
165 pool(alloc, n)
167 BOOST_STATIC_ASSERT(!has_capacity);
168 initialize();
170 // @}
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
177 * */
178 void reserve(size_type n)
180 BOOST_STATIC_ASSERT(!has_capacity);
181 pool.reserve(n);
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
189 * */
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
200 * */
201 ~stack(void)
203 T dummy;
204 while(unsynchronized_pop(dummy))
208 private:
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);
218 for (;;) {
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))
223 break;
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) {
243 ret = begin;
244 return make_tuple<node*, node*>(NULL, NULL);
247 node * new_top_node = end_node;
248 end_node->next = NULL;
250 try {
251 /* link nodes */
252 for (; it != end; ++it) {
253 node * newnode = pool.template construct<Threadsafe, Bounded>(*it);
254 if (newnode == NULL)
255 break;
256 newnode->next = new_top_node;
257 new_top_node = newnode;
259 } catch (...) {
260 for (node * current_node = new_top_node; current_node != NULL;) {
261 node * next = current_node->next;
262 pool.template destruct<Threadsafe>(current_node);
263 current_node = next;
265 throw;
267 ret = it;
268 return make_tuple(new_top_node, end_node);
270 #endif
272 public:
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
281 * */
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
293 * */
294 bool bounded_push(T const & v)
296 return do_push<true>(v);
299 #ifndef BOOST_DOXYGEN_INVOKED
300 private:
301 template <bool Bounded>
302 bool do_push(T const & v)
304 node * newnode = pool.template construct<true, Bounded>(v);
305 if (newnode == 0)
306 return false;
308 link_nodes_atomic(newnode, newnode);
309 return true;
312 template <bool Bounded, typename ConstIterator>
313 ConstIterator do_push(ConstIterator begin, ConstIterator end)
315 node * new_top_node;
316 node * end_node;
317 ConstIterator ret;
319 tie(new_top_node, end_node) = prepare_node_list<true, Bounded>(begin, end, ret);
320 if (new_top_node)
321 link_nodes_atomic(new_top_node, end_node);
323 return ret;
326 public:
327 #endif
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
367 * */
368 bool unsynchronized_push(T const & v)
370 node * newnode = pool.template construct<false, false>(v);
371 if (newnode == 0)
372 return false;
374 link_nodes_unsafe(newnode, newnode);
375 return true;
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)
389 node * new_top_node;
390 node * end_node;
391 ConstIterator ret;
393 tie(new_top_node, end_node) = prepare_node_list<false, false>(begin, end, ret);
394 if (new_top_node)
395 link_nodes_unsafe(new_top_node, end_node);
397 return ret;
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
408 * */
409 bool pop(T & ret)
411 return pop<T>(ret);
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
422 * */
423 template <typename U>
424 bool pop(U & ret)
426 BOOST_STATIC_ASSERT((boost::is_convertible<T, U>::value));
427 tagged_node_handle old_tos = tos.load(detail::memory_order_consume);
429 for (;;) {
430 node * old_tos_pointer = pool.get_pointer(old_tos);
431 if (!old_tos_pointer)
432 return false;
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);
439 return true;
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
452 * */
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
466 * */
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))
475 return false;
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);
483 return true;
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.
491 * */
492 bool empty(void) const
494 return pool.get_pointer(tos.load()) == NULL;
497 private:
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];
504 pool_t pool;
505 #endif
508 } /* namespace lockfree */
509 } /* namespace boost */
511 #endif /* BOOST_LOCKFREE_STACK_HPP_INCLUDED */