1 // Copyright (C) 2008-2013 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) 6 7 #ifndef BOOST_LOCKFREE_STACK_HPP_INCLUDED 8 #define BOOST_LOCKFREE_STACK_HPP_INCLUDED 9 10 #include <boost/assert.hpp> 11 #include <boost/checked_delete.hpp> 12 #include <boost/core/allocator_access.hpp> 13 #include <boost/core/no_exceptions_support.hpp> 14 #include <boost/integer_traits.hpp> 15 #include <boost/static_assert.hpp> 16 #include <boost/tuple/tuple.hpp> 17 #include <boost/type_traits/is_copy_constructible.hpp> 18 19 #include <boost/lockfree/detail/atomic.hpp> 20 #include <boost/lockfree/detail/copy_payload.hpp> 21 #include <boost/lockfree/detail/freelist.hpp> 22 #include <boost/lockfree/detail/parameter.hpp> 23 #include <boost/lockfree/detail/tagged_ptr.hpp> 24 25 #include <boost/lockfree/lockfree_forward.hpp> 26 27 #ifdef BOOST_HAS_PRAGMA_ONCE 28 #pragma once 29 #endif 30 31 namespace boost { 32 namespace lockfree { 33 namespace detail { 34 35 typedef parameter::parameters<boost::parameter::optional<tag::allocator>, 36 boost::parameter::optional<tag::capacity> 37 > stack_signature; 38 39 } 40 41 /** The stack class provides a multi-writer/multi-reader stack, pushing and popping is lock-free, 42 * construction/destruction has to be synchronized. It uses a freelist for memory management, 43 * freed nodes are pushed to the freelist and not returned to the OS before the stack is destroyed. 44 * 45 * \b Policies: 46 * 47 * - \c boost::lockfree::fixed_sized<>, defaults to \c boost::lockfree::fixed_sized<false> <br> 48 * Can be used to completely disable dynamic memory allocations during push in order to ensure lockfree behavior.<br> 49 * If the data structure is configured as fixed-sized, the internal nodes are stored inside an array and they are addressed 50 * by array indexing. This limits the possible size of the stack to the number of elements that can be addressed by the index 51 * type (usually 2**16-2), but on platforms that lack double-width compare-and-exchange instructions, this is the best way 52 * to achieve lock-freedom. 53 * 54 * - \c boost::lockfree::capacity<>, optional <br> 55 * If this template argument is passed to the options, the size of the stack is set at compile-time. <br> 56 * It this option implies \c fixed_sized<true> 57 * 58 * - \c boost::lockfree::allocator<>, defaults to \c boost::lockfree::allocator<std::allocator<void>> <br> 59 * Specifies the allocator that is used for the internal freelist 60 * 61 * \b Requirements: 62 * - T must have a copy constructor 63 * */ 64 #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES 65 template <typename T, class A0, class A1, class A2> 66 #else 67 template <typename T, typename ...Options> 68 #endif 69 class stack 70 { 71 private: 72 #ifndef BOOST_DOXYGEN_INVOKED 73 BOOST_STATIC_ASSERT(boost::is_copy_constructible<T>::value); 74 75 #ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES 76 typedef typename detail::stack_signature::bind<A0, A1, A2>::type bound_args; 77 #else 78 typedef typename detail::stack_signature::bind<Options...>::type bound_args; 79 #endif 80 81 static const bool has_capacity = detail::extract_capacity<bound_args>::has_capacity; 82 static const size_t capacity = detail::extract_capacity<bound_args>::capacity; 83 static const bool fixed_sized = detail::extract_fixed_sized<bound_args>::value; 84 static const bool node_based = !(has_capacity || fixed_sized); 85 static const bool compile_time_sized = has_capacity; 86 87 struct node 88 { nodeboost::lockfree::stack::node89 node(T const & val): 90 v(val) 91 {} 92 93 typedef typename detail::select_tagged_handle<node, node_based>::handle_type handle_t; 94 handle_t next; 95 const T v; 96 }; 97 98 typedef typename detail::extract_allocator<bound_args, node>::type node_allocator; 99 typedef typename detail::select_freelist<node, node_allocator, compile_time_sized, fixed_sized, capacity>::type pool_t; 100 typedef typename pool_t::tagged_node_handle tagged_node_handle; 101 102 // check compile-time capacity 103 BOOST_STATIC_ASSERT((mpl::if_c<has_capacity, 104 mpl::bool_<capacity - 1 < boost::integer_traits<boost::uint16_t>::const_max>, 105 mpl::true_ 106 >::type::value)); 107 108 struct implementation_defined 109 { 110 typedef node_allocator allocator; 111 typedef std::size_t size_type; 112 }; 113 114 #endif 115 116 BOOST_DELETED_FUNCTION(stack(stack const&)) 117 BOOST_DELETED_FUNCTION(stack& operator= (stack const&)) 118 119 public: 120 typedef T value_type; 121 typedef typename implementation_defined::allocator allocator; 122 typedef typename implementation_defined::size_type size_type; 123 124 /** 125 * \return true, if implementation is lock-free. 126 * 127 * \warning It only checks, if the top stack node and the freelist can be modified in a lock-free manner. 128 * On most platforms, the whole implementation is lock-free, if this is true. Using c++0x-style atomics, 129 * there is no possibility to provide a completely accurate implementation, because one would need to test 130 * every internal node, which is impossible if further nodes will be allocated from the operating system. 131 * 132 * */ is_lock_free(void) const133 bool is_lock_free (void) const 134 { 135 return tos.is_lock_free() && pool.is_lock_free(); 136 } 137 138 /** Construct a fixed-sized stack 139 * 140 * \pre Must specify a capacity<> argument 141 * */ stack(void)142 stack(void): 143 pool(node_allocator(), capacity) 144 { 145 // Don't use BOOST_STATIC_ASSERT() here since it will be evaluated when compiling 146 // this function and this function may be compiled even when it isn't being used. 147 BOOST_ASSERT(has_capacity); 148 initialize(); 149 } 150 151 /** Construct a fixed-sized stack with a custom allocator 152 * 153 * \pre Must specify a capacity<> argument 154 * */ 155 template <typename U> stack(typename boost::allocator_rebind<node_allocator,U>::type const & alloc)156 explicit stack(typename boost::allocator_rebind<node_allocator, U>::type const & alloc): 157 pool(alloc, capacity) 158 { 159 BOOST_STATIC_ASSERT(has_capacity); 160 initialize(); 161 } 162 163 /** Construct a fixed-sized stack with a custom allocator 164 * 165 * \pre Must specify a capacity<> argument 166 * */ stack(allocator const & alloc)167 explicit stack(allocator const & alloc): 168 pool(alloc, capacity) 169 { 170 // Don't use BOOST_STATIC_ASSERT() here since it will be evaluated when compiling 171 // this function and this function may be compiled even when it isn't being used. 172 BOOST_ASSERT(has_capacity); 173 initialize(); 174 } 175 176 /** Construct a variable-sized stack 177 * 178 * Allocate n nodes initially for the freelist 179 * 180 * \pre Must \b not specify a capacity<> argument 181 * */ stack(size_type n)182 explicit stack(size_type n): 183 pool(node_allocator(), n) 184 { 185 // Don't use BOOST_STATIC_ASSERT() here since it will be evaluated when compiling 186 // this function and this function may be compiled even when it isn't being used. 187 BOOST_ASSERT(!has_capacity); 188 initialize(); 189 } 190 191 /** Construct a variable-sized stack with a custom allocator 192 * 193 * Allocate n nodes initially for the freelist 194 * 195 * \pre Must \b not specify a capacity<> argument 196 * */ 197 template <typename U> stack(size_type n,typename boost::allocator_rebind<node_allocator,U>::type const & alloc)198 stack(size_type n, typename boost::allocator_rebind<node_allocator, U>::type const & alloc): 199 pool(alloc, n) 200 { 201 BOOST_STATIC_ASSERT(!has_capacity); 202 initialize(); 203 } 204 205 /** Allocate n nodes for freelist 206 * 207 * \pre only valid if no capacity<> argument given 208 * \note thread-safe, may block if memory allocator blocks 209 * 210 * */ reserve(size_type n)211 void reserve(size_type n) 212 { 213 // Don't use BOOST_STATIC_ASSERT() here since it will be evaluated when compiling 214 // this function and this function may be compiled even when it isn't being used. 215 BOOST_ASSERT(!has_capacity); 216 pool.template reserve<true>(n); 217 } 218 219 /** Allocate n nodes for freelist 220 * 221 * \pre only valid if no capacity<> argument given 222 * \note not thread-safe, may block if memory allocator blocks 223 * 224 * */ reserve_unsafe(size_type n)225 void reserve_unsafe(size_type n) 226 { 227 // Don't use BOOST_STATIC_ASSERT() here since it will be evaluated when compiling 228 // this function and this function may be compiled even when it isn't being used. 229 BOOST_ASSERT(!has_capacity); 230 pool.template reserve<false>(n); 231 } 232 233 /** Destroys stack, free all nodes from freelist. 234 * 235 * \note not thread-safe 236 * 237 * */ ~stack(void)238 ~stack(void) 239 { 240 T dummy; 241 while(unsynchronized_pop(dummy)) 242 {} 243 } 244 245 private: 246 #ifndef BOOST_DOXYGEN_INVOKED initialize(void)247 void initialize(void) 248 { 249 tos.store(tagged_node_handle(pool.null_handle(), 0)); 250 } 251 link_nodes_atomic(node * new_top_node,node * end_node)252 void link_nodes_atomic(node * new_top_node, node * end_node) 253 { 254 tagged_node_handle old_tos = tos.load(detail::memory_order_relaxed); 255 for (;;) { 256 tagged_node_handle new_tos (pool.get_handle(new_top_node), old_tos.get_tag()); 257 end_node->next = pool.get_handle(old_tos); 258 259 if (tos.compare_exchange_weak(old_tos, new_tos)) 260 break; 261 } 262 } 263 link_nodes_unsafe(node * new_top_node,node * end_node)264 void link_nodes_unsafe(node * new_top_node, node * end_node) 265 { 266 tagged_node_handle old_tos = tos.load(detail::memory_order_relaxed); 267 268 tagged_node_handle new_tos (pool.get_handle(new_top_node), old_tos.get_tag()); 269 end_node->next = pool.get_handle(old_tos); 270 271 tos.store(new_tos, memory_order_relaxed); 272 } 273 274 template <bool Threadsafe, bool Bounded, typename ConstIterator> prepare_node_list(ConstIterator begin,ConstIterator end,ConstIterator & ret)275 tuple<node*, node*> prepare_node_list(ConstIterator begin, ConstIterator end, ConstIterator & ret) 276 { 277 ConstIterator it = begin; 278 node * end_node = pool.template construct<Threadsafe, Bounded>(*it++); 279 if (end_node == NULL) { 280 ret = begin; 281 return make_tuple<node*, node*>(NULL, NULL); 282 } 283 284 node * new_top_node = end_node; 285 end_node->next = NULL; 286 287 BOOST_TRY { 288 /* link nodes */ 289 for (; it != end; ++it) { 290 node * newnode = pool.template construct<Threadsafe, Bounded>(*it); 291 if (newnode == NULL) 292 break; 293 newnode->next = new_top_node; 294 new_top_node = newnode; 295 } 296 } BOOST_CATCH (...) { 297 for (node * current_node = new_top_node; current_node != NULL;) { 298 node * next = current_node->next; 299 pool.template destruct<Threadsafe>(current_node); 300 current_node = next; 301 } 302 BOOST_RETHROW; 303 } 304 BOOST_CATCH_END 305 306 ret = it; 307 return make_tuple(new_top_node, end_node); 308 } 309 #endif 310 311 public: 312 /** Pushes object t to the stack. 313 * 314 * \post object will be pushed to the stack, if internal node can be allocated 315 * \returns true, if the push operation is successful. 316 * 317 * \note Thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated 318 * from the OS. This may not be lock-free. 319 * \throws if memory allocator throws 320 * */ push(T const & v)321 bool push(T const & v) 322 { 323 return do_push<false>(v); 324 } 325 326 /** Pushes object t to the stack. 327 * 328 * \post object will be pushed to the stack, if internal node can be allocated 329 * \returns true, if the push operation is successful. 330 * 331 * \note Thread-safe and non-blocking. If internal memory pool is exhausted, the push operation will fail 332 * */ bounded_push(T const & v)333 bool bounded_push(T const & v) 334 { 335 return do_push<true>(v); 336 } 337 338 #ifndef BOOST_DOXYGEN_INVOKED 339 private: 340 template <bool Bounded> do_push(T const & v)341 bool do_push(T const & v) 342 { 343 node * newnode = pool.template construct<true, Bounded>(v); 344 if (newnode == 0) 345 return false; 346 347 link_nodes_atomic(newnode, newnode); 348 return true; 349 } 350 351 template <bool Bounded, typename ConstIterator> do_push(ConstIterator begin,ConstIterator end)352 ConstIterator do_push(ConstIterator begin, ConstIterator end) 353 { 354 node * new_top_node; 355 node * end_node; 356 ConstIterator ret; 357 358 tie(new_top_node, end_node) = prepare_node_list<true, Bounded>(begin, end, ret); 359 if (new_top_node) 360 link_nodes_atomic(new_top_node, end_node); 361 362 return ret; 363 } 364 365 public: 366 #endif 367 368 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated. 369 * 370 * \return iterator to the first element, which has not been pushed 371 * 372 * \note Operation is applied atomically 373 * \note Thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated 374 * from the OS. This may not be lock-free. 375 * \throws if memory allocator throws 376 */ 377 template <typename ConstIterator> push(ConstIterator begin,ConstIterator end)378 ConstIterator push(ConstIterator begin, ConstIterator end) 379 { 380 return do_push<false, ConstIterator>(begin, end); 381 } 382 383 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated. 384 * 385 * \return iterator to the first element, which has not been pushed 386 * 387 * \note Operation is applied atomically 388 * \note Thread-safe and non-blocking. If internal memory pool is exhausted, the push operation will fail 389 * \throws if memory allocator throws 390 */ 391 template <typename ConstIterator> bounded_push(ConstIterator begin,ConstIterator end)392 ConstIterator bounded_push(ConstIterator begin, ConstIterator end) 393 { 394 return do_push<true, ConstIterator>(begin, end); 395 } 396 397 398 /** Pushes object t to the stack. 399 * 400 * \post object will be pushed to the stack, if internal node can be allocated 401 * \returns true, if the push operation is successful. 402 * 403 * \note Not thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated 404 * from the OS. This may not be lock-free. 405 * \throws if memory allocator throws 406 * */ unsynchronized_push(T const & v)407 bool unsynchronized_push(T const & v) 408 { 409 node * newnode = pool.template construct<false, false>(v); 410 if (newnode == 0) 411 return false; 412 413 link_nodes_unsafe(newnode, newnode); 414 return true; 415 } 416 417 /** Pushes as many objects from the range [begin, end) as freelist node can be allocated. 418 * 419 * \return iterator to the first element, which has not been pushed 420 * 421 * \note Not thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will be allocated 422 * from the OS. This may not be lock-free. 423 * \throws if memory allocator throws 424 */ 425 template <typename ConstIterator> unsynchronized_push(ConstIterator begin,ConstIterator end)426 ConstIterator unsynchronized_push(ConstIterator begin, ConstIterator end) 427 { 428 node * new_top_node; 429 node * end_node; 430 ConstIterator ret; 431 432 tie(new_top_node, end_node) = prepare_node_list<false, false>(begin, end, ret); 433 if (new_top_node) 434 link_nodes_unsafe(new_top_node, end_node); 435 436 return ret; 437 } 438 439 440 /** Pops object from stack. 441 * 442 * \post if pop operation is successful, object will be copied to ret. 443 * \returns true, if the pop operation is successful, false if stack was empty. 444 * 445 * \note Thread-safe and non-blocking 446 * 447 * */ pop(T & ret)448 bool pop(T & ret) 449 { 450 return pop<T>(ret); 451 } 452 453 /** Pops object from stack. 454 * 455 * \pre type T must be convertible to U 456 * \post if pop operation is successful, object will be copied to ret. 457 * \returns true, if the pop operation is successful, false if stack was empty. 458 * 459 * \note Thread-safe and non-blocking 460 * 461 * */ 462 template <typename U> pop(U & ret)463 bool pop(U & ret) 464 { 465 BOOST_STATIC_ASSERT((boost::is_convertible<T, U>::value)); 466 detail::consume_via_copy<U> consumer(ret); 467 468 return consume_one(consumer); 469 } 470 471 472 /** Pops object from stack. 473 * 474 * \post if pop operation is successful, object will be copied to ret. 475 * \returns true, if the pop operation is successful, false if stack was empty. 476 * 477 * \note Not thread-safe, but non-blocking 478 * 479 * */ unsynchronized_pop(T & ret)480 bool unsynchronized_pop(T & ret) 481 { 482 return unsynchronized_pop<T>(ret); 483 } 484 485 /** Pops object from stack. 486 * 487 * \pre type T must be convertible to U 488 * \post if pop operation is successful, object will be copied to ret. 489 * \returns true, if the pop operation is successful, false if stack was empty. 490 * 491 * \note Not thread-safe, but non-blocking 492 * 493 * */ 494 template <typename U> unsynchronized_pop(U & ret)495 bool unsynchronized_pop(U & ret) 496 { 497 BOOST_STATIC_ASSERT((boost::is_convertible<T, U>::value)); 498 tagged_node_handle old_tos = tos.load(detail::memory_order_relaxed); 499 node * old_tos_pointer = pool.get_pointer(old_tos); 500 501 if (!pool.get_pointer(old_tos)) 502 return false; 503 504 node * new_tos_ptr = pool.get_pointer(old_tos_pointer->next); 505 tagged_node_handle new_tos(pool.get_handle(new_tos_ptr), old_tos.get_next_tag()); 506 507 tos.store(new_tos, memory_order_relaxed); 508 detail::copy_payload(old_tos_pointer->v, ret); 509 pool.template destruct<false>(old_tos); 510 return true; 511 } 512 513 /** consumes one element via a functor 514 * 515 * pops one element from the stack and applies the functor on this object 516 * 517 * \returns true, if one element was consumed 518 * 519 * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking 520 * */ 521 template <typename Functor> consume_one(Functor & f)522 bool consume_one(Functor & f) 523 { 524 tagged_node_handle old_tos = tos.load(detail::memory_order_consume); 525 526 for (;;) { 527 node * old_tos_pointer = pool.get_pointer(old_tos); 528 if (!old_tos_pointer) 529 return false; 530 531 tagged_node_handle new_tos(old_tos_pointer->next, old_tos.get_next_tag()); 532 533 if (tos.compare_exchange_weak(old_tos, new_tos)) { 534 f(old_tos_pointer->v); 535 pool.template destruct<true>(old_tos); 536 return true; 537 } 538 } 539 } 540 541 /// \copydoc boost::lockfree::stack::consume_one(Functor & rhs) 542 template <typename Functor> consume_one(Functor const & f)543 bool consume_one(Functor const & f) 544 { 545 tagged_node_handle old_tos = tos.load(detail::memory_order_consume); 546 547 for (;;) { 548 node * old_tos_pointer = pool.get_pointer(old_tos); 549 if (!old_tos_pointer) 550 return false; 551 552 tagged_node_handle new_tos(old_tos_pointer->next, old_tos.get_next_tag()); 553 554 if (tos.compare_exchange_weak(old_tos, new_tos)) { 555 f(old_tos_pointer->v); 556 pool.template destruct<true>(old_tos); 557 return true; 558 } 559 } 560 } 561 562 /** consumes all elements via a functor 563 * 564 * sequentially pops all elements from the stack and applies the functor on each object 565 * 566 * \returns number of elements that are consumed 567 * 568 * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking 569 * */ 570 template <typename Functor> consume_all(Functor & f)571 size_t consume_all(Functor & f) 572 { 573 size_t element_count = 0; 574 while (consume_one(f)) 575 element_count += 1; 576 577 return element_count; 578 } 579 580 /// \copydoc boost::lockfree::stack::consume_all(Functor & rhs) 581 template <typename Functor> consume_all(Functor const & f)582 size_t consume_all(Functor const & f) 583 { 584 size_t element_count = 0; 585 while (consume_one(f)) 586 element_count += 1; 587 588 return element_count; 589 } 590 591 /** consumes all elements via a functor 592 * 593 * atomically pops all elements from the stack and applies the functor on each object 594 * 595 * \returns number of elements that are consumed 596 * 597 * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking 598 * */ 599 template <typename Functor> consume_all_atomic(Functor & f)600 size_t consume_all_atomic(Functor & f) 601 { 602 size_t element_count = 0; 603 tagged_node_handle old_tos = tos.load(detail::memory_order_consume); 604 605 for (;;) { 606 node * old_tos_pointer = pool.get_pointer(old_tos); 607 if (!old_tos_pointer) 608 return 0; 609 610 tagged_node_handle new_tos(pool.null_handle(), old_tos.get_next_tag()); 611 612 if (tos.compare_exchange_weak(old_tos, new_tos)) 613 break; 614 } 615 616 tagged_node_handle nodes_to_consume = old_tos; 617 618 for(;;) { 619 node * node_pointer = pool.get_pointer(nodes_to_consume); 620 f(node_pointer->v); 621 element_count += 1; 622 623 node * next_node = pool.get_pointer(node_pointer->next); 624 625 if (!next_node) { 626 pool.template destruct<true>(nodes_to_consume); 627 break; 628 } 629 630 tagged_node_handle next(pool.get_handle(next_node), nodes_to_consume.get_next_tag()); 631 pool.template destruct<true>(nodes_to_consume); 632 nodes_to_consume = next; 633 } 634 635 return element_count; 636 } 637 638 /// \copydoc boost::lockfree::stack::consume_all_atomic(Functor & rhs) 639 template <typename Functor> consume_all_atomic(Functor const & f)640 size_t consume_all_atomic(Functor const & f) 641 { 642 size_t element_count = 0; 643 tagged_node_handle old_tos = tos.load(detail::memory_order_consume); 644 645 for (;;) { 646 node * old_tos_pointer = pool.get_pointer(old_tos); 647 if (!old_tos_pointer) 648 return 0; 649 650 tagged_node_handle new_tos(pool.null_handle(), old_tos.get_next_tag()); 651 652 if (tos.compare_exchange_weak(old_tos, new_tos)) 653 break; 654 } 655 656 tagged_node_handle nodes_to_consume = old_tos; 657 658 for(;;) { 659 node * node_pointer = pool.get_pointer(nodes_to_consume); 660 f(node_pointer->v); 661 element_count += 1; 662 663 node * next_node = pool.get_pointer(node_pointer->next); 664 665 if (!next_node) { 666 pool.template destruct<true>(nodes_to_consume); 667 break; 668 } 669 670 tagged_node_handle next(pool.get_handle(next_node), nodes_to_consume.get_next_tag()); 671 pool.template destruct<true>(nodes_to_consume); 672 nodes_to_consume = next; 673 } 674 675 return element_count; 676 } 677 678 /** consumes all elements via a functor 679 * 680 * atomically pops all elements from the stack and applies the functor on each object in reversed order 681 * 682 * \returns number of elements that are consumed 683 * 684 * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking 685 * */ 686 template <typename Functor> consume_all_atomic_reversed(Functor & f)687 size_t consume_all_atomic_reversed(Functor & f) 688 { 689 size_t element_count = 0; 690 tagged_node_handle old_tos = tos.load(detail::memory_order_consume); 691 692 for (;;) { 693 node * old_tos_pointer = pool.get_pointer(old_tos); 694 if (!old_tos_pointer) 695 return 0; 696 697 tagged_node_handle new_tos(pool.null_handle(), old_tos.get_next_tag()); 698 699 if (tos.compare_exchange_weak(old_tos, new_tos)) 700 break; 701 } 702 703 tagged_node_handle nodes_to_consume = old_tos; 704 705 node * last_node_pointer = NULL; 706 tagged_node_handle nodes_in_reversed_order; 707 for(;;) { 708 node * node_pointer = pool.get_pointer(nodes_to_consume); 709 node * next_node = pool.get_pointer(node_pointer->next); 710 711 node_pointer->next = pool.get_handle(last_node_pointer); 712 last_node_pointer = node_pointer; 713 714 if (!next_node) { 715 nodes_in_reversed_order = nodes_to_consume; 716 break; 717 } 718 719 tagged_node_handle next(pool.get_handle(next_node), nodes_to_consume.get_next_tag()); 720 nodes_to_consume = next; 721 } 722 723 for(;;) { 724 node * node_pointer = pool.get_pointer(nodes_in_reversed_order); 725 f(node_pointer->v); 726 element_count += 1; 727 728 node * next_node = pool.get_pointer(node_pointer->next); 729 730 if (!next_node) { 731 pool.template destruct<true>(nodes_in_reversed_order); 732 break; 733 } 734 735 tagged_node_handle next(pool.get_handle(next_node), nodes_in_reversed_order.get_next_tag()); 736 pool.template destruct<true>(nodes_in_reversed_order); 737 nodes_in_reversed_order = next; 738 } 739 740 return element_count; 741 } 742 743 /// \copydoc boost::lockfree::stack::consume_all_atomic_reversed(Functor & rhs) 744 template <typename Functor> consume_all_atomic_reversed(Functor const & f)745 size_t consume_all_atomic_reversed(Functor const & f) 746 { 747 size_t element_count = 0; 748 tagged_node_handle old_tos = tos.load(detail::memory_order_consume); 749 750 for (;;) { 751 node * old_tos_pointer = pool.get_pointer(old_tos); 752 if (!old_tos_pointer) 753 return 0; 754 755 tagged_node_handle new_tos(pool.null_handle(), old_tos.get_next_tag()); 756 757 if (tos.compare_exchange_weak(old_tos, new_tos)) 758 break; 759 } 760 761 tagged_node_handle nodes_to_consume = old_tos; 762 763 node * last_node_pointer = NULL; 764 tagged_node_handle nodes_in_reversed_order; 765 for(;;) { 766 node * node_pointer = pool.get_pointer(nodes_to_consume); 767 node * next_node = pool.get_pointer(node_pointer->next); 768 769 node_pointer->next = pool.get_handle(last_node_pointer); 770 last_node_pointer = node_pointer; 771 772 if (!next_node) { 773 nodes_in_reversed_order = nodes_to_consume; 774 break; 775 } 776 777 tagged_node_handle next(pool.get_handle(next_node), nodes_to_consume.get_next_tag()); 778 nodes_to_consume = next; 779 } 780 781 for(;;) { 782 node * node_pointer = pool.get_pointer(nodes_in_reversed_order); 783 f(node_pointer->v); 784 element_count += 1; 785 786 node * next_node = pool.get_pointer(node_pointer->next); 787 788 if (!next_node) { 789 pool.template destruct<true>(nodes_in_reversed_order); 790 break; 791 } 792 793 tagged_node_handle next(pool.get_handle(next_node), nodes_in_reversed_order.get_next_tag()); 794 pool.template destruct<true>(nodes_in_reversed_order); 795 nodes_in_reversed_order = next; 796 } 797 798 return element_count; 799 } 800 /** 801 * \return true, if stack is empty. 802 * 803 * \note It only guarantees that at some point during the execution of the function the stack has been empty. 804 * It is rarely practical to use this value in program logic, because the stack can be modified by other threads. 805 * */ empty(void) const806 bool empty(void) const 807 { 808 return pool.get_pointer(tos.load()) == NULL; 809 } 810 811 private: 812 #ifndef BOOST_DOXYGEN_INVOKED 813 detail::atomic<tagged_node_handle> tos; 814 815 static const int padding_size = BOOST_LOCKFREE_CACHELINE_BYTES - sizeof(tagged_node_handle); 816 char padding[padding_size]; 817 818 pool_t pool; 819 #endif 820 }; 821 822 } /* namespace lockfree */ 823 } /* namespace boost */ 824 825 #endif /* BOOST_LOCKFREE_STACK_HPP_INCLUDED */ 826