annotate DEPENDENCIES/generic/include/boost/lockfree/stack.hpp @ 125:34e428693f5d vext

Vext -> Repoint
author Chris Cannam
date Thu, 14 Jun 2018 11:15:39 +0100
parents c530137014c0
children
rev   line source
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 */