Chris@16
|
1 // Copyright (C) 2008-2013 Tim Blechmann
|
Chris@16
|
2 //
|
Chris@16
|
3 // Distributed under the Boost Software License, Version 1.0. (See
|
Chris@16
|
4 // accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
5 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
6
|
Chris@16
|
7 #ifndef BOOST_LOCKFREE_STACK_HPP_INCLUDED
|
Chris@16
|
8 #define BOOST_LOCKFREE_STACK_HPP_INCLUDED
|
Chris@16
|
9
|
Chris@16
|
10 #include <boost/assert.hpp>
|
Chris@16
|
11 #include <boost/checked_delete.hpp>
|
Chris@16
|
12 #include <boost/integer_traits.hpp>
|
Chris@16
|
13 #include <boost/static_assert.hpp>
|
Chris@16
|
14 #include <boost/tuple/tuple.hpp>
|
Chris@16
|
15 #include <boost/type_traits/has_trivial_assign.hpp>
|
Chris@16
|
16 #include <boost/type_traits/has_trivial_destructor.hpp>
|
Chris@16
|
17
|
Chris@16
|
18 #include <boost/lockfree/detail/atomic.hpp>
|
Chris@16
|
19 #include <boost/lockfree/detail/copy_payload.hpp>
|
Chris@16
|
20 #include <boost/lockfree/detail/freelist.hpp>
|
Chris@16
|
21 #include <boost/lockfree/detail/parameter.hpp>
|
Chris@16
|
22 #include <boost/lockfree/detail/tagged_ptr.hpp>
|
Chris@16
|
23
|
Chris@101
|
24 #ifdef BOOST_HAS_PRAGMA_ONCE
|
Chris@101
|
25 #pragma once
|
Chris@101
|
26 #endif
|
Chris@101
|
27
|
Chris@16
|
28 namespace boost {
|
Chris@16
|
29 namespace lockfree {
|
Chris@16
|
30 namespace detail {
|
Chris@16
|
31
|
Chris@16
|
32 typedef parameter::parameters<boost::parameter::optional<tag::allocator>,
|
Chris@16
|
33 boost::parameter::optional<tag::capacity>
|
Chris@16
|
34 > stack_signature;
|
Chris@16
|
35
|
Chris@16
|
36 }
|
Chris@16
|
37
|
Chris@16
|
38 /** The stack class provides a multi-writer/multi-reader stack, pushing and popping is lock-free,
|
Chris@16
|
39 * construction/destruction has to be synchronized. It uses a freelist for memory management,
|
Chris@16
|
40 * freed nodes are pushed to the freelist and not returned to the OS before the stack is destroyed.
|
Chris@16
|
41 *
|
Chris@16
|
42 * \b Policies:
|
Chris@16
|
43 *
|
Chris@16
|
44 * - \c boost::lockfree::fixed_sized<>, defaults to \c boost::lockfree::fixed_sized<false> <br>
|
Chris@16
|
45 * Can be used to completely disable dynamic memory allocations during push in order to ensure lockfree behavior.<br>
|
Chris@16
|
46 * If the data structure is configured as fixed-sized, the internal nodes are stored inside an array and they are addressed
|
Chris@16
|
47 * by array indexing. This limits the possible size of the stack to the number of elements that can be addressed by the index
|
Chris@16
|
48 * type (usually 2**16-2), but on platforms that lack double-width compare-and-exchange instructions, this is the best way
|
Chris@16
|
49 * to achieve lock-freedom.
|
Chris@16
|
50 *
|
Chris@16
|
51 * - \c boost::lockfree::capacity<>, optional <br>
|
Chris@16
|
52 * If this template argument is passed to the options, the size of the stack is set at compile-time. <br>
|
Chris@16
|
53 * It this option implies \c fixed_sized<true>
|
Chris@16
|
54 *
|
Chris@16
|
55 * - \c boost::lockfree::allocator<>, defaults to \c boost::lockfree::allocator<std::allocator<void>> <br>
|
Chris@16
|
56 * Specifies the allocator that is used for the internal freelist
|
Chris@16
|
57 *
|
Chris@16
|
58 * \b Requirements:
|
Chris@16
|
59 * - T must have a copy constructor
|
Chris@16
|
60 * */
|
Chris@16
|
61 #ifndef BOOST_DOXYGEN_INVOKED
|
Chris@16
|
62 template <typename T,
|
Chris@16
|
63 class A0 = boost::parameter::void_,
|
Chris@16
|
64 class A1 = boost::parameter::void_,
|
Chris@16
|
65 class A2 = boost::parameter::void_>
|
Chris@16
|
66 #else
|
Chris@16
|
67 template <typename T, ...Options>
|
Chris@16
|
68 #endif
|
Chris@16
|
69 class stack
|
Chris@16
|
70 {
|
Chris@16
|
71 private:
|
Chris@16
|
72 #ifndef BOOST_DOXYGEN_INVOKED
|
Chris@16
|
73 BOOST_STATIC_ASSERT(boost::has_trivial_assign<T>::value);
|
Chris@16
|
74 BOOST_STATIC_ASSERT(boost::has_trivial_destructor<T>::value);
|
Chris@16
|
75
|
Chris@16
|
76 typedef typename detail::stack_signature::bind<A0, A1, A2>::type bound_args;
|
Chris@16
|
77
|
Chris@16
|
78 static const bool has_capacity = detail::extract_capacity<bound_args>::has_capacity;
|
Chris@16
|
79 static const size_t capacity = detail::extract_capacity<bound_args>::capacity;
|
Chris@16
|
80 static const bool fixed_sized = detail::extract_fixed_sized<bound_args>::value;
|
Chris@16
|
81 static const bool node_based = !(has_capacity || fixed_sized);
|
Chris@16
|
82 static const bool compile_time_sized = has_capacity;
|
Chris@16
|
83
|
Chris@16
|
84 struct node
|
Chris@16
|
85 {
|
Chris@16
|
86 node(T const & val):
|
Chris@16
|
87 v(val)
|
Chris@16
|
88 {}
|
Chris@16
|
89
|
Chris@16
|
90 typedef typename detail::select_tagged_handle<node, node_based>::handle_type handle_t;
|
Chris@16
|
91 handle_t next;
|
Chris@16
|
92 const T v;
|
Chris@16
|
93 };
|
Chris@16
|
94
|
Chris@16
|
95 typedef typename detail::extract_allocator<bound_args, node>::type node_allocator;
|
Chris@16
|
96 typedef typename detail::select_freelist<node, node_allocator, compile_time_sized, fixed_sized, capacity>::type pool_t;
|
Chris@16
|
97 typedef typename pool_t::tagged_node_handle tagged_node_handle;
|
Chris@16
|
98
|
Chris@16
|
99 // check compile-time capacity
|
Chris@16
|
100 BOOST_STATIC_ASSERT((mpl::if_c<has_capacity,
|
Chris@16
|
101 mpl::bool_<capacity - 1 < boost::integer_traits<boost::uint16_t>::const_max>,
|
Chris@16
|
102 mpl::true_
|
Chris@16
|
103 >::type::value));
|
Chris@16
|
104
|
Chris@16
|
105 struct implementation_defined
|
Chris@16
|
106 {
|
Chris@16
|
107 typedef node_allocator allocator;
|
Chris@16
|
108 typedef std::size_t size_type;
|
Chris@16
|
109 };
|
Chris@16
|
110
|
Chris@16
|
111 #endif
|
Chris@16
|
112
|
Chris@101
|
113 BOOST_DELETED_FUNCTION(stack(stack const&))
|
Chris@101
|
114 BOOST_DELETED_FUNCTION(stack& operator= (stack const&))
|
Chris@16
|
115
|
Chris@16
|
116 public:
|
Chris@16
|
117 typedef T value_type;
|
Chris@16
|
118 typedef typename implementation_defined::allocator allocator;
|
Chris@16
|
119 typedef typename implementation_defined::size_type size_type;
|
Chris@16
|
120
|
Chris@16
|
121 /**
|
Chris@16
|
122 * \return true, if implementation is lock-free.
|
Chris@16
|
123 *
|
Chris@16
|
124 * \warning It only checks, if the top stack node and the freelist can be modified in a lock-free manner.
|
Chris@16
|
125 * On most platforms, the whole implementation is lock-free, if this is true. Using c++0x-style atomics,
|
Chris@16
|
126 * there is no possibility to provide a completely accurate implementation, because one would need to test
|
Chris@16
|
127 * every internal node, which is impossible if further nodes will be allocated from the operating system.
|
Chris@16
|
128 *
|
Chris@16
|
129 * */
|
Chris@16
|
130 bool is_lock_free (void) const
|
Chris@16
|
131 {
|
Chris@16
|
132 return tos.is_lock_free() && pool.is_lock_free();
|
Chris@16
|
133 }
|
Chris@16
|
134
|
Chris@16
|
135 //! Construct stack
|
Chris@16
|
136 // @{
|
Chris@16
|
137 stack(void):
|
Chris@16
|
138 pool(node_allocator(), capacity)
|
Chris@16
|
139 {
|
Chris@16
|
140 BOOST_ASSERT(has_capacity);
|
Chris@16
|
141 initialize();
|
Chris@16
|
142 }
|
Chris@16
|
143
|
Chris@16
|
144 template <typename U>
|
Chris@16
|
145 explicit stack(typename node_allocator::template rebind<U>::other const & alloc):
|
Chris@16
|
146 pool(alloc, capacity)
|
Chris@16
|
147 {
|
Chris@16
|
148 BOOST_STATIC_ASSERT(has_capacity);
|
Chris@16
|
149 initialize();
|
Chris@16
|
150 }
|
Chris@16
|
151
|
Chris@16
|
152 explicit stack(allocator const & alloc):
|
Chris@16
|
153 pool(alloc, capacity)
|
Chris@16
|
154 {
|
Chris@16
|
155 BOOST_ASSERT(has_capacity);
|
Chris@16
|
156 initialize();
|
Chris@16
|
157 }
|
Chris@16
|
158 // @}
|
Chris@16
|
159
|
Chris@16
|
160 //! Construct stack, allocate n nodes for the freelist.
|
Chris@16
|
161 // @{
|
Chris@16
|
162 explicit stack(size_type n):
|
Chris@16
|
163 pool(node_allocator(), n)
|
Chris@16
|
164 {
|
Chris@16
|
165 BOOST_ASSERT(!has_capacity);
|
Chris@16
|
166 initialize();
|
Chris@16
|
167 }
|
Chris@16
|
168
|
Chris@16
|
169 template <typename U>
|
Chris@16
|
170 stack(size_type n, typename node_allocator::template rebind<U>::other const & alloc):
|
Chris@16
|
171 pool(alloc, n)
|
Chris@16
|
172 {
|
Chris@16
|
173 BOOST_STATIC_ASSERT(!has_capacity);
|
Chris@16
|
174 initialize();
|
Chris@16
|
175 }
|
Chris@16
|
176 // @}
|
Chris@16
|
177
|
Chris@16
|
178 /** Allocate n nodes for freelist
|
Chris@16
|
179 *
|
Chris@16
|
180 * \pre only valid if no capacity<> argument given
|
Chris@16
|
181 * \note thread-safe, may block if memory allocator blocks
|
Chris@16
|
182 *
|
Chris@16
|
183 * */
|
Chris@16
|
184 void reserve(size_type n)
|
Chris@16
|
185 {
|
Chris@16
|
186 BOOST_STATIC_ASSERT(!has_capacity);
|
Chris@16
|
187 pool.template reserve<true>(n);
|
Chris@16
|
188 }
|
Chris@16
|
189
|
Chris@16
|
190 /** Allocate n nodes for freelist
|
Chris@16
|
191 *
|
Chris@16
|
192 * \pre only valid if no capacity<> argument given
|
Chris@16
|
193 * \note not thread-safe, may block if memory allocator blocks
|
Chris@16
|
194 *
|
Chris@16
|
195 * */
|
Chris@16
|
196 void reserve_unsafe(size_type n)
|
Chris@16
|
197 {
|
Chris@16
|
198 BOOST_STATIC_ASSERT(!has_capacity);
|
Chris@16
|
199 pool.template reserve<false>(n);
|
Chris@16
|
200 }
|
Chris@16
|
201
|
Chris@16
|
202 /** Destroys stack, free all nodes from freelist.
|
Chris@16
|
203 *
|
Chris@16
|
204 * \note not thread-safe
|
Chris@16
|
205 *
|
Chris@16
|
206 * */
|
Chris@16
|
207 ~stack(void)
|
Chris@16
|
208 {
|
Chris@16
|
209 T dummy;
|
Chris@16
|
210 while(unsynchronized_pop(dummy))
|
Chris@16
|
211 {}
|
Chris@16
|
212 }
|
Chris@16
|
213
|
Chris@16
|
214 private:
|
Chris@16
|
215 #ifndef BOOST_DOXYGEN_INVOKED
|
Chris@16
|
216 void initialize(void)
|
Chris@16
|
217 {
|
Chris@16
|
218 tos.store(tagged_node_handle(pool.null_handle(), 0));
|
Chris@16
|
219 }
|
Chris@16
|
220
|
Chris@16
|
221 void link_nodes_atomic(node * new_top_node, node * end_node)
|
Chris@16
|
222 {
|
Chris@16
|
223 tagged_node_handle old_tos = tos.load(detail::memory_order_relaxed);
|
Chris@16
|
224 for (;;) {
|
Chris@16
|
225 tagged_node_handle new_tos (pool.get_handle(new_top_node), old_tos.get_tag());
|
Chris@16
|
226 end_node->next = pool.get_handle(old_tos);
|
Chris@16
|
227
|
Chris@16
|
228 if (tos.compare_exchange_weak(old_tos, new_tos))
|
Chris@16
|
229 break;
|
Chris@16
|
230 }
|
Chris@16
|
231 }
|
Chris@16
|
232
|
Chris@16
|
233 void link_nodes_unsafe(node * new_top_node, node * end_node)
|
Chris@16
|
234 {
|
Chris@16
|
235 tagged_node_handle old_tos = tos.load(detail::memory_order_relaxed);
|
Chris@16
|
236
|
Chris@16
|
237 tagged_node_handle new_tos (pool.get_handle(new_top_node), old_tos.get_tag());
|
Chris@16
|
238 end_node->next = pool.get_pointer(old_tos);
|
Chris@16
|
239
|
Chris@16
|
240 tos.store(new_tos, memory_order_relaxed);
|
Chris@16
|
241 }
|
Chris@16
|
242
|
Chris@16
|
243 template <bool Threadsafe, bool Bounded, typename ConstIterator>
|
Chris@16
|
244 tuple<node*, node*> prepare_node_list(ConstIterator begin, ConstIterator end, ConstIterator & ret)
|
Chris@16
|
245 {
|
Chris@16
|
246 ConstIterator it = begin;
|
Chris@16
|
247 node * end_node = pool.template construct<Threadsafe, Bounded>(*it++);
|
Chris@16
|
248 if (end_node == NULL) {
|
Chris@16
|
249 ret = begin;
|
Chris@16
|
250 return make_tuple<node*, node*>(NULL, NULL);
|
Chris@16
|
251 }
|
Chris@16
|
252
|
Chris@16
|
253 node * new_top_node = end_node;
|
Chris@16
|
254 end_node->next = NULL;
|
Chris@16
|
255
|
Chris@16
|
256 try {
|
Chris@16
|
257 /* link nodes */
|
Chris@16
|
258 for (; it != end; ++it) {
|
Chris@16
|
259 node * newnode = pool.template construct<Threadsafe, Bounded>(*it);
|
Chris@16
|
260 if (newnode == NULL)
|
Chris@16
|
261 break;
|
Chris@16
|
262 newnode->next = new_top_node;
|
Chris@16
|
263 new_top_node = newnode;
|
Chris@16
|
264 }
|
Chris@16
|
265 } catch (...) {
|
Chris@16
|
266 for (node * current_node = new_top_node; current_node != NULL;) {
|
Chris@16
|
267 node * next = current_node->next;
|
Chris@16
|
268 pool.template destruct<Threadsafe>(current_node);
|
Chris@16
|
269 current_node = next;
|
Chris@16
|
270 }
|
Chris@16
|
271 throw;
|
Chris@16
|
272 }
|
Chris@16
|
273 ret = it;
|
Chris@16
|
274 return make_tuple(new_top_node, end_node);
|
Chris@16
|
275 }
|
Chris@16
|
276 #endif
|
Chris@16
|
277
|
Chris@16
|
278 public:
|
Chris@16
|
279 /** Pushes object t to the stack.
|
Chris@16
|
280 *
|
Chris@16
|
281 * \post object will be pushed to the stack, if internal node can be allocated
|
Chris@16
|
282 * \returns true, if the push operation is successful.
|
Chris@16
|
283 *
|
Chris@16
|
284 * \note Thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated
|
Chris@16
|
285 * from the OS. This may not be lock-free.
|
Chris@16
|
286 * \throws if memory allocator throws
|
Chris@16
|
287 * */
|
Chris@16
|
288 bool push(T const & v)
|
Chris@16
|
289 {
|
Chris@16
|
290 return do_push<false>(v);
|
Chris@16
|
291 }
|
Chris@16
|
292
|
Chris@16
|
293 /** Pushes object t to the stack.
|
Chris@16
|
294 *
|
Chris@16
|
295 * \post object will be pushed to the stack, if internal node can be allocated
|
Chris@16
|
296 * \returns true, if the push operation is successful.
|
Chris@16
|
297 *
|
Chris@16
|
298 * \note Thread-safe and non-blocking. If internal memory pool is exhausted, the push operation will fail
|
Chris@16
|
299 * */
|
Chris@16
|
300 bool bounded_push(T const & v)
|
Chris@16
|
301 {
|
Chris@16
|
302 return do_push<true>(v);
|
Chris@16
|
303 }
|
Chris@16
|
304
|
Chris@16
|
305 #ifndef BOOST_DOXYGEN_INVOKED
|
Chris@16
|
306 private:
|
Chris@16
|
307 template <bool Bounded>
|
Chris@16
|
308 bool do_push(T const & v)
|
Chris@16
|
309 {
|
Chris@16
|
310 node * newnode = pool.template construct<true, Bounded>(v);
|
Chris@16
|
311 if (newnode == 0)
|
Chris@16
|
312 return false;
|
Chris@16
|
313
|
Chris@16
|
314 link_nodes_atomic(newnode, newnode);
|
Chris@16
|
315 return true;
|
Chris@16
|
316 }
|
Chris@16
|
317
|
Chris@16
|
318 template <bool Bounded, typename ConstIterator>
|
Chris@16
|
319 ConstIterator do_push(ConstIterator begin, ConstIterator end)
|
Chris@16
|
320 {
|
Chris@16
|
321 node * new_top_node;
|
Chris@16
|
322 node * end_node;
|
Chris@16
|
323 ConstIterator ret;
|
Chris@16
|
324
|
Chris@16
|
325 tie(new_top_node, end_node) = prepare_node_list<true, Bounded>(begin, end, ret);
|
Chris@16
|
326 if (new_top_node)
|
Chris@16
|
327 link_nodes_atomic(new_top_node, end_node);
|
Chris@16
|
328
|
Chris@16
|
329 return ret;
|
Chris@16
|
330 }
|
Chris@16
|
331
|
Chris@16
|
332 public:
|
Chris@16
|
333 #endif
|
Chris@16
|
334
|
Chris@16
|
335 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated.
|
Chris@16
|
336 *
|
Chris@16
|
337 * \return iterator to the first element, which has not been pushed
|
Chris@16
|
338 *
|
Chris@16
|
339 * \note Operation is applied atomically
|
Chris@16
|
340 * \note Thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated
|
Chris@16
|
341 * from the OS. This may not be lock-free.
|
Chris@16
|
342 * \throws if memory allocator throws
|
Chris@16
|
343 */
|
Chris@16
|
344 template <typename ConstIterator>
|
Chris@16
|
345 ConstIterator push(ConstIterator begin, ConstIterator end)
|
Chris@16
|
346 {
|
Chris@16
|
347 return do_push<false, ConstIterator>(begin, end);
|
Chris@16
|
348 }
|
Chris@16
|
349
|
Chris@16
|
350 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated.
|
Chris@16
|
351 *
|
Chris@16
|
352 * \return iterator to the first element, which has not been pushed
|
Chris@16
|
353 *
|
Chris@16
|
354 * \note Operation is applied atomically
|
Chris@16
|
355 * \note Thread-safe and non-blocking. If internal memory pool is exhausted, the push operation will fail
|
Chris@16
|
356 * \throws if memory allocator throws
|
Chris@16
|
357 */
|
Chris@16
|
358 template <typename ConstIterator>
|
Chris@16
|
359 ConstIterator bounded_push(ConstIterator begin, ConstIterator end)
|
Chris@16
|
360 {
|
Chris@16
|
361 return do_push<true, ConstIterator>(begin, end);
|
Chris@16
|
362 }
|
Chris@16
|
363
|
Chris@16
|
364
|
Chris@16
|
365 /** Pushes object t to the stack.
|
Chris@16
|
366 *
|
Chris@16
|
367 * \post object will be pushed to the stack, if internal node can be allocated
|
Chris@16
|
368 * \returns true, if the push operation is successful.
|
Chris@16
|
369 *
|
Chris@16
|
370 * \note Not thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated
|
Chris@16
|
371 * from the OS. This may not be lock-free.
|
Chris@16
|
372 * \throws if memory allocator throws
|
Chris@16
|
373 * */
|
Chris@16
|
374 bool unsynchronized_push(T const & v)
|
Chris@16
|
375 {
|
Chris@16
|
376 node * newnode = pool.template construct<false, false>(v);
|
Chris@16
|
377 if (newnode == 0)
|
Chris@16
|
378 return false;
|
Chris@16
|
379
|
Chris@16
|
380 link_nodes_unsafe(newnode, newnode);
|
Chris@16
|
381 return true;
|
Chris@16
|
382 }
|
Chris@16
|
383
|
Chris@16
|
384 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated.
|
Chris@16
|
385 *
|
Chris@16
|
386 * \return iterator to the first element, which has not been pushed
|
Chris@16
|
387 *
|
Chris@16
|
388 * \note Not thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated
|
Chris@16
|
389 * from the OS. This may not be lock-free.
|
Chris@16
|
390 * \throws if memory allocator throws
|
Chris@16
|
391 */
|
Chris@16
|
392 template <typename ConstIterator>
|
Chris@16
|
393 ConstIterator unsynchronized_push(ConstIterator begin, ConstIterator end)
|
Chris@16
|
394 {
|
Chris@16
|
395 node * new_top_node;
|
Chris@16
|
396 node * end_node;
|
Chris@16
|
397 ConstIterator ret;
|
Chris@16
|
398
|
Chris@16
|
399 tie(new_top_node, end_node) = prepare_node_list<false, false>(begin, end, ret);
|
Chris@16
|
400 if (new_top_node)
|
Chris@16
|
401 link_nodes_unsafe(new_top_node, end_node);
|
Chris@16
|
402
|
Chris@16
|
403 return ret;
|
Chris@16
|
404 }
|
Chris@16
|
405
|
Chris@16
|
406
|
Chris@16
|
407 /** Pops object from stack.
|
Chris@16
|
408 *
|
Chris@16
|
409 * \post if pop operation is successful, object will be copied to ret.
|
Chris@16
|
410 * \returns true, if the pop operation is successful, false if stack was empty.
|
Chris@16
|
411 *
|
Chris@16
|
412 * \note Thread-safe and non-blocking
|
Chris@16
|
413 *
|
Chris@16
|
414 * */
|
Chris@16
|
415 bool pop(T & ret)
|
Chris@16
|
416 {
|
Chris@16
|
417 return pop<T>(ret);
|
Chris@16
|
418 }
|
Chris@16
|
419
|
Chris@16
|
420 /** Pops object from stack.
|
Chris@16
|
421 *
|
Chris@16
|
422 * \pre type T must be convertible to U
|
Chris@16
|
423 * \post if pop operation is successful, object will be copied to ret.
|
Chris@16
|
424 * \returns true, if the pop operation is successful, false if stack was empty.
|
Chris@16
|
425 *
|
Chris@16
|
426 * \note Thread-safe and non-blocking
|
Chris@16
|
427 *
|
Chris@16
|
428 * */
|
Chris@16
|
429 template <typename U>
|
Chris@16
|
430 bool pop(U & ret)
|
Chris@16
|
431 {
|
Chris@16
|
432 BOOST_STATIC_ASSERT((boost::is_convertible<T, U>::value));
|
Chris@16
|
433 detail::consume_via_copy<U> consumer(ret);
|
Chris@16
|
434
|
Chris@16
|
435 return consume_one(consumer);
|
Chris@16
|
436 }
|
Chris@16
|
437
|
Chris@16
|
438
|
Chris@16
|
439 /** Pops object from stack.
|
Chris@16
|
440 *
|
Chris@16
|
441 * \post if pop operation is successful, object will be copied to ret.
|
Chris@16
|
442 * \returns true, if the pop operation is successful, false if stack was empty.
|
Chris@16
|
443 *
|
Chris@16
|
444 * \note Not thread-safe, but non-blocking
|
Chris@16
|
445 *
|
Chris@16
|
446 * */
|
Chris@16
|
447 bool unsynchronized_pop(T & ret)
|
Chris@16
|
448 {
|
Chris@16
|
449 return unsynchronized_pop<T>(ret);
|
Chris@16
|
450 }
|
Chris@16
|
451
|
Chris@16
|
452 /** Pops object from stack.
|
Chris@16
|
453 *
|
Chris@16
|
454 * \pre type T must be convertible to U
|
Chris@16
|
455 * \post if pop operation is successful, object will be copied to ret.
|
Chris@16
|
456 * \returns true, if the pop operation is successful, false if stack was empty.
|
Chris@16
|
457 *
|
Chris@16
|
458 * \note Not thread-safe, but non-blocking
|
Chris@16
|
459 *
|
Chris@16
|
460 * */
|
Chris@16
|
461 template <typename U>
|
Chris@16
|
462 bool unsynchronized_pop(U & ret)
|
Chris@16
|
463 {
|
Chris@16
|
464 BOOST_STATIC_ASSERT((boost::is_convertible<T, U>::value));
|
Chris@16
|
465 tagged_node_handle old_tos = tos.load(detail::memory_order_relaxed);
|
Chris@16
|
466 node * old_tos_pointer = pool.get_pointer(old_tos);
|
Chris@16
|
467
|
Chris@16
|
468 if (!pool.get_pointer(old_tos))
|
Chris@16
|
469 return false;
|
Chris@16
|
470
|
Chris@16
|
471 node * new_tos_ptr = pool.get_pointer(old_tos_pointer->next);
|
Chris@16
|
472 tagged_node_handle new_tos(pool.get_handle(new_tos_ptr), old_tos.get_next_tag());
|
Chris@16
|
473
|
Chris@16
|
474 tos.store(new_tos, memory_order_relaxed);
|
Chris@16
|
475 detail::copy_payload(old_tos_pointer->v, ret);
|
Chris@16
|
476 pool.template destruct<false>(old_tos);
|
Chris@16
|
477 return true;
|
Chris@16
|
478 }
|
Chris@16
|
479
|
Chris@16
|
480 /** consumes one element via a functor
|
Chris@16
|
481 *
|
Chris@16
|
482 * pops one element from the stack and applies the functor on this object
|
Chris@16
|
483 *
|
Chris@16
|
484 * \returns true, if one element was consumed
|
Chris@16
|
485 *
|
Chris@16
|
486 * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking
|
Chris@16
|
487 * */
|
Chris@16
|
488 template <typename Functor>
|
Chris@16
|
489 bool consume_one(Functor & f)
|
Chris@16
|
490 {
|
Chris@16
|
491 tagged_node_handle old_tos = tos.load(detail::memory_order_consume);
|
Chris@16
|
492
|
Chris@16
|
493 for (;;) {
|
Chris@16
|
494 node * old_tos_pointer = pool.get_pointer(old_tos);
|
Chris@16
|
495 if (!old_tos_pointer)
|
Chris@16
|
496 return false;
|
Chris@16
|
497
|
Chris@16
|
498 tagged_node_handle new_tos(old_tos_pointer->next, old_tos.get_next_tag());
|
Chris@16
|
499
|
Chris@16
|
500 if (tos.compare_exchange_weak(old_tos, new_tos)) {
|
Chris@16
|
501 f(old_tos_pointer->v);
|
Chris@16
|
502 pool.template destruct<true>(old_tos);
|
Chris@16
|
503 return true;
|
Chris@16
|
504 }
|
Chris@16
|
505 }
|
Chris@16
|
506 }
|
Chris@16
|
507
|
Chris@16
|
508 /// \copydoc boost::lockfree::stack::consume_one(Functor & rhs)
|
Chris@16
|
509 template <typename Functor>
|
Chris@16
|
510 bool consume_one(Functor const & f)
|
Chris@16
|
511 {
|
Chris@16
|
512 tagged_node_handle old_tos = tos.load(detail::memory_order_consume);
|
Chris@16
|
513
|
Chris@16
|
514 for (;;) {
|
Chris@16
|
515 node * old_tos_pointer = pool.get_pointer(old_tos);
|
Chris@16
|
516 if (!old_tos_pointer)
|
Chris@16
|
517 return false;
|
Chris@16
|
518
|
Chris@16
|
519 tagged_node_handle new_tos(old_tos_pointer->next, old_tos.get_next_tag());
|
Chris@16
|
520
|
Chris@16
|
521 if (tos.compare_exchange_weak(old_tos, new_tos)) {
|
Chris@16
|
522 f(old_tos_pointer->v);
|
Chris@16
|
523 pool.template destruct<true>(old_tos);
|
Chris@16
|
524 return true;
|
Chris@16
|
525 }
|
Chris@16
|
526 }
|
Chris@16
|
527 }
|
Chris@16
|
528
|
Chris@16
|
529 /** consumes all elements via a functor
|
Chris@16
|
530 *
|
Chris@16
|
531 * sequentially pops all elements from the stack and applies the functor on each object
|
Chris@16
|
532 *
|
Chris@16
|
533 * \returns number of elements that are consumed
|
Chris@16
|
534 *
|
Chris@16
|
535 * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking
|
Chris@16
|
536 * */
|
Chris@16
|
537 template <typename Functor>
|
Chris@16
|
538 size_t consume_all(Functor & f)
|
Chris@16
|
539 {
|
Chris@16
|
540 size_t element_count = 0;
|
Chris@16
|
541 while (consume_one(f))
|
Chris@16
|
542 element_count += 1;
|
Chris@16
|
543
|
Chris@16
|
544 return element_count;
|
Chris@16
|
545 }
|
Chris@16
|
546
|
Chris@16
|
547 /// \copydoc boost::lockfree::stack::consume_all(Functor & rhs)
|
Chris@16
|
548 template <typename Functor>
|
Chris@16
|
549 size_t consume_all(Functor const & f)
|
Chris@16
|
550 {
|
Chris@16
|
551 size_t element_count = 0;
|
Chris@16
|
552 while (consume_one(f))
|
Chris@16
|
553 element_count += 1;
|
Chris@16
|
554
|
Chris@16
|
555 return element_count;
|
Chris@16
|
556 }
|
Chris@16
|
557
|
Chris@16
|
558 /**
|
Chris@16
|
559 * \return true, if stack is empty.
|
Chris@16
|
560 *
|
Chris@16
|
561 * \note It only guarantees that at some point during the execution of the function the stack has been empty.
|
Chris@16
|
562 * It is rarely practical to use this value in program logic, because the stack can be modified by other threads.
|
Chris@16
|
563 * */
|
Chris@16
|
564 bool empty(void) const
|
Chris@16
|
565 {
|
Chris@16
|
566 return pool.get_pointer(tos.load()) == NULL;
|
Chris@16
|
567 }
|
Chris@16
|
568
|
Chris@16
|
569 private:
|
Chris@16
|
570 #ifndef BOOST_DOXYGEN_INVOKED
|
Chris@16
|
571 detail::atomic<tagged_node_handle> tos;
|
Chris@16
|
572
|
Chris@16
|
573 static const int padding_size = BOOST_LOCKFREE_CACHELINE_BYTES - sizeof(tagged_node_handle);
|
Chris@16
|
574 char padding[padding_size];
|
Chris@16
|
575
|
Chris@16
|
576 pool_t pool;
|
Chris@16
|
577 #endif
|
Chris@16
|
578 };
|
Chris@16
|
579
|
Chris@16
|
580 } /* namespace lockfree */
|
Chris@16
|
581 } /* namespace boost */
|
Chris@16
|
582
|
Chris@16
|
583 #endif /* BOOST_LOCKFREE_STACK_HPP_INCLUDED */
|