From ce962f9688e9d4e0d24d365f728dde1f7743b3ff Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Thu, 13 Oct 2016 20:17:01 +0200 Subject: [PATCH] (un)bounded_channel renamed to (un)bounded_queue --- doc/asio.qbk | 12 +- doc/fibers.qbk | 2 +- doc/fibers.xml | 733 +- doc/html/fiber/stack/fixedsize.html | 105 - doc/html/fiber/stack/pooled_fixedsize.html | 132 - doc/html/fiber/stack/protected_fixedsize.html | 128 - .../protected_fixedsize/pooled_fixedsize.html | 9374 ----------------- doc/html/fiber/stack/segmented_stack.html | 129 - doc/html/fiber/synchronization.html | 2 +- doc/html/fiber/synchronization/barriers.html | 6 +- doc/html/fiber/synchronization/futures.html | 6 +- .../{channels.html => queues.html} | 428 +- .../wait_all__collecting_all_exceptions.html | 6 +- .../when_all__return_values.html | 96 +- .../when_all_until_first_exception.html | 34 +- .../when_any__heterogeneous_types.html | 16 +- ..._outcome__whether_result_or_exception.html | 42 +- .../when_any__produce_first_success.html | 22 +- .../when_any/when_any__return_value.html | 24 +- doc/html/fiber_HTML.manifest | 2 +- doc/html/index.html | 4 +- doc/{channel.qbk => queue.qbk} | 246 +- doc/when_any.qbk | 64 +- examples/asio/ps/publisher.cpp | 6 +- examples/asio/ps/server.cpp | 138 +- examples/asio/ps/subscriber.cpp | 6 +- examples/ping_pong.cpp | 2 +- examples/wait_stuff.cpp | 200 +- include/boost/fiber/all.hpp | 4 +- ...{bounded_channel.hpp => bounded_queue.hpp} | 80 +- ...nnel_op_status.hpp => queue_op_status.hpp} | 2 +- ...ounded_channel.hpp => unbounded_queue.hpp} | 52 +- performance/fiber/skynet.cpp | 8 +- performance/fiber/skynet_shared.cpp | 8 +- test/Jamfile.v2 | 8 +- ...ch.cpp => test_bounded_queue_dispatch.cpp} | 244 +- ...l_post.cpp => test_bounded_queue_post.cpp} | 244 +- ....cpp => test_unbounded_queue_dispatch.cpp} | 116 +- ...post.cpp => test_unbounded_queue_post.cpp} | 116 +- 39 files changed, 1491 insertions(+), 11356 deletions(-) delete mode 100644 doc/html/fiber/stack/fixedsize.html delete mode 100644 doc/html/fiber/stack/pooled_fixedsize.html delete mode 100644 doc/html/fiber/stack/protected_fixedsize.html delete mode 100644 doc/html/fiber/stack/protected_fixedsize/pooled_fixedsize.html delete mode 100644 doc/html/fiber/stack/segmented_stack.html rename doc/html/fiber/synchronization/{channels.html => queues.html} (63%) rename doc/{channel.qbk => queue.qbk} (52%) rename include/boost/fiber/{bounded_channel.hpp => bounded_queue.hpp} (86%) rename include/boost/fiber/{channel_op_status.hpp => queue_op_status.hpp} (95%) rename include/boost/fiber/{unbounded_channel.hpp => unbounded_queue.hpp} (84%) rename test/{test_bounded_channel_dispatch.cpp => test_bounded_queue_dispatch.cpp} (66%) rename test/{test_bounded_channel_post.cpp => test_bounded_queue_post.cpp} (66%) rename test/{test_unbounded_channel_dispatch.cpp => test_unbounded_queue_dispatch.cpp} (59%) rename test/{test_unbounded_channel_post.cpp => test_unbounded_queue_post.cpp} (59%) diff --git a/doc/asio.qbk b/doc/asio.qbk index 83e084ff..4011862b 100644 --- a/doc/asio.qbk +++ b/doc/asio.qbk @@ -39,16 +39,16 @@ __yield_fiber__ abstracts the fiber in asio's context. { boost::system::error_code ec; - // read first message == channel name - std::string channel; + // read first message == queue name + std::string queue; boost::asio::async_read( socket_, - boost::asio::buffer( channel), + boost::asio::buffer( queue), yield[ec]); - if ( ec) throw std::runtime_error("no channel from subscriber"); + if ( ec) throw std::runtime_error("no queue from subscriber"); - // register new channel - reg_.subscribe( channel, shared_from_this() ); + // register new queue + reg_.subscribe( queue, shared_from_this() ); for (;;) { diff --git a/doc/fibers.qbk b/doc/fibers.qbk index 0ae71087..6cb7b073 100644 --- a/doc/fibers.qbk +++ b/doc/fibers.qbk @@ -205,7 +205,7 @@ object are running in the same thread. [include mutexes.qbk] [include condition_variables.qbk] [include barrier.qbk] -[include channel.qbk] +[include queue.qbk] [include futures.qbk] [endsect] [include fls.qbk] diff --git a/doc/fibers.xml b/doc/fibers.xml index c7be0b2f..70b41f36 100644 --- a/doc/fibers.xml +++ b/doc/fibers.xml @@ -1,6 +1,6 @@ - @@ -6848,45 +6848,45 @@ implementation’s -
- <link linkend="fiber.synchronization.channels">Channels</link> +
+ <link linkend="fiber.synchronization.queues">Queues</link> Boost.Fiber provides a bounded and a unbounded - channel suitable to synchonize fibers via message passing. + queue suitable to synchonize fibers via message passing. -typedef boost::fibers::unbounded_channel< int > channel_t; +typedef boost::fibers::unbounded_queue< int > queue_t; -void send( channel_t & channel) { +void send( queue_t & queue) { for ( int i = 0; i < 5; ++i) { - channel.push( i); + queue.push( i); } - channel.close(); + queue.close(); } -void recv( channel_t & channel) { +void recv( queue_t & queue) { int i; - while ( boost::fibers::channel_op_status::success == channel.pop(i) ) { + while ( boost::fibers::queue_op_status::success == queue.pop(i) ) { std::cout << "received " << i << std::endl; } } -channel_t channel; -boost::fibers::fiber f1( std::bind( send, ref( channel) ) ); -boost::fibers::fiber f2( std::bind( recv, ref( channel) ) ); +queue_t queue; +boost::fibers::fiber f1( std::bind( send, ref( queue) ) ); +boost::fibers::fiber f2( std::bind( recv, ref( queue) ) ); f1.join(); f2.join(); - - - Enumeration - channel_op_status + + + Enumeration + queue_op_status - channel operations return the state of the channel. + queue operations return the state of the queue. -enum class channel_op_status { +enum class queue_op_status { success, empty, full, @@ -6894,9 +6894,9 @@ implementation’s timeout }; - - + success @@ -6910,9 +6910,9 @@ implementation’s - - + empty @@ -6921,14 +6921,14 @@ implementation’s Effects: - channel is empty, operation failed. + queue is empty, operation failed. - - + full @@ -6937,14 +6937,14 @@ implementation’s Effects: - channel is full, operation failed. + queue is full, operation failed. - - + closed @@ -6953,14 +6953,14 @@ implementation’s Effects: - channel is closed, operation failed. + queue is closed, operation failed. - - + timeout @@ -6975,51 +6975,51 @@ implementation’s - - - Template - unbounded_channel<> + + + Template + unbounded_queue<> -#include <boost/fiber/unbounded_channel.hpp> +#include <boost/fiber/unbounded_queue.hpp> namespace boost { namespace fibers { template< typename T, typename Allocator = std::allocator< T > > -class unbounded_channel { +class unbounded_queue { public: typedef T value_type; - explicit unbounded_channel( Allocator const& alloc = Allocator() ) noexcept; + explicit unbounded_queue( Allocator const& alloc = Allocator() ) noexcept; - unbounded_channel( unbounded_channel const& other) = delete; - unbounded_channel & operator=( unbounded_channel const& other) = delete; + unbounded_queue( unbounded_queue const& other) = delete; + unbounded_queue & operator=( unbounded_queue const& other) = delete; void close() noexcept; - channel_op_status push( value_type const& va); - channel_op_status push( value_type && va); + queue_op_status push( value_type const& va); + queue_op_status push( value_type && va); - channel_op_status pop( value_type & va); + queue_op_status pop( value_type & va); value_type value_pop(); - channel_op_status try_pop( value_type & va); + queue_op_status try_pop( value_type & va); template< typename Rep, typename Period > - channel_op_status pop_wait_for( + queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Clock, typename Duration > - channel_op_status pop_wait_until( + queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time); }; }} - - Constructor + + Constructor -explicit unbounded_channel( Allocator const& alloc = Allocator() ) noexcept; +explicit unbounded_queue( Allocator const& alloc = Allocator() ) noexcept; @@ -7027,7 +7027,7 @@ implementation’s Effects: - Constructs an object of class unbounded_channel. + Constructs an object of class unbounded_queue. Internal nodes are allocated using alloc - C++11-allocators are supported. @@ -7056,9 +7056,9 @@ implementation’s - - - Member + + + Member function close() @@ -7070,7 +7070,7 @@ implementation’s Effects: - Deactivates the channel. No values can be put after calling this->close(). Fibers blocked in this - - - Member + + + Member function push() -channel_op_status push( value_type const& va); -channel_op_status push( value_type && va); +queue_op_status push( value_type const& va); +queue_op_status push( value_type && va); @@ -7121,8 +7121,8 @@ implementation’s Effects: - If channel is closed, returns closed. - Otherwise enqueues the value in the channel, wakes up a fiber blocked + If queue is closed, returns closed. + Otherwise enqueues the value in the queue, wakes up a fiber blocked on this->pop(), this-> - - - Member + + + Member function pop() -channel_op_status pop( value_type & va); +queue_op_status pop( value_type & va); @@ -7160,11 +7160,11 @@ implementation’s Effects: - Dequeues a value from the channel. If the channel is empty, the fiber - gets suspended until at least one new item is pushpush()ed (return value success and va contains dequeued - value) or the channel gets closeclose()d (return value closed). @@ -7179,9 +7179,9 @@ implementation’s - - - Member + + + Member function value_pop() @@ -7193,9 +7193,9 @@ implementation’s Effects: - Dequeues a value from the channel. If the channel is empty, the fiber - gets suspended until at least one new item is push()ed or the channel gets push()ed or the queue gets close()d (which throws an exception). @@ -7223,13 +7223,13 @@ implementation’s - - - Member + + + Member function try_pop() -channel_op_status try_pop( value_type & va); +queue_op_status try_pop( value_type & va); @@ -7237,8 +7237,8 @@ implementation’s Effects: - If channel is empty, returns empty. - If channel is closed, returns closed. + If queue is empty, returns empty. + If queue is closed, returns closed. Otherwise it returns success and va contains the dequeued value. @@ -7255,14 +7255,14 @@ implementation’s - - - Member + + + Member function pop_wait_for() template< typename Rep, typename Period > -channel_op_status pop_wait_for( +queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration) @@ -7276,12 +7276,12 @@ implementation’s role="identifier">chrono::duration and internally computes a timeout time as (system time + timeout_duration). - If channel is not empty, immediately dequeues a value from the channel. + If queue is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is push()ed (return value success and va contains dequeued - value), or the channel gets closeclose()d (return value closed), or the system time reaches the computed timeout time (return value timeout). @@ -7298,14 +7298,14 @@ implementation’s - - - Member + + + Member function pop_wait_until() template< typename Clock, typename Duration > -channel_op_status pop_wait_until( +queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time) @@ -7320,12 +7320,12 @@ implementation’s role="identifier">time_point< Clock, Duration >. - If channel is not empty, immediately dequeues a value from the channel. + If queue is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is push()ed (return value success and va contains dequeued - value), or the channel gets closeclose()d (return value closed), or the system time reaches the passed time_point (return value timeout). @@ -7342,72 +7342,72 @@ implementation’s - - - Template - bounded_channel<> + + + Template + bounded_queue<> -#include <boost/fiber/bounded_channel.hpp> +#include <boost/fiber/bounded_queue.hpp> namespace boost { namespace fibers { template< typename T, typename Allocator = std::allocator< T > > -class bounded_channel { +class bounded_queue { public: typedef T value_type; - bounded_channel( std::size_t wm, Allocator const& alloc = Allocator() ); - bounded_channel( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() ); + bounded_queue( std::size_t wm, Allocator const& alloc = Allocator() ); + bounded_queue( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() ); - bounded_channel( bounded_channel const& other) = delete; - bounded_channel & operator=( bounded_channel const& other) = delete; + bounded_queue( bounded_queue const& other) = delete; + bounded_queue & operator=( bounded_queue const& other) = delete; std::size_t upper_bound() const noexcept; std::size_t lower_bound() const noexcept; void close() noexcept; - channel_op_status push( value_type const& va); - channel_op_status push( value_type && va); + queue_op_status push( value_type const& va); + queue_op_status push( value_type && va); template< typename Rep, typename Period > - channel_op_status push_wait_for( + queue_op_status push_wait_for( value_type const& va, std::chrono::duration< Rep, Period > const& timeout_duration); - channel_op_status push_wait_for( value_type && va, + queue_op_status push_wait_for( value_type && va, std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Clock, typename Duration > - channel_op_status push_wait_until( + queue_op_status push_wait_until( value_type const& va, std::chrono::time_point< Clock, Duration > const& timeout_time); template< typename Clock, typename Duration > - channel_op_status push_wait_until( + queue_op_status push_wait_until( value_type && va, std::chrono::time_point< Clock, Duration > const& timeout_time); - channel_op_status try_push( value_type const& va); - channel_op_status try_push( value_type && va); + queue_op_status try_push( value_type const& va); + queue_op_status try_push( value_type && va); - channel_op_status pop( value_type & va); + queue_op_status pop( value_type & va); value_type value_pop(); template< typename Rep, typename Period > - channel_op_status pop_wait_for( + queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Clock, typename Duration > - channel_op_status pop_wait_until( + queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time); - channel_op_status try_pop( value_type & va); + queue_op_status try_pop( value_type & va); }; }} - - Constructor + + Constructor -bounded_channel( std::size_t wm, Allocator const& alloc = Allocator() ); -bounded_channel( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() ); +bounded_queue( std::size_t wm, Allocator const& alloc = Allocator() ); +bounded_queue( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() ); @@ -7424,14 +7424,14 @@ implementation’s Effects: - Constructs an object of class bounded_channel. + Constructs an object of class bounded_queue. The constructor with two arguments constructs an object of class bounded_channel with a high-watermark + role="identifier">bounded_queue with a high-watermark of hwm and a low-watermark of lwm items. The constructor with one std::size_t argument is effectively the - same as bounded_channelbounded_queue(wm, (wm-Notes: - Once the number of values in the channel reaches hwm, + Once the number of values in the queue reaches hwm, any call to push(), push_wait_for() or push_wait_until() will block until the number of values - in the channel is at most lwm. + in the queue is at most lwm. That is, if lwm < (hwm-1), - the channel can be in a state in which pushpush(), push_wait_for() or push_wait_until() calls will block (channel is full) - even though the number of values in the channel is less than () calls will block (queue is full) + even though the number of values in the queue is less than hwm. @@ -7497,9 +7497,9 @@ implementation’s - - - Member + + + Member function upper_bound() @@ -7526,9 +7526,9 @@ implementation’s - - - Member + + + Member function lower_bound() @@ -7555,9 +7555,9 @@ implementation’s - - - Member + + + Member function close() @@ -7569,7 +7569,7 @@ implementation’s Effects: - Deactivates the channel. No values can be put after calling this->close(). Fibers blocked in this - - - Member - function push() + + + Member function + push() -channel_op_status push( value_type const& va); -channel_op_status push( value_type && va); +queue_op_status push( value_type const& va); +queue_op_status push( value_type && va); @@ -7620,21 +7620,21 @@ implementation’s Effects: - If channel is closed, returns closed. - If channel is not full, enqueues the value in the channel, wakes up - a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. + If queue is closed, returns closed. + If queue is not full, enqueues the value in the queue, wakes up a fiber + blocked on this->pop(), + this->value_pop(), + this->pop_wait_for() + or this->pop_wait_until() + and returns success. Otherwise the calling fiber is suspended until the number of values - in the channel drops to lwm + in the queue drops to lwm (return value success)or - the channel is closeclose()d (return value closed). @@ -7650,19 +7650,19 @@ implementation’s - - - Member + + + Member function push_wait_for() template< typename Rep, typename Period > -channel_op_status push_wait_for( +queue_op_status push_wait_for( value_type const& va, std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Rep, typename Period > -channel_op_status push_wait_for( +queue_op_status push_wait_for( value_type && va, std::chrono::duration< Rep, Period > const& timeout_duration); @@ -7676,21 +7676,21 @@ implementation’s role="identifier">chrono::duration and internally computes a time_point as (system time + timeout_duration). - If channel is closed, returns closed. - If channel is not full, enqueues the value in the channel, wakes up - a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. + If queue is closed, returns closed. + If queue is not full, enqueues the value in the queue, wakes up a fiber + blocked on this->pop(), + this->value_pop(), + this->pop_wait_for() + or this->pop_wait_until() + and returns success. Otherwise the calling fiber is suspended until the number of values - in the channel drops to lwm + in the queue drops to lwm (return value success), - the channel is closeclose()d (return value closed), or the system time reaches the computed time_point (return value timeout). @@ -7708,19 +7708,19 @@ implementation’s - - - Member + + + Member function push_wait_until() template< typename Clock, typename Duration > -channel_op_status push_wait_until( +queue_op_status push_wait_until( value_type const& va, std::chrono::time_point< Clock, Duration > const& timeout_time); template< typename Clock, typename Duration > -channel_op_status push_wait_until( +queue_op_status push_wait_until( value_type && va, std::chrono::time_point< Clock, Duration > const& timeout_time); @@ -7731,22 +7731,21 @@ implementation’s Accepts an absolute timeout_time - in any supported time_point type. If channel is closed, returns closed. If channel is not full, enqueues - the value in the channel, wakes up a fiber blocked on this->pop(), - this->value_pop(), - this->pop_wait_for() - or this->pop_wait_until() - and returns success. + in any supported time_point type. If queue is closed, returns closed. If queue is not full, enqueues + the value in the queue, wakes up a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. Otherwise the calling fiber is suspended until the number of values - in the channel drops to lwm + in the queue drops to lwm (return value success), - the channel is closeclose()d (return value closed), or the system time reaches the passed time_point (return value timeout). @@ -7764,14 +7763,14 @@ implementation’s - - - Member + + + Member function try_push() -channel_op_status try_push( value_type const& va); -channel_op_status try_push( value_type && va); +queue_op_status try_push( value_type const& va); +queue_op_status try_push( value_type && va); @@ -7779,9 +7778,9 @@ implementation’s Effects: - If channel is full, returns full. - If channel is closed, returns closed. - Otherwise enqueues the value in the channel, wakes up a fiber blocked + If queue is full, returns full. + If queue is closed, returns closed. + Otherwise enqueues the value in the queue, wakes up a fiber blocked on this->pop(), this-> - - - Member - function pop() + + + Member function + pop() -channel_op_status pop( value_type & va); +queue_op_status pop( value_type & va); @@ -7819,13 +7818,13 @@ implementation’s Effects: - Dequeues a value from the channel. If the channel is empty, the fiber - gets suspended until at least one new item is pushpush()ed (return value success and va contains dequeued - value) or the channel gets closeclose()d (return value closed). - Once the number of items remaining in the channel drops to lwm, any fibers blocked on push(), push_wait_for() @@ -7844,9 +7843,9 @@ implementation’s - - - Member + + + Member function value_pop() @@ -7858,12 +7857,12 @@ implementation’s Effects: - Dequeues a value from the channel. If the channel is empty, the fiber - gets suspended until at least one new item is push()ed or the channel gets push()ed or the queue gets close()d (which throws an exception). Once the number of items remaining in - the channel drops to lwm, + the queue drops to lwm, any fibers blocked on push(), push_wait_for() or push_wait_until - - - Member + + + Member function try_pop() -channel_op_status try_pop( value_type & va); +queue_op_status try_pop( value_type & va); @@ -7907,11 +7906,11 @@ implementation’s Effects: - If channel is empty, returns empty. - If channel is closed, returns closed. + If queue is empty, returns empty. + If queue is closed, returns closed. Otherwise it returns success and va contains the - dequeued value. Once the number of items remaining in the channel drops + dequeued value. Once the number of items remaining in the queue drops to lwm, any fibers blocked on push(), push_wait_for() @@ -7930,14 +7929,14 @@ implementation’s - - - Member + + + Member function pop_wait_for() template< typename Rep, typename Period > -channel_op_status pop_wait_for( +queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration) @@ -7951,16 +7950,16 @@ implementation’s role="identifier">chrono::duration and internally computes a timeout time as (system time + timeout_duration). - If channel is not empty, immediately dequeues a value from the channel. + If queue is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is push()ed (return value success and va contains dequeued - value), or the channel gets closeclose()d (return value closed), or the system time reaches the computed timeout time (return value timeout). Once the - number of items remaining in the channel drops to lwm, + number of items remaining in the queue drops to lwm, any fibers blocked on push(), push_wait_for() or push_wait_until - - - Member + + + Member function pop_wait_until() template< typename Clock, typename Duration > -channel_op_status pop_wait_until( +queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time) @@ -8000,16 +7999,16 @@ implementation’s role="identifier">time_point< Clock, Duration >. - If channel is not empty, immediately dequeues a value from the channel. + If queue is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is push()ed (return value success and va contains dequeued - value), or the channel gets closeclose()d (return value closed), or the system time reaches the passed time_point (return value timeout). - Once the number of items remaining in the channel drops to lwm, any fibers blocked on push(), push_wait_for() @@ -12272,8 +12271,8 @@ before One tactic would be to adapt our Done class to store the first of the return values, rather than a simple bool. - However, we choose instead to use a unbounded_channel<>. - We'll only need to enqueue the first value, so we'll unbounded_channel::close() it + However, we choose instead to use a unbounded_queue<>. + We'll only need to enqueue the first value, so we'll unbounded_queue::close() it once we've retrieved that value. Subsequent push() calls will return closed. @@ -12286,16 +12285,16 @@ before typename std::result_of< Fn() >::type wait_first_value( Fn && function, Fns && ... functions) { typedef typename std::result_of< Fn() >::type return_t; - typedef boost::fibers::unbounded_channel< return_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< return_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_value_impl< return_t >( channelp, + wait_first_value_impl< return_t >( queuep, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // retrieve the first value - return_t value( channelp->value_pop() ); - // close the channel: no subsequent push() has to succeed - channelp->close(); + return_t value( queuep->value_pop() ); + // close the queue: no subsequent push() has to succeed + queuep->close(); return value; } @@ -12306,12 +12305,12 @@ before template< typename T, typename Fn > -void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_channel< T > > channel, +void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_queue< T > > queue, Fn && function) { - boost::fibers::fiber( [channel, function](){ - // Ignore channel_op_status returned by push(): + boost::fibers::fiber( [queue, function](){ + // Ignore queue_op_status returned by push(): // might be closed; we simply don't care. - channel->push( function() ); + queue->push( function() ); }).detach(); } @@ -12349,7 +12348,7 @@ before awaiting the first result. We can use future<> to transport either a return value or an exception. Therefore, we will change wait_first_value()'s unbounded_channel<> to + role="special">()'s unbounded_queue<> to hold future< T > items instead of simply T. @@ -12364,21 +12363,21 @@ before template< typename Fn, typename ... Fns > typename std::result_of< Fn() >::type wait_first_outcome( Fn && function, Fns && ... functions) { - // In this case, the value we pass through the channel is actually a + // In this case, the value we pass through the queue is actually a // future -- which is already ready. future can carry either a value or an // exception. typedef typename std::result_of< Fn() >::type return_t; typedef boost::fibers::future< return_t > future_t; - typedef boost::fibers::unbounded_channel< future_t > channel_t; - auto channelp(std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< future_t > queue_t; + auto queuep(std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_outcome_impl< return_t >( channelp, + wait_first_outcome_impl< return_t >( queuep, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // retrieve the first future - future_t future( channelp->value_pop() ); - // close the channel: no subsequent push() has to succeed - channelp->close(); + future_t future( queuep->value_pop() ); + // close the queue: no subsequent push() has to succeed + queuep->close(); // either return value or throw exception return future.get(); } @@ -12387,21 +12386,21 @@ before So far so good — but there's a timing issue. How should we obtain the future<> - to unbounded_channel::push() on the channel? + to unbounded_queue::push() on the queue? We could call fibers::async(). That would certainly produce a future<> for the task function. The trouble is that it would return too quickly! We only want future<> - items for completed tasks on our unbounded_channelcompleted tasks on our unbounded_queue<>. In fact, we only want the future<> for the one that completes first. If each fiber launched by wait_first_outcome() were to push() the result of calling async(), the channel would only ever report the + role="special">(), the queue would only ever report the result of the leftmost task item — not the one that completes most quickly. @@ -12412,7 +12411,7 @@ before role="special">() once per future<> instance! And if there were an exception, it would be rethrown inside the helper fiber at the producer - end of the channel, rather than propagated to the consumer end. + end of the queue, rather than propagated to the consumer end. We could call future::wait(). That would block the helper fiber @@ -12428,26 +12427,26 @@ before role="special"><> to a new fiber — that is, in fact, what async() does — in this case, we're already running in the helper fiber at the producer - end of the channel! We can simply call the call the packaged_task<>. On return from that call, the task function has completed, meaning that the future<> obtained from the packaged_task<> is certain to be ready. At that point we can simply push() it to the channel. + role="special">() it to the queue. -template< typename T, typename CHANNELP, typename Fn > -void wait_first_outcome_impl( CHANNELP channel, Fn && function) { +template< typename T, typename QUEUEP, typename Fn > +void wait_first_outcome_impl( QUEUEP queue, Fn && function) { boost::fibers::fiber( // Use std::bind() here for C++11 compatibility. C++11 lambda capture // can't move a move-only Fn type, but bind() can. Let bind() move the - // channel pointer and the function into the bound object, passing + // queue pointer and the function into the bound object, passing // references into the lambda. std::bind( - []( CHANNELP & channel, + []( QUEUEP & queue, typename std::decay< Fn >::type & function) { // Instantiate a packaged_task to capture any exception thrown by // function. @@ -12456,11 +12455,11 @@ before // function() to have completed BEFORE we push the future. task(); // Pass the corresponding future to consumer. Ignore - // channel_op_status returned by push(): might be closed; we + // queue_op_status returned by push(): might be closed; we // simply don't care. - channel->push( task.get_future() ); + queue->push( task.get_future() ); }, - channel, + queue, std::forward< Fn >( function) )).detach(); } @@ -12509,7 +12508,7 @@ before first! - Given the unbounded_channelunbounded_queue< future< T > > @@ -12572,13 +12571,13 @@ before Instead of retrieving only the first future<> from the channel, we must now loop + role="special"><> from the queue, we must now loop over future<> items. Of course we must limit that iteration! If we launch only count producer fibers, the (count+1)st -unbounded_channel::pop() call +unbounded_queue::pop() call would block forever. @@ -12596,7 +12595,7 @@ before nullptr, though, we collect it into our pending exception_list and loop back for the next future<> from the channel. + role="special"><> from the queue. If we fall out of the loop — if every single task fiber threw an exception @@ -12610,15 +12609,15 @@ before typename std::result_of< Fn() >::type wait_first_success( Fn && function, Fns && ... functions) { std::size_t count( 1 + sizeof ... ( functions) ); - // In this case, the value we pass through the channel is actually a + // In this case, the value we pass through the queue is actually a // future -- which is already ready. future can carry either a value or an // exception. typedef typename std::result_of< typename std::decay< Fn >::type() >::type return_t; typedef boost::fibers::future< return_t > future_t; - typedef boost::fibers::unbounded_channel< future_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< future_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_outcome_impl< return_t >( channelp, + wait_first_outcome_impl< return_t >( queuep, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // instantiate exception_list, just in case @@ -12626,13 +12625,13 @@ before // retrieve up to 'count' results -- but stop there! for ( std::size_t i = 0; i < count; ++i) { // retrieve the next future - future_t future( channelp->value_pop() ); + future_t future( queuep->value_pop() ); // retrieve exception_ptr if any std::exception_ptr error( future.get_exception_ptr() ); // if no error, then yay, return value if ( ! error) { - // close the channel: no subsequent push() has to succeed - channelp->close(); + // close the queue: no subsequent push() has to succeed + queuep->close(); // show caller the value we got return future.get(); } @@ -12679,7 +12678,7 @@ before role="identifier">variant<T0, T1, ...> as the channel's value type rather + role="special">...> as the queue's value type rather than the common T! @@ -12696,17 +12695,17 @@ before template< typename ... Fns > boost::variant< typename std::result_of< Fns() >::type ... > wait_first_value_het( Fns && ... functions) { - // Use unbounded_channel<boost::variant<T1, T2, ...>>; see remarks above. + // Use unbounded_queue<boost::variant<T1, T2, ...>>; see remarks above. typedef boost::variant< typename std::result_of< Fns() >::type ... > return_t; - typedef boost::fibers::unbounded_channel< return_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< return_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_value_impl< return_t >( channelp, + wait_first_value_impl< return_t >( queuep, std::forward< Fns >( functions) ... ); // retrieve the first value - return_t value( channelp->value_pop() ); - // close the channel: no subsequent push() has to succeed - channelp->close(); + return_t value( queuep->value_pop() ); + // close the queue: no subsequent push() has to succeed + queuep->close(); return value; } @@ -12835,8 +12834,8 @@ before As soon as we want to collect return values from all the task functions, we can see right away how to reuse wait_first_value()'s - channel<T> for the purpose. All we have to do is avoid closing it - after the first value! + queue<T> for the purpose. All we have to do is avoid closing it after + the first value! But in fact, collecting multiple values raises an interesting question: @@ -12847,13 +12846,13 @@ before Fortunately we can present both APIs. Let's define wait_all_values_source() to return shared_ptr<unbounded_channel<unbounded_queue<T>>. - We could have used either bounded_channel<> or - unbounded_channel<>. We chose unbounded_channel<> + We could have used either bounded_queue<> or + unbounded_queue<>. We chose unbounded_queue<> on the assumption that its simpler semantics imply a cheaper implementation. @@ -12873,13 +12872,13 @@ before vector_t results; results.reserve( count); - // get channel - std::shared_ptr< boost::fibers::unbounded_channel< return_t > > channel = + // get queue + std::shared_ptr< boost::fibers::unbounded_queue< return_t > > queue = wait_all_values_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // fill results vector return_t value; - while ( boost::fibers::channel_op_status::success == channel->pop(value) ) { + while ( boost::fibers::queue_op_status::success == queue->pop(value) ) { results.push_back( value); } // return vector to caller @@ -12902,89 +12901,89 @@ before As you can see from the loop in wait_all_values(), instead of requiring its caller to count values, we define wait_all_values_source() to unbounded_channel::close() the - channel when done. But how do we do that? Each producer fiber is independent. - It has no idea whether it is the last one to unbounded_channel::push() a + role="special">() to unbounded_queue::close() the + queue when done. But how do we do that? Each producer fiber is independent. + It has no idea whether it is the last one to unbounded_queue::push() a value. - We can address that problem with a counting - façade for the unbounded_channelWe can address that problem with a counting façade + for the unbounded_queue<>. In fact, our façade need only - support the producer end of the channel. + support the producer end of the queue. -// Introduce a channel facade that closes the channel once a specific number +// Introduce a queue facade that closes the queue once a specific number // of items has been pushed. This allows an arbitrary consumer to read until // 'closed' without itself having to count items. template< typename T > -class nchannel { +class nqueue { public: - nchannel( std::shared_ptr< boost::fibers::unbounded_channel< T > > cp, + nqueue( std::shared_ptr< boost::fibers::unbounded_queue< T > > cp, std::size_t lm): - channel_( cp), + queue_( cp), limit_( lm) { - assert(channel_); + assert(queue_); if ( 0 == limit_) { - channel_->close(); + queue_->close(); } } - boost::fibers::channel_op_status push( T && va) { - boost::fibers::channel_op_status ok = - channel_->push( std::forward< T >( va) ); - if ( ok == boost::fibers::channel_op_status::success && + boost::fibers::queue_op_status push( T && va) { + boost::fibers::queue_op_status ok = + queue_->push( std::forward< T >( va) ); + if ( ok == boost::fibers::queue_op_status::success && --limit_ == 0) { - // after the 'limit_'th successful push, close the channel - channel_->close(); + // after the 'limit_'th successful push, close the queue + queue_->close(); } return ok; } private: - std::shared_ptr< boost::fibers::unbounded_channel< T > > channel_; + std::shared_ptr< boost::fibers::unbounded_queue< T > > queue_; std::size_t limit_; }; - Armed with nchannelArmed with nqueue<>, we can implement wait_all_values_source(). It starts just like wait_first_value(). The difference is that we wrap - the unbounded_channel<unbounded_queue<T> - with an nchannel<nqueue<T> to pass to the producer fibers. - Then, of course, instead of popping the first value, closing the channel + Then, of course, instead of popping the first value, closing the queue and returning it, we simply return the shared_ptr<unbounded_channel<unbounded_queue<T>>. -// Return a shared_ptr<unbounded_channel<T>> from which the caller can +// Return a shared_ptr<unbounded_queue<T>> from which the caller can // retrieve each new result as it arrives, until 'closed'. template< typename Fn, typename ... Fns > -std::shared_ptr< boost::fibers::unbounded_channel< typename std::result_of< Fn() >::type > > +std::shared_ptr< boost::fibers::unbounded_queue< typename std::result_of< Fn() >::type > > wait_all_values_source( Fn && function, Fns && ... functions) { std::size_t count( 1 + sizeof ... ( functions) ); typedef typename std::result_of< Fn() >::type return_t; - typedef boost::fibers::unbounded_channel< return_t > channel_t; - // make the channel - auto channelp( std::make_shared< channel_t >() ); - // and make an nchannel facade to close it after 'count' items - auto ncp( std::make_shared< nchannel< return_t > >( channelp, count) ); - // pass that nchannel facade to all the relevant fibers + typedef boost::fibers::unbounded_queue< return_t > queue_t; + // make the queue + auto queuep( std::make_shared< queue_t >() ); + // and make an nqueue facade to close it after 'count' items + auto ncp( std::make_shared< nqueue< return_t > >( queuep, count) ); + // pass that nqueue facade to all the relevant fibers wait_all_values_impl< return_t >( ncp, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); - // then return the channel for consumer - return channelp; + // then return the queue for consumer + return queuep; } @@ -12992,13 +12991,13 @@ before For example: -std::shared_ptr< boost::fibers::unbounded_channel< std::string > > channel = +std::shared_ptr< boost::fibers::unbounded_queue< std::string > > queue = wait_all_values_source( [](){ return sleeper("wavs_third", 150); }, [](){ return sleeper("wavs_second", 100); }, [](){ return sleeper("wavs_first", 50); }); std::string value; -while ( boost::fibers::channel_op_status::success == channel->pop(value) ) { +while ( boost::fibers::queue_op_status::success == queue->pop(value) ) { std::cout << "wait_all_values_source() => '" << value << "'" << std::endl; } @@ -13008,18 +13007,18 @@ before wait_all_values_impl() really is just like wait_first_value_impl() - except for the use of nchannelnqueue<T> rather than unbounded_channel> rather than unbounded_queue<T>: template< typename T, typename Fn > -void wait_all_values_impl( std::shared_ptr< nchannel< T > > channel, +void wait_all_values_impl( std::shared_ptr< nqueue< T > > queue, Fn && function) { - boost::fibers::fiber( [channel, function](){ - channel->push(function()); + boost::fibers::fiber( [queue, function](){ + queue->push(function()); }).detach(); } @@ -13055,14 +13054,14 @@ before vector_t results; results.reserve( count); - // get channel + // get queue std::shared_ptr< - boost::fibers::unbounded_channel< future_t > > channel( + boost::fibers::unbounded_queue< future_t > > queue( wait_all_until_error_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ) ); // fill results vector future_t future; - while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { + while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { results.push_back( future.get() ); } // return vector to caller @@ -13095,42 +13094,42 @@ before T > and call its get() method. It would, of course, be possible to return a façade over the consumer - end of the channel that would implicitly perform the getget() and return a simple T (or throw). The implementation is just as you would expect. Notice, however, that we can reuse wait_first_outcome_impl(), passing the nchannel(), passing the nqueue<T> rather than unbounded_channel> rather than unbounded_queue<T>. -// Return a shared_ptr<unbounded_channel<future<T>>> from which the caller can +// Return a shared_ptr<unbounded_queue<future<T>>> from which the caller can // get() each new result as it arrives, until 'closed'. template< typename Fn, typename ... Fns > std::shared_ptr< - boost::fibers::unbounded_channel< + boost::fibers::unbounded_queue< boost::fibers::future< typename std::result_of< Fn() >::type > > > wait_all_until_error_source( Fn && function, Fns && ... functions) { std::size_t count( 1 + sizeof ... ( functions) ); typedef typename std::result_of< Fn() >::type return_t; typedef boost::fibers::future< return_t > future_t; - typedef boost::fibers::unbounded_channel< future_t > channel_t; - // make the channel - auto channelp( std::make_shared< channel_t >() ); - // and make an nchannel facade to close it after 'count' items - auto ncp( std::make_shared< nchannel< future_t > >( channelp, count) ); - // pass that nchannel facade to all the relevant fibers + typedef boost::fibers::unbounded_queue< future_t > queue_t; + // make the queue + auto queuep( std::make_shared< queue_t >() ); + // and make an nqueue facade to close it after 'count' items + auto ncp( std::make_shared< nqueue< future_t > >( queuep, count) ); + // pass that nqueue facade to all the relevant fibers wait_first_outcome_impl< return_t >( ncp, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); - // then return the channel for consumer - return channelp; + // then return the queue for consumer + return queuep; } @@ -13139,13 +13138,13 @@ before typedef boost::fibers::future< std::string > future_t; -std::shared_ptr< boost::fibers::unbounded_channel< future_t > > channel = +std::shared_ptr< boost::fibers::unbounded_queue< future_t > > queue = wait_all_until_error_source( [](){ return sleeper("wauess_third", 150); }, [](){ return sleeper("wauess_second", 100); }, [](){ return sleeper("wauess_first", 50); }); future_t future; -while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { +while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { std::string value( future.get() ); std::cout << "wait_all_until_error_source(success) => '" << value << "'" << std::endl; @@ -13175,14 +13174,14 @@ before results.reserve( count); exception_list exceptions("wait_all_collect_errors() exceptions"); - // get channel + // get queue std::shared_ptr< - boost::fibers::unbounded_channel< future_t > > channel( + boost::fibers::unbounded_queue< future_t > > queue( wait_all_until_error_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ) ); // fill results and/or exceptions vectors future_t future; - while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { + while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { std::exception_ptr exp = future.get_exception_ptr(); if ( ! exp) { results.push_back( future.get() ); diff --git a/doc/html/fiber/stack/fixedsize.html b/doc/html/fiber/stack/fixedsize.html deleted file mode 100644 index 451bd761..00000000 --- a/doc/html/fiber/stack/fixedsize.html +++ /dev/null @@ -1,105 +0,0 @@ - - - -Class fixedsize_stack - - - - - - - - - - - - - - - -
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
-
-
-PrevUpHomeNext -
-
- -

- Boost.Fiber provides the class __fixedsize__ - which models the stack-allocator - concept. In contrast to __protected_fixedsize__ it does - not append a guard page at the end of each stack. The memory is simply managed - by std::malloc() - and std::free(). -

-
#include <boost/context/fixedsize_stack.hpp>
-
-struct fixedsize_stack {
-    fixedsize_stack(std::size_t size = traits_type::default_size());
-
-    stack_context allocate();
-
-    void deallocate( stack_context &);
-}
-
-
- - stack_context allocate() -
-
-

-
-
Preconditions:
-

- traits_type::minimum_size() - <= size - and traits_type::is_unbounded() - || ( - traits_type::maximum_size() - >= size). -

-
Effects:
-

- Allocates memory of at least size - bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture - (the stack grows downwards/upwards) the stored address is the highest/lowest - address of the stack. -

-
-
-
- - void deallocate( stack_context - & sctx) -
-
-

-
-
Preconditions:
-

- sctx.sp is valid, traits_type::minimum_size() <= sctx.size and traits_type::is_unbounded() || ( traits_type::maximum_size() >= sctx.size). -

-
Effects:
-

- Deallocates the stack space. -

-
-
-
- - - -
-
-
-PrevUpHomeNext -
- - diff --git a/doc/html/fiber/stack/pooled_fixedsize.html b/doc/html/fiber/stack/pooled_fixedsize.html deleted file mode 100644 index ef83e1d4..00000000 --- a/doc/html/fiber/stack/pooled_fixedsize.html +++ /dev/null @@ -1,132 +0,0 @@ - - - -Class pooled_fixedsize_stack - - - - - - - - - - - - - - - -
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
-
-
-PrevUpHomeNext -
-
- -

- Boost.Fiber provides the class pooled_fixedsize_stack which - models the stack-allocator - concept. In contrast to protected_fixedsize_stack it - does not append a guard page at the end of each stack. The memory is managed - internally by boost::pool<>. -

-
#include <boost/fiber/pooled_fixedsize_stack.hpp>
-
-struct pooled_fixedsize_stack {
-    pooled_fixedsize_stack(std::size_t stack_size = traits_type::default_size(), std::size_t next_size = 32, std::size_t max_size = 0);
-
-    stack_context allocate();
-
-    void deallocate( stack_context &);
-}
-
-
- - pooled_fixedsize_stack(std::size_t - stack_size, - std::size_t next_size, std::size_t max_size) -
-
-

-
-
Preconditions:
-

- traits_type::is_unbounded() - || ( - traits_type::maximum_size() - >= stack_size) and 0 - < next_size. -

-
Effects:
-

- Allocates memory of at least stack_size - bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture - (the stack grows downwards/upwards) the stored address is the highest/lowest - address of the stack. Argument next_size - determines the number of stacks to request from the system the first - time that *this - needs to allocate system memory. The third argument max_size - controls how much memory might be allocated for stacks — a value of zero - means no upper limit. -

-
-
-
- - stack_context allocate() -
-
-

-
-
Preconditions:
-

- traits_type::is_unbounded() - || ( - traits_type::maximum_size() - >= stack_size). -

-
Effects:
-

- Allocates memory of at least stack_size - bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture - (the stack grows downwards/upwards) the stored address is the highest/lowest - address of the stack. -

-
-
-
- - void deallocate( stack_context - & sctx) -
-
-

-
-
Preconditions:
-

- sctx.sp is valid, traits_type::is_unbounded() || ( traits_type::maximum_size() >= sctx.size). -

-
Effects:
-

- Deallocates the stack space. -

-
-
-
- - - -
-
-
-PrevUpHomeNext -
- - diff --git a/doc/html/fiber/stack/protected_fixedsize.html b/doc/html/fiber/stack/protected_fixedsize.html deleted file mode 100644 index f7963fe5..00000000 --- a/doc/html/fiber/stack/protected_fixedsize.html +++ /dev/null @@ -1,128 +0,0 @@ - - - -Class protected_fixedsize_stack - - - - - - - - - - - - - - - -
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
-
-
-PrevUpHomeNext -
-
- -

- Boost.Fiber provides the class protected_fixedsize_stack which - models the stack-allocator - concept. It appends a guard page at the end of each stack - to protect against exceeding the stack. If the guard page is accessed (read - or write operation) a segmentation fault/access violation is generated by - the operating system. -

-
- - - - - -
[Important]Important

- Using protected_fixedsize_stack is expensive. - Launching a new fiber with a stack of this type incurs the overhead of - setting the memory protection; once allocated, this stack is just as efficient - to use as fixedsize_stack. -

-
- - - - - -
[Note]Note

- The appended guard page - is not mapped to physical memory, only - virtual addresses are used. -

-
#include <boost/fiber/protected_fixedsize.hpp>
-
-struct protected_fixedsize {
-    protected_fixesize(std::size_t size = traits_type::default_size());
-
-    stack_context allocate();
-
-    void deallocate( stack_context &);
-}
-
-
- - stack_context allocate() -
-
-

-
-
Preconditions:
-

- traits_type::minimum_size() - <= size - and traits_type::is_unbounded() - || ( - size <= - traits_type::maximum_size() - ). -

-
Effects:
-

- Allocates memory of at least size - bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture - (the stack grows downwards/upwards) the stored address is the highest/lowest - address of the stack. -

-
-
-
- - void deallocate( stack_context - & sctx) -
-
-

-
-
Preconditions:
-

- sctx.sp is valid, traits_type::minimum_size() <= sctx.size and traits_type::is_unbounded() || ( sctx.size <= traits_type::maximum_size() ). -

-
Effects:
-

- Deallocates the stack space. -

-
-
-
- - - -
-
-
-PrevUpHomeNext -
- - diff --git a/doc/html/fiber/stack/protected_fixedsize/pooled_fixedsize.html b/doc/html/fiber/stack/protected_fixedsize/pooled_fixedsize.html deleted file mode 100644 index ff8f3832..00000000 --- a/doc/html/fiber/stack/protected_fixedsize/pooled_fixedsize.html +++ /dev/null @@ -1,9374 +0,0 @@ - - - -Class pooled_fixedsize_stack - - - - - - - - - - - - - - -
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
-
-
-PrevUpHome -
-
- - -

- Boost.Fiber provides the class pooled_fixedsize_stack which - models the stack-allocator - concept. In contrast to protected_fixedsize_stack it - does not append a guard page at the end of each stack. The memory is managed - internally by boost::pool<>. -

-
#include <boost/fiber/pooled_fixedsize_stack.hpp>
-
-struct pooled_fixedsize_stack {
-    pooled_fixedsize_stack(std::size_t stack_size = traits_type::default_size(), std::size_t next_size = 32, std::size_t max_size = 0);
-
-    stack_context allocate();
-
-    void deallocate( stack_context &);
-}
-
-
- - pooled_fixedsize_stack(std::size_t stack_size, std::size_t next_size, - std::size_t max_size) -
-
-

-
-
Preconditions:
-

- traits_type::is_unbounded() - || ( - traits_type::maximum_size() - >= stack_size) and 0 - < next_size. -

-
Effects:
-

- Allocates memory of at least stack_size - bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture - (the stack grows downwards/upwards) the stored address is the highest/lowest - address of the stack. Argument next_size - determines the number of stacks to request from the system the first - time that *this - needs to allocate system memory. The third argument max_size controls how much memory - might be allocated for stacks — a value of zero means no upper limit. -

-
-
-
- - stack_context allocate() -
-
-

-
-
Preconditions:
-

- traits_type::is_unbounded() - || ( - traits_type::maximum_size() - >= stack_size). -

-
Effects:
-

- Allocates memory of at least stack_size - bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture - (the stack grows downwards/upwards) the stored address is the highest/lowest - address of the stack. -

-
-
-
- - void deallocate( stack_context - & sctx) -
-
-

-
-
Preconditions:
-

- sctx.sp is valid, traits_type::is_unbounded() || ( traits_type::maximum_size() >= - sctx.size). -

-
Effects:
-

- Deallocates the stack space. -

-
-
-
- -

- Boost.Fiber provides the class __fixedsize__ - which models the stack-allocator - concept. In contrast to __protected_fixedsize__ it - does not append a guard page at the end of each stack. The memory is - simply managed by std::malloc() and std::free(). -

-
#include <boost/context/fixedsize_stack.hpp>
-
-struct fixedsize_stack {
-    fixedsize_stack(std::size_t size = traits_type::default_size());
-
-    stack_context allocate();
-
-    void deallocate( stack_context &);
-}
-
-
- - stack_context allocate() -
-
-

-
-
Preconditions:
-

- traits_type::minimum_size() - <= size - and traits_type::is_unbounded() || - ( traits_type::maximum_size() >= - size). -

-
Effects:
-

- Allocates memory of at least size - bytes and stores a pointer to the stack and its actual size in - sctx. Depending - on the architecture (the stack grows downwards/upwards) the stored - address is the highest/lowest address of the stack. -

-
-
-
- - void deallocate( stack_context - & sctx) -
-
-

-
-
Preconditions:
-

- sctx.sp is valid, traits_type::minimum_size() <= - sctx.size and traits_type::is_unbounded() || - ( traits_type::maximum_size() >= - sctx.size). -

-
Effects:
-

- Deallocates the stack space. -

-
-
-
-
- -

- Boost.Fiber supports usage of a segmented_stack, - i.e. the stack grows on demand. The fiber is created with a minimal stack - size which will be increased as required. Class segmented_stack models - the stack-allocator - concept. In contrast to protected_fixedsize_stack and - fixedsize_stack it creates a stack which grows on - demand. -

-
- - - - - -
[Note]Note

- Segmented stacks are currently only supported by gcc - from version 4.7 and clang - from version 3.4 onwards. In order - to use a segmented_stack Boost.Fiber - must be built with property segmented-stacks, - e.g. toolset=gcc segmented-stacks=on - at b2/bjam command line. -

-
#include <boost/fiber/segmented_stack.hpp>
-
-struct segmented_stack {
-    segmented_stack(std::size_t stack_size = traits_type::default_size());
-
-    stack_context allocate();
-
-    void deallocate( stack_context &);
-}
-
-
- - stack_context allocate() -
-
-

-
-
Preconditions:
-

- traits_type::minimum_size() - <= size - and traits_type::is_unbounded() || - ( traits_type::maximum_size() >= - size). -

-
Effects:
-

- Allocates memory of at least size - bytes and stores a pointer to the stack and its actual size in - sctx. Depending - on the architecture (the stack grows downwards/upwards) the stored - address is the highest/lowest address of the stack. -

-
-
-
- - void deallocate( stack_context - & sctx) -
-
-

-
-
Preconditions:
-

- sctx.sp is valid, traits_type::minimum_size() <= - sctx.size and traits_type::is_unbounded() || - ( traits_type::maximum_size() >= - sctx.size). -

-
Effects:
-

- Deallocates the stack space. -

-
-
-
- - - - - -
[Note]Note

- If the library is compiled for segmented stacks, segmented_stack is - the only available stack allocator. -

-
-
- - -

- In general, Boost.Fiber synchronization - objects can neither be moved nor copied. A synchronization object acts - as a mutually-agreed rendezvous point between different fibers. If such - an object were copied somewhere else, the new copy would have no consumers. - If such an object were moved somewhere else, leaving - the original instance in an unspecified state, existing consumers would - behave strangely. -

-

- The fiber synchronization objects provided by this library will, by default, - safely synchronize fibers running on different threads. However, this - level of synchronization can be removed (for performance) by building - the library with BOOST_FIBERS_NO_ATOMICS - defined. When the library is built with that macro, you must ensure that - all the fibers referencing a particular synchronization object are running - in the same thread. -

-
- -

-

-
- - - Class mutex -
-

-

-
#include <boost/fiber/mutex.hpp>
-
-class mutex {
-public:
-    mutex();
-    ~mutex();
-
-    mutex( mutex const& other) = delete;
-    mutex & operator=( mutex const& other) = delete;
-
-    void lock();
-    bool try_lock();
-    void unlock();
-};
-
-

- mutex provides an exclusive-ownership mutex. At most one - fiber can own the lock on a given instance of mutex at any - time. Multiple concurrent calls to lock(), try_lock() and unlock() shall be permitted. -

-

- Any fiber blocked in lock() is suspended until the owning fiber - releases the lock by calling unlock(). -

-

-

-
- - - Member function lock() -
-

-

-
void lock();
-
-
-

-
-
Precondition:
-

- The calling fiber doesn't own the mutex. -

-
Effects:
-

- The current fiber blocks until ownership can be obtained. -

-
Throws:
-

- lock_error -

-
Error Conditions:
-

- resource_deadlock_would_occur: - if boost::this_fiber::get_id() - already owns the mutex. -

-
-
-

-

-
- - - Member function - try_lock() -
-

-

-
bool try_lock();
-
-
-

-
-
Precondition:
-

- The calling fiber doesn't own the mutex. -

-
Effects:
-

- Attempt to obtain ownership for the current fiber without blocking. -

-
Returns:
-

- true if ownership - was obtained for the current fiber, false - otherwise. -

-
Throws:
-

- lock_error -

-
Error Conditions:
-

- resource_deadlock_would_occur: - if boost::this_fiber::get_id() - already owns the mutex. -

-
-
-

-

-
- - - Member function unlock() -
-

-

-
void unlock();
-
-
-

-
-
Precondition:
-

- The current fiber owns *this. -

-
Effects:
-

- Releases a lock on *this by the current fiber. -

-
Throws:
-

- lock_error -

-
Error Conditions:
-

- operation_not_permitted: if - boost::this_fiber::get_id() - does not own the mutex. -

-
-
-

-

-
- - - Class - timed_mutex -
-

-

-
#include <boost/fiber/timed_mutex.hpp>
-
-class timed_mutex {
-public:
-    timed_mutex();
-    ~timed_mutex();
-
-    timed_mutex( timed_mutex const& other) = delete;
-    timed_mutex & operator=( timed_mutex const& other) = delete;
-
-    void lock();
-    bool try_lock();
-    void unlock();
-
-    template< typename Clock, typename Duration >
-    bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time);
-    template< typename Rep, typename Period >
-    bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration);
-};
-
-

- timed_mutex provides an exclusive-ownership mutex. - At most one fiber can own the lock on a given instance of timed_mutex at - any time. Multiple concurrent calls to lock(), try_lock(), try_lock_until(), try_lock_for() and unlock() shall be permitted. -

-

-

-
- - - Member function - lock() -
-

-

-
void lock();
-
-
-

-
-
Precondition:
-

- The calling fiber doesn't own the mutex. -

-
Effects:
-

- The current fiber blocks until ownership can be obtained. -

-
Throws:
-

- lock_error -

-
Error Conditions:
-

- resource_deadlock_would_occur: - if boost::this_fiber::get_id() - already owns the mutex. -

-
-
-

-

-
- - - Member - function try_lock() -
-

-

-
bool try_lock();
-
-
-

-
-
Precondition:
-

- The calling fiber doesn't own the mutex. -

-
Effects:
-

- Attempt to obtain ownership for the current fiber without blocking. -

-
Returns:
-

- true if ownership - was obtained for the current fiber, false - otherwise. -

-
Throws:
-

- lock_error -

-
Error Conditions:
-

- resource_deadlock_would_occur: - if boost::this_fiber::get_id() - already owns the mutex. -

-
-
-

-

-
- - - Member - function unlock() -
-

-

-
void unlock();
-
-
-

-
-
Precondition:
-

- The current fiber owns *this. -

-
Effects:
-

- Releases a lock on *this by the current fiber. -

-
Throws:
-

- lock_error -

-
Error Conditions:
-

- operation_not_permitted: if - boost::this_fiber::get_id() - does not own the mutex. -

-
-
-

-

-
- - - Templated - member function try_lock_until() -
-

-

-
template< typename Clock, typename Duration >
-bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time);
-
-
-

-
-
Precondition:
-

- The calling fiber doesn't own the mutex. -

-
Effects:
-

- Attempt to obtain ownership for the current fiber. Blocks until - ownership can be obtained, or the specified time is reached. - If the specified time has already passed, behaves as timed_mutex::try_lock(). -

-
Returns:
-

- true if ownership - was obtained for the current fiber, false - otherwise. -

-
Throws:
-

- lock_error, timeout-related - exceptions. -

-
Error Conditions:
-

- resource_deadlock_would_occur: - if boost::this_fiber::get_id() - already owns the mutex. -

-
-
-

-

-
- - - Templated - member function try_lock_for() -
-

-

-
template< typename Rep, typename Period >
-bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration);
-
-
-

-
-
Precondition:
-

- The calling fiber doesn't own the mutex. -

-
Effects:
-

- Attempt to obtain ownership for the current fiber. Blocks until - ownership can be obtained, or the specified time is reached. - If the specified time has already passed, behaves as timed_mutex::try_lock(). -

-
Returns:
-

- true if ownership - was obtained for the current fiber, false - otherwise. -

-
Throws:
-

- lock_error, timeout-related - exceptions. -

-
Error Conditions:
-

- resource_deadlock_would_occur: - if boost::this_fiber::get_id() - already owns the mutex. -

-
-
-

-

-
- - - Class - recursive_mutex -
-

-

-
#include <boost/fiber/recursive_mutex.hpp>
-
-class recursive_mutex {
-public:
-    recursive_mutex();
-    ~recursive_mutex();
-
-    recursive_mutex( recursive_mutex const& other) = delete;
-    recursive_mutex & operator=( recursive_mutex const& other) = delete;
-
-    void lock();
-    bool try_lock() noexcept;
-    void unlock();
-};
-
-

- recursive_mutex provides an exclusive-ownership - recursive mutex. At most one fiber can own the lock on a given instance - of recursive_mutex at any time. Multiple concurrent - calls to lock(), - try_lock() - and unlock() - shall be permitted. A fiber that already has exclusive ownership of - a given recursive_mutex instance can call lock() - or try_lock() - to acquire an additional level of ownership of the mutex. unlock() - must be called once for each level of ownership acquired by a single - fiber before ownership can be acquired by another fiber. -

-

-

-
- - - Member - function lock() -
-

-

-
void lock();
-
-
-

-
-
Effects:
-

- The current fiber blocks until ownership can be obtained. -

-
Throws:
-

- fiber_interrupted -

-
-
-

-

-
- - - Member - function try_lock() -
-

-

-
bool try_lock() noexcept;
-
-
-

-
-
Effects:
-

- Attempt to obtain ownership for the current fiber without blocking. -

-
Returns:
-

- true if ownership - was obtained for the current fiber, false - otherwise. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function unlock() -
-

-

-
void unlock();
-
-
-

-
-
Effects:
-

- Releases a lock on *this by the current fiber. -

-
Throws:
-

- lock_error -

-
Error Conditions:
-

- operation_not_permitted: if - boost::this_fiber::get_id() - does not own the mutex. -

-
-
-

-

-
- - - Class - recursive_timed_mutex -
-

-

-
#include <boost/fiber/recursive_timed_mutex.hpp>
-
-class recursive_timed_mutex {
-public:
-    recursive_timed_mutex();
-    ~recursive_timed_mutex();
-
-    recursive_timed_mutex( recursive_timed_mutex const& other) = delete;
-    recursive_timed_mutex & operator=( recursive_timed_mutex const& other) = delete;
-
-    void lock();
-    bool try_lock() noexcept;
-    void unlock();
-
-    template< typename Clock, typename Duration >
-    bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time);
-    template< typename Rep, typename Period >
-    bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration);
-};
-
-

- recursive_timed_mutex provides an exclusive-ownership - recursive mutex. At most one fiber can own the lock on a given instance - of recursive_timed_mutex at any time. Multiple - concurrent calls to lock(), try_lock(), try_lock_for(), try_lock_until() and unlock() shall be permitted. A fiber that - already has exclusive ownership of a given recursive_timed_mutex instance - can call lock(), - try_lock(), - try_lock_for() - or try_lock_until() - to acquire an additional level of ownership of the mutex. unlock() - must be called once for each level of ownership acquired by a single - fiber before ownership can be acquired by another fiber. -

-

-

-
- - - Member - function lock() -
-

-

-
void lock();
-
-
-

-
-
Effects:
-

- The current fiber blocks until ownership can be obtained. -

-
Throws:
-

- fiber_interrupted -

-
-
-

-

-
- - - Member - function try_lock() -
-

-

-
bool try_lock() noexcept;
-
-
-

-
-
Effects:
-

- Attempt to obtain ownership for the current fiber without blocking. -

-
Returns:
-

- true if ownership - was obtained for the current fiber, false - otherwise. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function unlock() -
-

-

-
void unlock();
-
-
-

-
-
Effects:
-

- Releases a lock on *this by the current fiber. -

-
Throws:
-

- lock_error -

-
Error Conditions:
-

- operation_not_permitted: if - boost::this_fiber::get_id() - does not own the mutex. -

-
-
-

-

-
- - - Templated - member function try_lock_until() -
-

-

-
template< typename Clock, typename Duration >
-bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time);
-
-
-

-
-
Effects:
-

- Attempt to obtain ownership for the current fiber. Blocks until - ownership can be obtained, or the specified time is reached. - If the specified time has already passed, behaves as recursive_timed_mutex::try_lock(). -

-
Returns:
-

- true if ownership - was obtained for the current fiber, false - otherwise. -

-
Throws:
-

- Timeout-related exceptions. -

-
-
-

-

-
- - - Templated - member function try_lock_for() -
-

-

-
template< typename Rep, typename Period >
-bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration);
-
-
-

-
-
Effects:
-

- Attempt to obtain ownership for the current fiber. Blocks until - ownership can be obtained, or the specified time is reached. - If the specified time has already passed, behaves as recursive_timed_mutex::try_lock(). -

-
Returns:
-

- true if ownership - was obtained for the current fiber, false - otherwise. -

-
Throws:
-

- Timeout-related exceptions. -

-
-
-
-
- - - Synopsis -
enum class cv_status; {
-    no_timeout,
-    timeout
-};
-
-class condition_variable;
-class condition_variable_any;
-
-

- The class condition_variable provides a mechanism - for a fiber to wait for notification from another fiber. When the fiber - awakens from the wait, then it checks to see if the appropriate condition - is now true, and continues if so. If the condition is not true, then - the fiber calls wait - again to resume waiting. In the simplest case, this condition is just - a boolean variable: -

-
boost::fibers::condition_variable cond;
-boost::fibers::mutex mtx;
-bool data_ready = false;
-
-void process_data();
-
-void wait_for_data_to_process() {
-    {
-        std::unique_lock< boost::fibers::mutex > lk( mtx);
-        while ( ! data_ready) {
-            cond.wait( lk);
-        }
-    }   // release lk
-    process_data();
-}
-
-

- Notice that the lk - is passed to condition_variable::wait(): wait() - will atomically add the fiber to the set of fibers waiting on the condition - variable, and unlock the mutex. When the fiber is awakened, - the mutex will be locked - again before the call to wait() returns. This allows other fibers - to acquire the mutex - in order to update the shared data, and ensures that the data associated - with the condition is correctly synchronized. -

-

- wait_for_data_to_process() could equivalently be written: -

-
void wait_for_data_to_process() {
-    {
-        std::unique_lock< boost::fibers::mutex > lk( mtx);
-        // make condition_variable::wait() perform the loop
-        cond.wait( lk, [](){ return data_ready; });
-    }   // release lk
-    process_data();
-}
-
-

- In the meantime, another fiber sets data_ready - to true, and then calls - either condition_variable::notify_one() or - condition_variable::notify_all() on the - condition_variable cond - to wake one waiting fiber or all the waiting fibers respectively. -

-
void retrieve_data();
-void prepare_data();
-
-void prepare_data_for_processing() {
-    retrieve_data();
-    prepare_data();
-    {
-        std::unique_lock< boost::fibers::mutex > lk( mtx);
-        data_ready = true;
-    }
-    cond.notify_one();
-}
-
-

- Note that the same mutex is locked before the shared data - is updated, but that the mutex - does not have to be locked across the call to condition_variable::notify_one(). -

-

- Locking is important because the synchronization objects provided by - Boost.Fiber can be used to synchronize - fibers running on different threads. -

-

- Boost.Fiber provides both condition_variable and - condition_variable_any. boost::fibers::condition_variable - can only wait on std::unique_lock< boost::fibers:: mutex > - while boost::fibers::condition_variable_any can wait on - user-defined lock types. -

- - No - Spurious Wakeups -

- Neither condition_variable nor condition_variable_any are - subject to spurious wakeup: condition_variable::wait() can - only wake up when condition_variable::notify_one() or - condition_variable::notify_all() is called. - Even so, it is prudent to use one of the wait( lock, predicate - ) overloads. -

-

- Consider a set of consumer fibers processing items from a std::queue. The queue is continually - populated by a set of producer fibers. -

-

- The consumer fibers might reasonably wait on a condition_variable - as long as the queue remains empty(). -

-

- Because producer fibers might push() - items to the queue in bursts, they call condition_variable::notify_all() rather - than condition_variable::notify_one(). -

-

- But a given consumer fiber might well wake up from condition_variable::wait() and - find the queue empty(), because other consumer fibers might - already have processed all pending items. -

-

- (See also spurious wakeup.) -

- - Enumeration - cv_status -

- A timed wait operation might return because of timeout or not. -

-
enum class cv_status {
-    no_timeout,
-    timeout
-};
-
- - no_timeout -
-

-
-
Effects:
-

- The condition variable was awakened with notify_one - or notify_all. -

-
-
- - timeout -
-

-
-
Effects:
-

- The condition variable was awakened by timeout. -

-
-
-

-

-
- - - Class - condition_variable_any -
-

-

-
#include <boost/fiber/condition_variable.hpp>
-
-class condition_variable_any {
-public:
-    condition_variable_any();
-    ~condition_variable_any();
-
-    condition_variable_any( condition_variable_any const&) = delete;
-    condition_variable_any & operator=( condition_variable_any const&) = delete;
-
-    void notify_one() noexcept;
-    void notify_all() noexcept;
-
-    template< typename LockType >
-    void wait( LockType &);
-
-    template< typename LockType, typename Pred >
-    void wait( LockType &, Pred);
-
-    template< typename LockType, typename Clock, typename Duration >
-    cv_status wait_until( LockType &,
-                          std::chrono::time_point< Clock, Duration > const&);
-
-    template< typename LockType, typename Clock, typename Duration, typename Pred >
-    bool wait_until( LockType &,
-                     std::chrono::time_point< Clock, Duration > const&,
-                     Pred);
-
-    template< typename LockType, typename Rep, typename Period >
-    cv_status wait_for( LockType &,
-                        std::chrono::duration< Rep, Period > const&);
-
-    template< typename LockType, typename Rep, typename Period, typename Pred >
-    bool wait_for( LockType &,
-                   std::chrono::duration< Rep, Period > const&,
-                   Pred);
-};
-
- - Constructor -
condition_variable_any()
-
-
-

-
-
Effects:
-

- Creates the object. -

-
Throws:
-

- Nothing. -

-
-
- - Destructor -
~condition_variable_any()
-
-
-

-
-
Precondition:
-

- All fibers waiting on *this have been notified by a call - to notify_one - or notify_all - (though the respective calls to wait, - wait_for or - wait_until need - not have returned). -

-
Effects:
-

- Destroys the object. -

-
-
-

-

-
- - - Member - function notify_one() -
-

-

-
void notify_one() noexcept;
-
-
-

-
-
Effects:
-

- If any fibers are currently blocked - waiting on *this - in a call to wait, - wait_for or - wait_until, unblocks - one of those fibers. -

-
Throws:
-

- Nothing. -

-
Note:
-

- It is arbitrary which waiting fiber is resumed. -

-
-
-

-

-
- - - Member - function notify_all() -
-

-

-
void notify_all() noexcept;
-
-
-

-
-
Effects:
-

- If any fibers are currently blocked - waiting on *this - in a call to wait, - wait_for or - wait_until, unblocks - all of those fibers. -

-
Throws:
-

- Nothing. -

-
Note:
-

- This is why a waiting fiber must also check - for the desired program state using a mechanism external to the - condition_variable_any, and retry the wait until that state is - reached. A fiber waiting on a condition_variable_any might well - wake up a number of times before the desired state is reached. -

-
-
-

-

-
- - - Templated - member function wait() -
-

-

-
template< typename LockType >
-    void wait( LockType & lk);
-
-template< typename LockType, typename Pred >
-void wait( LockType & lk, Pred pred);
-
-
-

-
-
Precondition:
-

- lk is locked - by the current fiber, and either no other fiber is currently - waiting on *this, - or the execution of the mutex() - member function on the lk - objects supplied in the calls to wait - in all the fibers currently waiting on *this would return the same value - as lk->mutex() - for this call to wait. -

-
Effects:
-
-

- Atomically call lk.unlock() and blocks the current fiber. - The fiber will unblock when notified by a call to this->notify_one() - or this->notify_all(). - When the fiber is unblocked (for whatever reason), the lock is - reacquired by invoking lk.lock() before the call to wait returns. The lock is also - reacquired by invoking lk.lock() if the function exits with - an exception. The member function accepting pred - is shorthand for: -

-
while ( ! pred() ) {
-    wait( lk);
-}
-
-

-

-
-
Postcondition:
-

- lk is locked - by the current fiber. -

-
Throws:
-

- fiber_error if - an error occurs, fiber_interrupted - if the wait was interrupted by a call to fiber::interrupt() on - the fiber object associated with the current fiber - of execution. -

-
Note:
-

- The Precondition is a bit dense. It merely states that all the - fibers concurrently calling wait - on *this - must wait on lk - objects governing the same mutex. - Three distinct objects are involved in any condition_variable_any::wait() call: - the condition_variable_any itself, the mutex - coordinating access between fibers and a local lock object (e.g. - std::unique_lock). In general, - you can partition the lifespan of a given condition_variable_any instance - into periods with one or more fibers waiting on it, separated - by periods when no fibers are waiting on it. When more than one - fiber is waiting on that condition_variable_any, all must pass - lock objects referencing the same mutex instance. -

-
-
-

-

-
- - - Templated - member function wait_until() -
-

-

-
template< typename LockType, typename Clock, typename Duration >
-cv_status wait_until( LockType & lk,
-                      std::chrono::time_point< Clock, Duration > const& abs_time);
-
-template< typename LockType, typename Clock, typename Duration, typename Pred >
-bool wait_until( LockType & lk,
-                 std::chrono::time_point< Clock, Duration > const& abs_time,
-                 Pred pred);
-
-
-

-
-
Precondition:
-

- lk is locked - by the current fiber, and either no other fiber is currently - waiting on *this, - or the execution of the mutex() member function on the lk objects supplied in the - calls to wait, - wait_for or - wait_until in - all the fibers currently waiting on *this would return the same value - as lk.mutex() - for this call to wait_until. -

-
Effects:
-
-

- Atomically call lk.unlock() and blocks the current fiber. - The fiber will unblock when notified by a call to this->notify_one() - or this->notify_all(), - when the system time would be equal to or later than the specified - abs_time. When - the fiber is unblocked (for whatever reason), the lock is reacquired - by invoking lk.lock() before the call to wait_until returns. The lock - is also reacquired by invoking lk.lock() if the function exits with - an exception. The member function accepting pred - is shorthand for: -

-
while ( ! pred() ) {
-    if ( cv_status::timeout == wait_until( lk, abs_time) )
-        return pred();
-}
-return true;
-
-

- That is, even if wait_until() times out, it can still return - true if pred() - returns true at - that time. -

-
-
Postcondition:
-

- lk is locked - by the current fiber. -

-
Throws:
-

- fiber_error if - an error occurs, fiber_interrupted - if the wait was interrupted by a call to fiber::interrupt() on - the fiber object associated with the current fiber - of execution or timeout-related exceptions. -

-
Returns:
-

- The overload without pred - returns cv_status::no_timeout - if awakened by notify_one() or notify_all(), or cv_status::timeout - if awakened because the system time is past abs_time. -

-
Returns:
-

- The overload accepting pred - returns false if - the call is returning because the time specified by abs_time was reached and the - predicate returns false, - true otherwise. -

-
Note:
-

- See Note for condition_variable_any::wait(). -

-
-
-

-

-
- - - Templated - member function wait_for() -
-

-

-
template< typename LockType, typename Rep, typename Period >
-cv_status wait_for( LockType & lk,
-                    std::chrono::duration< Rep, Period > const& rel_time);
-
-template< typename LockType, typename Rep, typename Period, typename Pred >
-bool wait_for( LockType & lk,
-               std::chrono::duration< Rep, Period > const& rel_time,
-               Pred pred);
-
-
-

-
-
Precondition:
-

- lk is locked - by the current fiber, and either no other fiber is currently - waiting on *this, - or the execution of the mutex() member function on the lk objects supplied in the - calls to wait, - wait_for or - wait_until in - all the fibers currently waiting on *this would return the same value - as lk.mutex() - for this call to wait_for. -

-
Effects:
-
-

- Atomically call lk.unlock() and blocks the current fiber. - The fiber will unblock when notified by a call to this->notify_one() - or this->notify_all(), - when a time interval equal to or greater than the specified - rel_time has - elapsed. When the fiber is unblocked (for whatever reason), the - lock is reacquired by invoking lk.lock() before the call to wait returns. The lock is also - reacquired by invoking lk.lock() if the function exits with - an exception. The wait_for() member function accepting - pred is shorthand - for: -

-
while ( ! pred() ) {
-    if ( cv_status::timeout == wait_for( lk, rel_time) ) {
-        return pred();
-    }
-}
-return true;
-
-

- (except of course that rel_time - is adjusted for each iteration). The point is that, even if - wait_for() - times out, it can still return true - if pred() - returns true at - that time. -

-
-
Postcondition:
-

- lk is locked - by the current fiber. -

-
Throws:
-

- fiber_error if - an error occurs, fiber_interrupted - if the wait was interrupted by a call to fiber::interrupt() on - the fiber object associated with the current fiber - of execution or timeout-related exceptions. -

-
Returns:
-

- The overload without pred - returns cv_status::no_timeout - if awakened by notify_one() or notify_all(), or cv_status::timeout - if awakened because at least rel_time - has elapsed. -

-
Returns:
-

- The overload accepting pred - returns false if - the call is returning because at least rel_time - has elapsed and the predicate returns false, - true otherwise. -

-
Note:
-

- See Note for condition_variable_any::wait(). -

-
-
-

-

-
- - - Class - condition_variable -
-

-

-
#include <boost/fiber/condition_variable.hpp>
-
-class condition_variable {
-public:
-    condition_variable();
-    ~condition_variable();
-
-    condition_variable( condition_variable const&) = delete;
-    condition_variable & operator=( condition_variable const&) = delete;
-
-    void notify_one() noexcept;
-    void notify_all() noexcept;
-
-    void wait( std::unique_lock< mutex > &);
-
-    template< typename Pred >
-    void wait( std::unique_lock< mutex > &, Pred);
-
-    template< typename Clock, typename Duration >
-    cv_status wait_until( std::unique_lock< mutex > &,
-                          std::chrono::time_point< Clock, Duration > const&);
-
-    template< typename Clock, typename Duration, typename Pred >
-    bool wait_until( std::unique_lock< mutex > &,
-                     std::chrono::time_point< Clock, Duration > const&,
-                     Pred);
-
-    template< typename Rep, typename Period >
-    cv_status wait_for( std::unique_lock< mutex > &,
-                        std::chrono::duration< Rep, Period > const&);
-
-    template< typename Rep, typename Period, typename Pred >
-    bool wait_for( std::unique_lock< mutex > &,
-                   std::chrono::duration< Rep, Period > const&,
-                   Pred);
-};
-
- - Constructor -
condition_variable()
-
-
-

-
-
Effects:
-

- Creates the object. -

-
Throws:
-

- Nothing. -

-
-
- - Destructor -
~condition_variable()
-
-
-

-
-
Precondition:
-

- All fibers waiting on *this have been notified by a call - to notify_one - or notify_all - (though the respective calls to wait, - wait_for or - wait_until need - not have returned). -

-
Effects:
-

- Destroys the object. -

-
-
-

-

-
- - - Member - function notify_one() -
-

-

-
void notify_one() noexcept;
-
-
-

-
-
Effects:
-

- If any fibers are currently blocked - waiting on *this - in a call to wait, - wait_for or - wait_until, unblocks - one of those fibers. -

-
Throws:
-

- Nothing. -

-
Note:
-

- It is arbitrary which waiting fiber is resumed. -

-
-
-

-

-
- - - Member - function notify_all() -
-

-

-
void notify_all() noexcept;
-
-
-

-
-
Effects:
-

- If any fibers are currently blocked - waiting on *this - in a call to wait, - wait_for or - wait_until, unblocks - all of those fibers. -

-
Throws:
-

- Nothing. -

-
Note:
-

- This is why a waiting fiber must also check - for the desired program state using a mechanism external to the - condition_variable, and retry the wait until that state is reached. - A fiber waiting on a condition_variable might well wake up a number - of times before the desired state is reached. -

-
-
-

-

-
- - - Templated - member function wait() -
-

-

-
void wait( std::unique_lock< mutex > & lk);
-
-template< typename Pred >
-void wait( std::unique_lock< mutex > & lk, Pred pred);
-
-
-

-
-
Precondition:
-

- lk is locked - by the current fiber, and either no other fiber is currently - waiting on *this, - or the execution of the mutex() - member function on the lk - objects supplied in the calls to wait - in all the fibers currently waiting on *this would return the same value - as lk->mutex() - for this call to wait. -

-
Effects:
-
-

- Atomically call lk.unlock() and blocks the current fiber. - The fiber will unblock when notified by a call to this->notify_one() - or this->notify_all(). - When the fiber is unblocked (for whatever reason), the lock is - reacquired by invoking lk.lock() before the call to wait returns. The lock is also - reacquired by invoking lk.lock() if the function exits with - an exception. The member function accepting pred - is shorthand for: -

-
while ( ! pred() ) {
-    wait( lk);
-}
-
-

-

-
-
Postcondition:
-

- lk is locked - by the current fiber. -

-
Throws:
-

- fiber_error if - an error occurs, fiber_interrupted - if the wait was interrupted by a call to fiber::interrupt() on - the fiber object associated with the current fiber - of execution. -

-
Note:
-

- The Precondition is a bit dense. It merely states that all the - fibers concurrently calling wait - on *this - must wait on lk - objects governing the same mutex. - Three distinct objects are involved in any condition_variable::wait() call: - the condition_variable itself, the mutex - coordinating access between fibers and a local lock object (e.g. - std::unique_lock). In general, - you can partition the lifespan of a given condition_variable instance - into periods with one or more fibers waiting on it, separated - by periods when no fibers are waiting on it. When more than one - fiber is waiting on that condition_variable, all must pass lock - objects referencing the same mutex instance. -

-
-
-

-

-
- - - Templated - member function wait_until() -
-

-

-
template< typename Clock, typename Duration >
-cv_status wait_until( std::unique_lock< mutex > & lk,
-                      std::chrono::time_point< Clock, Duration > const& abs_time);
-
-template< typename Clock, typename Duration, typename Pred >
-bool wait_until( std::unique_lock< mutex > & lk,
-                 std::chrono::time_point< Clock, Duration > const& abs_time,
-                 Pred pred);
-
-
-

-
-
Precondition:
-

- lk is locked - by the current fiber, and either no other fiber is currently - waiting on *this, - or the execution of the mutex() member function on the lk objects supplied in the - calls to wait, - wait_for or - wait_until in - all the fibers currently waiting on *this would return the same value - as lk.mutex() - for this call to wait_until. -

-
Effects:
-
-

- Atomically call lk.unlock() and blocks the current fiber. - The fiber will unblock when notified by a call to this->notify_one() - or this->notify_all(), - when the system time would be equal to or later than the specified - abs_time. When - the fiber is unblocked (for whatever reason), the lock is reacquired - by invoking lk.lock() before the call to wait_until returns. The lock - is also reacquired by invoking lk.lock() if the function exits with - an exception. The member function accepting pred - is shorthand for: -

-
while ( ! pred() ) {
-    if ( cv_status::timeout == wait_until( lk, abs_time) )
-        return pred();
-}
-return true;
-
-

- That is, even if wait_until() times out, it can still return - true if pred() - returns true at - that time. -

-
-
Postcondition:
-

- lk is locked - by the current fiber. -

-
Throws:
-

- fiber_error if - an error occurs, fiber_interrupted - if the wait was interrupted by a call to fiber::interrupt() on - the fiber object associated with the current fiber - of execution or timeout-related exceptions. -

-
Returns:
-

- The overload without pred - returns cv_status::no_timeout - if awakened by notify_one() or notify_all(), or cv_status::timeout - if awakened because the system time is past abs_time. -

-
Returns:
-

- The overload accepting pred - returns false if - the call is returning because the time specified by abs_time was reached and the - predicate returns false, - true otherwise. -

-
Note:
-

- See Note for condition_variable::wait(). -

-
-
-

-

-
- - - Templated - member function wait_for() -
-

-

-
template< typename Rep, typename Period >
-cv_status wait_for( std::unique_lock< mutex > & lk,
-                    std::chrono::duration< Rep, Period > const& rel_time);
-
-template< typename Rep, typename Period, typename Pred >
-bool wait_for( std::unique_lock< mutex > & lk,
-               std::chrono::duration< Rep, Period > const& rel_time,
-               Pred pred);
-
-
-

-
-
Precondition:
-

- lk is locked - by the current fiber, and either no other fiber is currently - waiting on *this, - or the execution of the mutex() member function on the lk objects supplied in the - calls to wait, - wait_for or - wait_until in - all the fibers currently waiting on *this would return the same value - as lk.mutex() - for this call to wait_for. -

-
Effects:
-
-

- Atomically call lk.unlock() and blocks the current fiber. - The fiber will unblock when notified by a call to this->notify_one() - or this->notify_all(), - when a time interval equal to or greater than the specified - rel_time has - elapsed. When the fiber is unblocked (for whatever reason), the - lock is reacquired by invoking lk.lock() before the call to wait returns. The lock is also - reacquired by invoking lk.lock() if the function exits with - an exception. The wait_for() member function accepting - pred is shorthand - for: -

-
while ( ! pred() ) {
-    if ( cv_status::timeout == wait_for( lk, rel_time) ) {
-        return pred();
-    }
-}
-return true;
-
-

- (except of course that rel_time - is adjusted for each iteration). The point is that, even if - wait_for() - times out, it can still return true - if pred() - returns true at - that time. -

-
-
Postcondition:
-

- lk is locked - by the current fiber. -

-
Throws:
-

- fiber_error if - an error occurs, fiber_interrupted - if the wait was interrupted by a call to fiber::interrupt() on - the fiber object associated with the current fiber - of execution or timeout-related exceptions. -

-
Returns:
-

- The overload without pred - returns cv_status::no_timeout - if awakened by notify_one() or notify_all(), or cv_status::timeout - if awakened because at least rel_time - has elapsed. -

-
Returns:
-

- The overload accepting pred - returns false if - the call is returning because at least rel_time - has elapsed and the predicate returns false, - true otherwise. -

-
Note:
-

- See Note for condition_variable::wait(). -

-
-
-
-
- -

- A barrier is a concept also known as a rendezvous, - it is a synchronization point between multiple contexts of execution - (fibers). The barrier is configured for a particular number of fibers - (n), and as fibers - reach the barrier they must wait until all n - fibers have arrived. Once the n-th - fiber has reached the barrier, all the waiting fibers can proceed, - and the barrier is reset. -

-

- The fact that the barrier automatically resets is significant. Consider - a case in which you launch some number of fibers and want to wait only - until the first of them has completed. You might be tempted to use - a barrier(2) as - the synchronization mechanism, making each new fiber call its barrier::wait() method, - then calling wait() - in the launching fiber to wait until the first other fiber completes. -

-

- That will in fact unblock the launching fiber. The unfortunate part - is that it will continue blocking the remaining - fibers. -

-

- Consider the following scenario: -

-
    -
  1. - Fiber main launches fibers A, B, C and D, then calls - barrier::wait(). -
  2. -
  3. - Fiber C finishes first and likewise calls barrier::wait(). -
  4. -
  5. - Fiber main is unblocked, as desired. -
  6. -
  7. - Fiber B calls barrier::wait(). Fiber B is blocked! -
  8. -
  9. - Fiber A calls barrier::wait(). Fibers A and B are unblocked. -
  10. -
  11. - Fiber D calls barrier::wait(). Fiber D is blocked indefinitely. -
  12. -
-

- (See also when_any, simple - completion.) -

-
- - - - - -
[Note]Note

- It is unwise to tie the lifespan of a barrier to any one of its participating - fibers. Although conceptually all waiting fibers awaken simultaneously, - because of the nature of fibers, in practice they will awaken one - by one in indeterminate order.[2] The rest of the waiting fibers will still be blocked - in wait(), - which must, before returning, access data members in the barrier - object. -

-

-

-
- - - Class barrier -
-

-

-
#include <boost/fiber/barrier.hpp>
-
-class barrier {
-public:
-    explicit barrier( std::size_t);
-
-    barrier( barrier const&) = delete;
-    barrier & operator=( barrier const&) = delete;
-
-    bool wait();
-};
-
-

- Instances of barrier are not copyable or movable. -

- - Constructor -
explicit barrier( std::size_t initial);
-
-
-

-
-
Effects:
-

- Construct a barrier for initial - fibers. -

-
Throws:
-

- fiber_error -

-
Error Conditions:
-

- invalid_argument: if initial is zero. -

-
-
-

-

-
- - - Member function wait() -
-

-

-
bool wait();
-
-
-

-
-
Effects:
-

- Block until initial - fibers have called wait - on *this. - When the initial-th - fiber calls wait, - all waiting fibers are unblocked, and the barrier is reset. -

-
Returns:
-

- true for exactly - one fiber from each batch of waiting fibers, false - otherwise. -

-
Throws:
-

- fiber_error -

-
Notes:
-

- wait() - is one of the predefined interruption-points. -

-
-
-
-
- -

- Boost.Fiber provides a bounded and - a unbounded channel suitable to synchonize fibers via message passing. -

-
typedef boost::fibers::unbounded_channel< int > channel_t;
-
-void send( channel_t & channel) {
-    for ( int i = 0; i < 5; ++i) {
-        channel.push( i);
-    }
-    channel.close();
-}
-
-void recv( channel_t & channel) {
-    int i;
-    while ( boost::fibers::channel_op_status::success == channel.pop(i) ) {
-        std::cout << "received " << i << std::endl;
-    }
-}
-
-channel_t channel;
-boost::fibers::fiber f1( std::bind( send, ref( channel) ) );
-boost::fibers::fiber f2( std::bind( recv, ref( channel) ) );
-
-f1.join();
-f2.join();
-
- - Enumeration - channel_op_status -

- channel operations return the state of the channel. -

-
enum class channel_op_status {
-    success,
-    empty,
-    full,
-    closed,
-    timeout
-};
-
- - success -
-

-
-
Effects:
-

- Operation was successful. -

-
-
- - empty -
-

-
-
Effects:
-

- channel is empty, operation failed. -

-
-
- - full -
-

-
-
Effects:
-

- channel is full, operation failed. -

-
-
- - closed -
-

-
-
Effects:
-

- channel is closed, operation failed. -

-
-
- - timeout -
-

-
-
Effects:
-

- The operation did not become ready before specified timeout elapsed. -

-
-
-

-

-
- - - Template - unbounded_channel<> -
-

-

-
#include <boost/fiber/unbounded_channel.hpp>
-
-template< typename T, typename Allocator = std::allocator< T > >
-class unbounded_channel {
-public:
-    typedef T   value_type;
-
-    explicit unbounded_channel( Allocator const& alloc = Allocator() ) noexcept;
-
-    unbounded_channel( unbounded_channel const& other) = delete;
-    unbounded_channel & operator=( unbounded_channel const& other) = delete;
-
-    void close() noexcept;
-
-    channel_op_status push( value_type const& va);
-    channel_op_status push( value_type && va);
-
-    channel_op_status pop( value_type & va);
-    value_type value_pop();
-    channel_op_status try_pop( value_type & va);
-    template< typename Rep, typename Period >
-    channel_op_status pop_wait_for(
-        value_type & va,
-        std::chrono::duration< Rep, Period > const& timeout_duration);
-    template< typename Clock, typename Duration >
-    channel_op_status pop_wait_until(
-        value_type & va,
-        std::chrono::time_point< Clock, Duration > const& timeout_time);
-};
-
- - Constructor -
explicit unbounded_channel( Allocator const& alloc = Allocator() ) noexcept;
-
-
-

-
-
Effects:
-

- Constructs an object of class unbounded_channel. - Internal nodes are allocated using alloc - - C++11-allocators are supported. -

-
Throws:
-

- Nothing. -

-
See also:
-

- Allocator concept, - std::allocator< - T > -

-
-
-

-

-
- - - Member - function close() -
-

-

-
void close() noexcept;
-
-
-

-
-
Effects:
-

- Deactivates the channel. No values can be put after calling - this->close(). - Fibers blocked in this->pop(), this->pop_wait_for() or this->pop_wait_until() will return closed. - Fibers blocked in this->value_pop() will receive an exception. -

-
Throws:
-

- Nothing. -

-
Note:
-

- close() - is like closing a pipe. It informs waiting consumers that no - more values will arrive. -

-
-
-

-

-
- - - Member - function push() -
-

-

-
channel_op_status push( value_type const& va);
-channel_op_status push( value_type && va);
-
-
-

-
-
Effects:
-

- If channel is closed, returns closed. - Otherwise enqueues the value in the channel, wakes up a fiber - blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. -

-
Throws:
-

- Exceptions thrown by memory allocation and copying or moving - va. -

-
-
-

-

-
- - - Member - function pop() -
-

-

-
channel_op_status pop( value_type & va);
-
-
-

-
-
Effects:
-

- Dequeues a value from the channel. If the channel is empty, the - fiber gets suspended until at least one new item is push()ed - (return value success - and va contains - dequeued value) or the channel gets close()d (return value closed). -

-
Throws:
-

- fiber_interrupted -

-
-
-

-

-
- - - Member - function value_pop() -
-

-

-
value_type value_pop();
-
-
-

-
-
Effects:
-

- Dequeues a value from the channel. If the channel is empty, the - fiber gets suspended until at least one new item is push()ed - or the channel gets close()d (which throws an exception). -

-
Throws:
-

- fiber_error if - *this - is closed or fiber_interrupted -

-
Error conditions:
-

- std::errc::operation_not_permitted -

-
-
-

-

-
- - - Member - function try_pop() -
-

-

-
channel_op_status try_pop( value_type & va);
-
-
-

-
-
Effects:
-

- If channel is empty, returns empty. - If channel is closed, returns closed. - Otherwise it returns success - and va contains - the dequeued value. -

-
Throws:
-

- Exceptions thrown by copy- or move-operations. -

-
-
-

-

-
- - - Member - function pop_wait_for() -
-

-

-
template< typename Rep, typename Period >
-channel_op_status pop_wait_for(
-    value_type & va,
-    std::chrono::duration< Rep, Period > const& timeout_duration)
-
-
-

-
-
Effects:
-

- Accepts std::chrono::duration and internally computes - a timeout time as (system time + timeout_duration). - If channel is not empty, immediately dequeues a value from the - channel. Otherwise the fiber gets suspended until at least one - new item is push()ed (return value success and va - contains dequeued value), or the channel gets close()d (return value closed), or the system time - reaches the computed timeout time (return value timeout). -

-
Throws:
-

- fiber_interrupted - or timeout-related exceptions. -

-
-
-

-

-
- - - Member - function pop_wait_until() -
-

-

-
template< typename Clock, typename Duration >
-channel_op_status pop_wait_until(
-    value_type & va,
-    std::chrono::time_point< Clock, Duration > const& timeout_time)
-
-
-

-
-
Effects:
-

- Accepts a std::chrono::time_point< Clock, Duration - >. If channel is not - empty, immediately dequeues a value from the channel. Otherwise - the fiber gets suspended until at least one new item is push()ed - (return value success - and va contains - dequeued value), or the channel gets close()d (return value closed), or the system time - reaches the passed time_point - (return value timeout). -

-
Throws:
-

- fiber_interrupted - or timeout-related exceptions. -

-
-
-

-

-
- - - Template - bounded_channel<> -
-

-

-
#include <boost/fiber/bounded_channel.hpp>
-
-template< typename T, typename Allocator = std::allocator< T > >
-class bounded_channel {
-public:
-    typedef T   value_type;
-
-    bounded_channel( std::size_t wm, Allocator const& alloc = Allocator() );
-    bounded_channel( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() );
-
-    bounded_channel( bounded_channel const& other) = delete;
-    bounded_channel & operator=( bounded_channel const& other) = delete;
-
-    std::size_t upper_bound() const noexcept;
-    std::size_t lower_bound() const noexcept;
-
-    void close() noexcept;
-
-    channel_op_status push( value_type const& va);
-    channel_op_status push( value_type && va);
-    template< typename Rep, typename Period >
-    channel_op_status push_wait_for(
-        value_type const& va,
-        std::chrono::duration< Rep, Period > const& timeout_duration);
-    channel_op_status push_wait_for( value_type && va,
-        std::chrono::duration< Rep, Period > const& timeout_duration);
-    template< typename Clock, typename Duration >
-    channel_op_status push_wait_until(
-        value_type const& va,
-        std::chrono::time_point< Clock, Duration > const& timeout_time);
-    template< typename Clock, typename Duration >
-    channel_op_status push_wait_until(
-        value_type && va,
-        std::chrono::time_point< Clock, Duration > const& timeout_time);
-    channel_op_status try_push( value_type const& va);
-    channel_op_status try_push( value_type && va);
-
-    channel_op_status pop( value_type & va);
-    value_type value_pop();
-    template< typename Rep, typename Period >
-    channel_op_status pop_wait_for(
-        value_type & va,
-        std::chrono::duration< Rep, Period > const& timeout_duration);
-    template< typename Clock, typename Duration >
-    channel_op_status pop_wait_until(
-        value_type & va,
-        std::chrono::time_point< Clock, Duration > const& timeout_time);
-    channel_op_status try_pop( value_type & va);
-};
-
- - Constructor -
bounded_channel( std::size_t wm, Allocator const& alloc = Allocator() );
-bounded_channel( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() );
-
-
-

-
-
Preconditions:
-

- hwm > - lwm -

-
Effects:
-

- Constructs an object of class bounded_channel. - The constructor with two arguments constructs an object of class - bounded_channel - with a high-watermark of hwm - and a low-watermark of lwm - items. The constructor with one std::size_t - argument is effectively the same as bounded_channel(wm, (wm-1), alloc). - Internal nodes are allocated using alloc - - C++11-allocators are supported. -

-
Throws:
-

- fiber_error -

-
Error Conditions:
-

- invalid_argument: if lwm >= - hwm. -

-
Notes:
-

- Once the number of values in the channel reaches hwm, any call to push(), - push_wait_for() or push_wait_until() will block until the number - of values in the channel is at most lwm. - That is, if lwm < (hwm-1), - the channel can be in a state in which push(), push_wait_for() or push_wait_until() calls will block (channel is - full) even though the number of values in the channel is less - than hwm. -

-
See also:
-

- Allocator concept, - std::allocator< - T > -

-
-
-

-

-
- - - Member - function upper_bound() -
-

-

-
std::size_t upper_bound() const noexcept;
-
-
-

-
-
Returns:
-

- the high-watermark with which *this was constructed. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function lower_bound() -
-

-

-
std::size_t lower_bound() const noexcept;
-
-
-

-
-
Returns:
-

- the low-watermark with which *this was constructed. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function close() -
-

-

-
void close() noexcept;
-
-
-

-
-
Effects:
-

- Deactivates the channel. No values can be put after calling - this->close(). - Fibers blocked in this->pop(), this->pop_wait_for() or this->pop_wait_until() will return closed. - Fibers blocked in this->value_pop() will receive an exception. -

-
Throws:
-

- Nothing. -

-
Note:
-

- close() - is like closing a pipe. It informs waiting consumers that no - more values will arrive. -

-
-
-

-

-
- - - Member - function push() -
-

-

-
channel_op_status push( value_type const& va);
-channel_op_status push( value_type && va);
-
-
-

-
-
Effects:
-

- If channel is closed, returns closed. - If channel is not full, enqueues the value in the channel, wakes - up a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. - Otherwise the calling fiber is suspended until the number of - values in the channel drops to lwm - (return value success)or - the channel is close()d (return value closed). -

-
Throws:
-

- fiber_interrupted - or exceptions thrown by memory allocation and copying or moving - va. -

-
-
-

-

-
- - - Member - function push_wait_for() -
-

-

-
template< typename Rep, typename Period >
-channel_op_status push_wait_for(
-    value_type const& va,
-    std::chrono::duration< Rep, Period > const& timeout_duration);
-
-template< typename Rep, typename Period >
-channel_op_status push_wait_for(
-    value_type && va,
-    std::chrono::duration< Rep, Period > const& timeout_duration);
-
-
-

-
-
Effects:
-

- Accepts std::chrono::duration and internally computes - a time_point as (system time + timeout_duration). - If channel is closed, returns closed. - If channel is not full, enqueues the value in the channel, wakes - up a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. - Otherwise the calling fiber is suspended until the number of - values in the channel drops to lwm - (return value success), - the channel is close()d (return value closed), or the system time - reaches the computed time_point (return value timeout). -

-
Throws:
-

- fiber_interrupted, - exceptions thrown by memory allocation and copying or moving - va or timeout-related - exceptions. -

-
-
-

-

-
- - - Member - function push_wait_until() -
-

-

-
template< typename Clock, typename Duration >
-channel_op_status push_wait_until(
-    value_type const& va,
-    std::chrono::time_point< Clock, Duration > const& timeout_time);
-
-template< typename Clock, typename Duration >
-channel_op_status push_wait_until(
-    value_type && va,
-    std::chrono::time_point< Clock, Duration > const& timeout_time);
-
-
-

-
-
Effects:
-

- Accepts an absolute timeout_time - in any supported time_point type. If channel is closed, returns - closed. If channel - is not full, enqueues the value in the channel, wakes up a fiber - blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. - Otherwise the calling fiber is suspended until the number of - values in the channel drops to lwm - (return value success), - the channel is close()d (return value closed), or the system time - reaches the passed time_point (return value timeout). -

-
Throws:
-

- fiber_interrupted - or exceptions thrown by memory allocation and copying or moving - va or timeout-related - exceptions. -

-
-
-

-

-
- - - Member - function try_push() -
-

-

-
channel_op_status try_push( value_type const& va);
-channel_op_status try_push( value_type && va);
-
-
-

-
-
Effects:
-

- If channel is full, returns full. - If channel is closed, returns closed. - Otherwise enqueues the value in the channel, wakes up a fiber - blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. -

-
Throws:
-

- Exceptions thrown by memory allocation and copying or moving - va. -

-
-
-

-

-
- - - Member - function pop() -
-

-

-
channel_op_status pop( value_type & va);
-
-
-

-
-
Effects:
-

- Dequeues a value from the channel. If the channel is empty, the - fiber gets suspended until at least one new item is push()ed - (return value success - and va contains - dequeued value) or the channel gets close()d (return value closed). Once the number of - items remaining in the channel drops to lwm, - any fibers blocked on push(), push_wait_for() or push_wait_until() may resume. -

-
Throws:
-

- fiber_interrupted -

-
-
-

-

-
- - - Member - function value_pop() -
-

-

-
value_type value_pop();
-
-
-

-
-
Effects:
-

- Dequeues a value from the channel. If the channel is empty, the - fiber gets suspended until at least one new item is push()ed - or the channel gets close()d (which throws an exception). - Once the number of items remaining in the channel drops to lwm, any fibers blocked on - push(), - push_wait_for() or push_wait_until() may resume. -

-
Throws:
-

- fiber_error if - *this - is closed or fiber_interrupted -

-
Error conditions:
-

- std::errc::operation_not_permitted -

-
-
-

-

-
- - - Member - function try_pop() -
-

-

-
channel_op_status try_pop( value_type & va);
-
-
-

-
-
Effects:
-

- If channel is empty, returns empty. - If channel is closed, returns closed. - Otherwise it returns success - and va contains - the dequeued value. Once the number of items remaining in the - channel drops to lwm, - any fibers blocked on push(), push_wait_for() or push_wait_until() may resume. -

-
Throws:
-

- Exceptions thrown by copy- or move-operations. -

-
-
-

-

-
- - - Member - function pop_wait_for() -
-

-

-
template< typename Rep, typename Period >
-channel_op_status pop_wait_for(
-    value_type & va,
-    std::chrono::duration< Rep, Period > const& timeout_duration)
-
-
-

-
-
Effects:
-

- Accepts std::chrono::duration and internally computes - a timeout time as (system time + timeout_duration). - If channel is not empty, immediately dequeues a value from the - channel. Otherwise the fiber gets suspended until at least one - new item is push()ed (return value success and va - contains dequeued value), or the channel gets close()d (return value closed), or the system time - reaches the computed timeout time (return value timeout). Once the number of - items remaining in the channel drops to lwm, - any fibers blocked on push(), push_wait_for() or push_wait_until() may resume. -

-
Throws:
-

- fiber_interrupted - or timeout-related exceptions. -

-
-
-

-

-
- - - Member - function pop_wait_until() -
-

-

-
template< typename Clock, typename Duration >
-channel_op_status pop_wait_until(
-    value_type & va,
-    std::chrono::time_point< Clock, Duration > const& timeout_time)
-
-
-

-
-
Effects:
-

- Accepts a std::chrono::time_point< Clock, Duration - >. If channel is not - empty, immediately dequeues a value from the channel. Otherwise - the fiber gets suspended until at least one new item is push()ed - (return value success - and va contains - dequeued value), or the channel gets close()d (return value closed), or the system time - reaches the passed time_point - (return value timeout). - Once the number of items remaining in the channel drops to lwm, any fibers blocked on - push(), - push_wait_for() or push_wait_until() may resume. -

-
Throws:
-

- fiber_interrupted - or timeout-related exceptions. -

-
-
-
-
- - - - Overview -

- The futures library provides a means of handling asynchronous future - values, whether those values are generated by another fiber, or on - a single fiber in response to external stimuli, or on-demand. -

-

- This is done through the provision of four class templates: future<> and - shared_future<> which are used to retrieve - the asynchronous results, and promise<> and packaged_task<> which - are used to generate the asynchronous results. -

-

- An instance of future<> holds the one and only reference - to a result. Ownership can be transferred between instances using the - move constructor or move-assignment operator, but at most one instance - holds a reference to a given asynchronous result. When the result is - ready, it is returned from future::get() by rvalue-reference - to allow the result to be moved or copied as appropriate for the type. -

-

- On the other hand, many instances of shared_future<> may - reference the same result. Instances can be freely copied and assigned, - and shared_future::get() -returns a const - reference so that multiple calls to shared_future::get() -are - safe. You can move an instance of future<> into an - instance of shared_future<>, thus transferring - ownership of the associated asynchronous result, but not vice-versa. -

-

- fibers::async() is a simple way of running asynchronous - tasks. A call to async() spawns a fiber and returns a future<> that - will deliver the result of the fiber function. -

- - Creating - asynchronous values -

- You can set the value in a future with either a promise<> or - a packaged_task<>. A packaged_task<> is - a callable object with void - return that wraps a function or callable object returning the specified - type. When the packaged_task<> is invoked, - it invokes the contained function in turn, and populates a future with - the contained function's return value. This is an answer to the perennial - question: How do I return a value from a fiber? Package - the function you wish to run as a packaged_task<> and - pass the packaged task to the fiber constructor. The future retrieved - from the packaged task can then be used to obtain the return value. - If the function throws an exception, that is stored in the future in - place of the return value. -

-
int calculate_the_answer_to_life_the_universe_and_everything() {
-    return 42;
-}
-
-boost::fibers::packaged_task<int()> pt(calculate_the_answer_to_life_the_universe_and_everything);
-boost::fibers::future<int> fi=pt.get_future();
-boost::fibers::fiber(std::move(pt)).detach(); // launch task on a fiber
-
-fi.wait(); // wait for it to finish
-
-assert(fi.is_ready());
-assert(fi.has_value());
-assert(!fi.has_exception());
-assert(fi.get()==42);
-
-

- A promise<> is a bit more low level: it just provides - explicit functions to store a value or an exception in the associated - future. A promise can therefore be used where the value might come - from more than one possible source. -

-
boost::fibers::promise<int> pi;
-boost::fibers::future<int> fi;
-fi=pi.get_future();
-
-pi.set_value(42);
-
-assert(fi.is_ready());
-assert(fi.has_value());
-assert(!fi.has_exception());
-assert(fi.get()==42);
-
-
-
-Future -
-

- A future provides a mechanism to access the result of an asynchronous - operation. -

- - shared - state -

- Behind a promise<> and its future<> lies - an unspecified object called their shared state. - The shared state is what will actually hold the async result (or - the exception). -

-

- The shared state is instantiated along with the promise<>. -

-

- Aside from its originating promise<>, a future<> holds - a unique reference to a particular shared state. However, multiple - shared_future<> instances can reference the - same underlying shared state. -

-

- As packaged_task<> and fibers::async() are - implemented using promise<>, discussions of shared - state apply to them as well. -

- - Enumeration - future_status -

- Timed wait-operations ( future::wait_for() and future::wait_until()) - return the state of the future. -

-
enum class future_status {
-    ready,
-    timeout,
-    deferred  // not supported yet
-};
-
- - ready -
-

-
-
Effects:
-

- The shared state is ready. -

-
-
- - timeout -
-

-
-
Effects:
-

- The shared state did not - become ready before timeout has passed. -

-
-
-
- - - - - -
[Note]Note

- Deferred futures are not supported. -

-

-

-
- - - Template future<> -
-

-

-

- A future<> contains a shared - state which is not shared with any other future. -

-
template< typename R >
-class future {
-public:
-    future() noexcept;
-
-    future( future const& other) = delete;
-
-    future & operator=( future const& other) = delete;
-
-    future( future && other) noexcept;
-
-    future & operator=( future && other) noexcept;
-
-    ~future();
-
-    bool valid() const noexcept;
-
-    shared_future< R > share();
-
-    R get();    // member only of generic future template
-    R & get();  // member only of future< R & > template specialization
-    void get(); // member only of future< void > template specialization
-
-    std::exception_ptr get_exception_ptr();
-
-    void wait() const;
-
-    template< class Rep, class Period >
-    future_status wait_for(
-        std::chrono::duration< Rep, Period > const& timeout_duration) const;
-
-    template< typename Clock, typename Duration >
-    future_status wait_until(
-        std::chrono::time_point< Clock, Duration > const& timeout_time) const;
-};
-
- - Default - constructor -
future() noexcept;
-
-
-

-
-
Effects:
-

- Creates a future with no shared - state. After construction false - == valid(). -

-
Throws:
-

- Nothing. -

-
-
- - Move - constructor -
future( future && other) noexcept;
-
-
-

-
-
Effects:
-

- Constructs a future with the shared - state of other. After construction false - == other.valid(). -

-
Throws:
-

- Nothing. -

-
-
- - Destructor -
~future();
-
-
-

-
-
Effects:
-

- Destroys the future; ownership is abandoned. -

-
Note:
-

- ~future() does not block the calling fiber. -

-
-
-

- Consider a sequence such as: -

-
    -
  1. - instantiate promise<> -
  2. -
  3. - obtain its future<> via promise::get_future() -
  4. -
  5. - launch fiber, capturing promise<> -
  6. -
  7. - destroy future<> -
  8. -
  9. - call promise::set_value() -
  10. -
-

- The final set_value() call succeeds, but the value is - silently discarded: no additional future<> can be obtained from that - promise<>. -

-

-

-
- - - Member - function operator=() -
-

-

-
future & operator=( future && other) noexcept;
-
-
-

-
-
Effects:
-

- Moves the shared state - of other to this. - After the assignment, false - == other.valid(). -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member function valid() -
-

-

-
bool valid() const noexcept;
-
-
-

-
-
Effects:
-

- Returns true if - future contains a shared state. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member function share() -
-

-

-
shared_future< R > share();
-
-
-

-
-
Effects:
-

- Move the state to a shared_future<>. -

-
Returns:
-

- a shared_future<> containing the shared state formerly belonging - to *this. -

-
Postcondition:
-

- false == - valid() -

-
Throws:
-

- future_error - with error condition future_errc::no_state. -

-
-
-

-

-
- - - Member function get() -
-

-

-
R get();    // member only of generic future template
-R & get();  // member only of future< R & > template specialization
-void get(); // member only of future< void > template specialization
-
-
-

-
-
Precondition:
-

- true == - valid() -

-
Returns:
-

- Waits until promise::set_value() or promise::set_exception() is - called. If promise::set_value() is called, - returns the value. If promise::set_exception() is - called, throws the indicated exception. -

-
Postcondition:
-

- false == - valid() -

-
Throws:
-

- future_error - with error condition future_errc::no_state, - fiber_interrupted, - future_errc::broken_promise. - Any exception passed to promise::set_exception(). -

-
-
-

-

-
- - - Member - function get_exception_ptr() -
-

-

-
std::exception_ptr get_exception_ptr();
-
-
-

-
-
Precondition:
-

- true == - valid() -

-
Returns:
-

- Waits until promise::set_value() or promise::set_exception() is - called. If set_value() is called, returns a default-constructed - std::exception_ptr. If set_exception() - is called, returns the passed std::exception_ptr. -

-
Throws:
-

- future_error - with error condition future_errc::no_state - or fiber_interrupted. -

-
Note:
-

- get_exception_ptr() does not - invalidate the future. After calling get_exception_ptr(), you may still call future::get(). -

-
-
-

-

-
- - - Member function wait() -
-

-

-
void wait();
-
-
-

-
-
Effects:
-

- Waits until promise::set_value() or promise::set_exception() is - called. -

-
Throws:
-

- future_error - with error condition future_errc::no_state - or fiber_interrupted. -

-
-
-

-

-
- - - Templated - member function wait_for() -
-

-

-
template< class Rep, class Period >
-future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const;
-
-
-

-
-
Effects:
-

- Waits until promise::set_value() or promise::set_exception() is - called, or timeout_duration - has passed. -

-
Result:
-

- A future_status - is returned indicating the reason for returning. -

-
Throws:
-

- future_error - with error condition future_errc::no_state - or fiber_interrupted - or timeout-related exceptions. -

-
-
-

-

-
- - - Templated - member function wait_until() -
-

-

-
template< typename Clock, typename Duration >
-future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const;
-
-
-

-
-
Effects:
-

- Waits until promise::set_value() or promise::set_exception() is - called, or timeout_time - has passed. -

-
Result:
-

- A future_status - is returned indicating the reason for returning. -

-
Throws:
-

- future_error - with error condition future_errc::no_state - or fiber_interrupted - or timeout-related exceptions. -

-
-
-

-

-
- - - Template - shared_future<> -
-

-

-

- A shared_future<> contains a shared - state which might be shared with other shared_future<> instances. -

-
template< typename R >
-class shared_future {
-public:
-    shared_future() noexcept;
-
-    ~shared_future();
-
-    shared_future( shared_future const& other);
-
-    shared_future( future< R > && other) noexcept;
-
-    shared_future( shared_future && other) noexcept;
-
-    shared_future & operator=( shared_future && other) noexcept;
-
-    shared_future & operator=( future< R > && other) noexcept;
-
-    shared_future & operator=( shared_future const& other) noexcept;
-
-    bool valid() const noexcept;
-
-    R const& get(); // member only of generic shared_future template
-    R & get();      // member only of shared_future< R & > template specialization
-    void get();     // member only of shared_future< void > template specialization
-
-    std::exception_ptr get_exception_ptr();
-
-    void wait() const;
-
-    template< class Rep, class Period >
-    future_status wait_for(
-        std::chrono::duration< Rep, Period > const& timeout_duration) const;
-
-    template< typename Clock, typename Duration >
-    future_status wait_until(
-        std::chrono::time_point< Clock, Duration > const& timeout_time) const;
-};
-
- - Default - constructor -
shared_future();
-
-
-

-
-
Effects:
-

- Creates a shared_future with no shared - state. After construction false - == valid(). -

-
Throws:
-

- Nothing. -

-
-
- - Move - constructor -
shared_future( future< R > && other) noexcept;
-shared_future( shared_future && other) noexcept;
-
-
-

-
-
Effects:
-

- Constructs a shared_future with the shared - state of other. After construction false - == other.valid(). -

-
Throws:
-

- Nothing. -

-
-
- - Copy - constructor -
shared_future( shared_future const& other) noexcept;
-
-
-

-
-
Effects:
-

- Constructs a shared_future with the shared - state of other. After construction other.valid() is unchanged. -

-
Throws:
-

- Nothing. -

-
-
- - Destructor -
~shared_future();
-
-
-

-
-
Effects:
-

- Destroys the shared_future; ownership is abandoned if not shared. -

-
Note:
-

- ~shared_future() does not block the calling - fiber. -

-
-
-

-

-
- - - Member - function operator=() -
-

-

-
shared_future & operator=( future< R > && other) noexcept;
-shared_future & operator=( shared_future && other) noexcept;
-shared_future & operator=( shared_future const& other) noexcept;
-
-
-

-
-
Effects:
-

- Moves or copies the shared state - of other to this. - After the assignment, the state of other.valid() depends on which overload - was invoked: unchanged for the overload accepting shared_future const&, otherwise false. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function valid() -
-

-

-
bool valid() const noexcept;
-
-
-

-
-
Effects:
-

- Returns true if - shared_future contains a shared - state. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function get() -
-

-

-
R const& get(); // member only of generic shared_future template
-R & get();      // member only of shared_future< R & > template specialization
-void get();     // member only of shared_future< void > template specialization
-
-
-

-
-
Precondition:
-

- true == - valid() -

-
Returns:
-

- Waits until promise::set_value() or promise::set_exception() is - called. If promise::set_value() is called, - returns the value. If promise::set_exception() is - called, throws the indicated exception. -

-
Postcondition:
-

- false == - valid() -

-
Throws:
-

- future_error - with error condition future_errc::no_state, - fiber_interrupted, - future_errc::broken_promise. - Any exception passed to promise::set_exception(). -

-
-
-

-

-
- - - Member - function get_exception_ptr() -
-

-

-
std::exception_ptr get_exception_ptr();
-
-
-

-
-
Precondition:
-

- true == - valid() -

-
Returns:
-

- Waits until promise::set_value() or promise::set_exception() is - called. If set_value() is called, returns a default-constructed - std::exception_ptr. If set_exception() - is called, returns the passed std::exception_ptr. -

-
Throws:
-

- future_error - with error condition future_errc::no_state - or fiber_interrupted. -

-
Note:
-

- get_exception_ptr() does not - invalidate the shared_future. After calling get_exception_ptr(), you may still call shared_future::get(). -

-
-
-

-

-
- - - Member - function wait() -
-

-

-
void wait();
-
-
-

-
-
Effects:
-

- Waits until promise::set_value() or promise::set_exception() is - called. -

-
Throws:
-

- future_error - with error condition future_errc::no_state - or fiber_interrupted. -

-
-
-

-

-
- - - Templated - member function wait_for() -
-

-

-
template< class Rep, class Period >
-future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const;
-
-
-

-
-
Effects:
-

- Waits until promise::set_value() or promise::set_exception() is - called, or timeout_duration - has passed. -

-
Result:
-

- A future_status - is returned indicating the reason for returning. -

-
Throws:
-

- future_error - with error condition future_errc::no_state - or fiber_interrupted - or timeout-related exceptions. -

-
-
-

-

-
- - - Templated - member function wait_until() -
-

-

-
template< typename Clock, typename Duration >
-future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const;
-
-
-

-
-
Effects:
-

- Waits until promise::set_value() or promise::set_exception() is - called, or timeout_time - has passed. -

-
Result:
-

- A future_status - is returned indicating the reason for returning. -

-
Throws:
-

- future_error - with error condition future_errc::no_state - or fiber_interrupted - or timeout-related exceptions. -

-
-
-

-

-
- - - Non-member function - fibers::async() -
-

-

-
#include <boost/fiber/future/async.hpp>
-
-template< class Function, class ... Args >
-future<
-    std::result_of_t<
-        std::decay_t< Function >( std::decay_t< Args > ... )
-    >
->
-async( Function && fn, Args && ... args);
-
-template< typename StackAllocator, class Function, class ... Args >
-future<
-    std::result_of_t<
-        std::decay_t< Function >( std::decay_t< Args > ... )
-    >
->
-async( std::allocator_arg_t, StackAllocator salloc, Function && fn, Args && ... args);
-
-
-

-
-
Effects:
-

- Executes fn - in a fiber and returns an associated future<>. -

-
Result:
-
-

-

-
future<
-    std::result_of_t<
-        std::decay_t< Function >( std::decay_t< Args > ... )
-    >
->
-

- representing the shared state - associated with the asynchronous execution of fn. -

-
-
Throws:
-

- fiber_error - or future_error - if an error occurs. -

-
Notes:
-

- The overload accepting std::allocator_arg_t uses - the passed StackAllocator when - constructing the launched fiber. -

-
-
-
- - - - - -
[Note]Note

- Deferred futures are not supported. -

-
-
- -

- A promise<> provides a mechanism to store a value - (or exception) that can later be retrieved from the corresponding - future<> object. promise<> and future<> communicate via their underlying - shared state. -

-
template< typename R >
-class promise {
-public:
-    promise();
-
-    template< typename Allocator >
-    promise( std::allocator_arg_t, Allocator);
-
-    promise( promise &&) noexcept;
-
-    promise & operator=( promise &&) noexcept;
-
-    promise( promise const&) = delete;
-
-    promise & operator=( promise const&) = delete;
-
-    ~promise();
-
-    void swap( promise &) noexcept;
-
-    future< R > get_future();
-
-    void set_value( R const&);  // member only of generic promise template
-    void set_value( R &&);      // member only of generic promise template
-    void set_value( R &);       // member only of promise< R & > template
-    void set_value();           // member only of promise< void > template
-
-    void set_exception( std::exception_ptr p);
-};
-
-template< typename R >
-void swap( promise< R > &, promise< R > &) noexcept;
-
- - Default - constructor -
promise();
-
-
-

-
-
Effects:
-

- Creates a promise with an empty shared - state. -

-
Throws:
-

- Exceptions caused by memory allocation. -

-
-
- - Constructor -
template< typename Allocator >
-promise( std::allocator_arg_t, Allocator alloc);
-
-
-

-
-
Effects:
-

- Creates a promise with an empty shared - state by using alloc. -

-
Throws:
-

- Exceptions caused by memory allocation. -

-
See also:
-

- std::allocator_arg_t -

-
-
- - Move - constructor -
promise( promise && other) noexcept;
-
-
-

-
-
Effects:
-

- Creates a promise by moving the shared - state from other. -

-
Postcondition:
-

- other contains - no valid shared state. -

-
Throws:
-

- Nothing. -

-
-
- - Destructor -
~promise();
-
-
-

-
-
Effects:
-

- Destroys *this - and abandons the shared state - if shared state is ready; otherwise stores future_error - with error condition future_errc::broken_promise - as if by promise::set_exception(): the - shared state is set ready. -

-
-
-

-

-
- - - Member - function operator=() -
-

-

-
promise & operator=( promise && other) noexcept;
-
-
-

-
-
Effects:
-

- Transfers the ownership of shared - state to *this. -

-
Postcondition:
-

- other contains - no valid shared state. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member function swap() -
-

-

-
void swap( promise & other) noexcept;
-
-
-

-
-
Effects:
-

- Swaps the shared state - between other and *this. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function get_future() -
-

-

-
future< R > get_future();
-
-
-

-
-
Returns:
-

- A future<> with the same shared - state. -

-
Throws:
-

- future_error - with future_errc::future_already_retrieved - or future_errc::no_state. -

-
-
-

-

-
- - - Member - function set_value() -
-

-

-
void set_value( R const& value);  // member only of generic promise template
-void set_value( R && value);      // member only of generic promise template
-void set_value( R & value);       // member only of promise< R & > template
-void set_value();                 // member only of promise< void > template
-
-
-

-
-
Effects:
-

- Store the result in the shared - state and marks the state as ready. -

-
Throws:
-

- future_error - with future_errc::future_already_satisfied - or future_errc::no_state. -

-
-
-

-

-
- - - Member - function set_exception() -
-

-

-
void set_exception( std::exception_ptr);
-
-
-

-
-
Effects:
-

- Store an exception pointer in the shared - state and marks the state as ready. -

-
Throws:
-

- future_error - with future_errc::future_already_satisfied - or future_errc::no_state. -

-
-
-

-

-
- - - Non-member - function swap() -
-

-

-
template< typename R >
-void swap( promise< R > & l, promise< R > & r) noexcept;
-
-
-

-
-
Effects:
-

- Same as l.swap( - r). -

-
-
-
-
- -

- A packaged_task<> wraps a callable target - that returns a value so that the return value can be computed asynchronously. -

-

- Conventional usage of packaged_task<> is like this: -

-
    -
  1. - Instantiate packaged_task<> with template arguments - matching the signature of the callable. Pass the callable to - the constructor. -
  2. -
  3. - Call packaged_task::get_future() and capture - the returned future<> instance. -
  4. -
  5. - Launch a fiber to run the new packaged_task<>, passing any arguments - required by the original callable. -
  6. -
  7. - Call fiber::detach() on the newly-launched fiber. -
  8. -
  9. - At some later point, retrieve the result from the future<>. -
  10. -
-

- This is, in fact, pretty much what fibers::async() -encapsulates. -

-
template< class R, typename ... Args >
-class packaged_task< R( Args ... ) > {
-public:
-    packaged_task() noexcept;
-
-    template< typename Fn >
-    explicit packaged_task( Fn &&);
-
-    template< typename Fn, typename Allocator >
-    packaged_task( std::allocator_arg_t, Allocator const&, Fn &&);
-
-    packaged_task( packaged_task &&) noexcept;
-
-    packaged_task & operator=( packaged_task &&) noexcept;
-
-    packaged_task( packaged_task const&) = delete;
-
-    packaged_task & operator=( packaged_task const&) = delete;
-
-    ~packaged_task();
-
-    void swap( packaged_task &) noexcept;
-
-    bool valid() const noexcept;
-
-    future< R > get_future();
-
-    void operator()( Args ...);
-
-    void reset();
-};
-
-template< typename Signature >
-void swap( packaged_task< Signature > &, packaged_task< Signature > &) noexcept;
-
- - Default - constructor packaged_task() -
packaged_task() noexcept;
-
-
-

-
-
Effects:
-

- Constructs an object of class packaged_task - with no shared state. -

-
Throws:
-

- Nothing. -

-
-
- - Templated - constructor packaged_task() -
template< typename Fn >
-explicit packaged_task( Fn && fn);
-
-template< typename Fn, typename Allocator >
-packaged_task( std::allocator_arg_t, Allocator const& alloc, Fn && fn);
-
-
-

-
-
Effects:
-

- Constructs an object of class packaged_task - with a shared state and - copies or moves the callable target fn - to internal storage. -

-
Throws:
-

- Exceptions caused by memory allocation. -

-
Note:
-

- The signature of Fn - should have a return type convertible to R. -

-
See also:
-

- std::allocator_arg_t -

-
-
- - Move - constructor -
packaged_task( packaged_task && other) noexcept;
-
-
-

-
-
Effects:
-

- Creates a packaged_task by moving the shared - state from other. -

-
Postcondition:
-

- other contains - no valid shared state. -

-
Throws:
-

- Nothing. -

-
-
- - Destructor -
~packaged_task();
-
-
-

-
-
Effects:
-

- Destroys *this - and abandons the shared state - if shared state is ready; otherwise stores future_error - with error condition future_errc::broken_promise - as if by promise::set_exception(): the - shared state is set ready. -

-
-
-

-

-
- - - Member - function operator=() -
-

-

-
packaged_task & operator=( packaged_task && other) noexcept;
-
-
-

-
-
Effects:
-

- Transfers the ownership of shared - state to *this. -

-
Postcondition:
-

- other contains - no valid shared state. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function swap() -
-

-

-
void swap( packaged_task & other) noexcept;
-
-
-

-
-
Effects:
-

- Swaps the shared state - between other and *this. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function valid() -
-

-

-
bool valid() const noexcept;
-
-
-

-
-
Effects:
-

- Returns true if - *this - contains a shared state. -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function get_future() -
-

-

-
future< R > get_future();
-
-
-

-
-
Returns:
-

- A future<> with the same shared - state. -

-
Throws:
-

- future_error - with future_errc::future_already_retrieved - or future_errc::no_state. -

-
-
-

-

-
- - - Member - function operator()() -
-

-

-
void operator()( Args && ... args);
-
-
-

-
-
Effects:
-

- Invokes the stored callable target. Any exception thrown by - the callable target fn - is stored in the shared state - as if by promise::set_exception(). Otherwise, - the value returned by fn - is stored in the shared state as if by promise::set_value(). -

-
Throws:
-

- future_error - with future_errc::no_state. -

-
-
-

-

-
- - - Member - function reset() -
-

-

-
void reset();
-
-
-

-
-
Effects:
-

- Resets the shared state - and abandons the result of previous executions. A new shared - state is constructed. -

-
Throws:
-

- future_error - with future_errc::no_state. -

-
-
-

-

-
- - - Non-member - function swap() -
-

-

-
template< typename Signature >
-void swap( packaged_task< Signature > & l, packaged_task< Signature > & r) noexcept;
-
-
-

-
-
Effects:
-

- Same as l.swap( - r). -

-
-
-
-
-
-
- -
- - Synopsis -
-

- Fiber local storage allows a separate instance of a given data item for - each fiber. -

-
- - Cleanup - at fiber exit -
-

- When a fiber exits, the objects associated with each fiber_specific_ptr instance - are destroyed. By default, the object pointed to by a pointer p is destroyed by invoking delete p, - but this can be overridden for a specific instance of fiber_specific_ptr by - providing a cleanup routine func - to the constructor. In this case, the object is destroyed by invoking - func(p). - The cleanup functions are called in an unspecified order. -

-

-

-
- - - Class - fiber_specific_ptr -
-

-

-
#include <boost/fiber/fss.hpp>
-
-template< typename T >
-class fiber_specific_ptr {
-public:
-    typedef T   element_type;
-
-    fiber_specific_ptr();
-
-    explicit fiber_specific_ptr( void(*fn)(T*) );
-
-    ~fiber_specific_ptr();
-
-    fiber_specific_ptr( fiber_specific_ptr const&) = delete;
-    fiber_specific_ptr & operator=( fiber_specific_ptr const&) = delete;
-
-    T * get() const noexcept;
-
-    T * operator->() const noexcept;
-
-    T & operator*() const noexcept;
-
-    T * release();
-
-    void reset( T *);
-};
-
-
- - Constructor -
-
fiber_specific_ptr();
-explicit fiber_specific_ptr( void(*fn)(T*) );
-
-
-

-
-
Requires:
-

- delete this->get() is well-formed; fn(this->get()) - does not throw -

-
Effects:
-

- Construct a fiber_specific_ptr object for - storing a pointer to an object of type T - specific to each fiber. When reset() is called, or the fiber exits, - fiber_specific_ptr calls fn(this->get()). If the no-arguments constructor - is used, the default delete-based - cleanup function will be used to destroy the fiber-local objects. -

-
Throws:
-

- fiber_error if - an error occurs. -

-
-
-
- - Destructor -
-
~fiber_specific_ptr();
-
-
-

-
-
Requires:
-

- All the fiber specific instances associated to this fiber_specific_ptr -(except - maybe the one associated to this fiber) must be nullptr. -

-
Effects:
-

- Calls this->reset() - to clean up the associated value for the current fiber, and destroys - *this. -

-
Remarks:
-

- The requirement is an implementation restriction. If the destructor - promised to delete instances for all fibers, the implementation - would be forced to maintain a list of all the fibers having an - associated specific ptr, which is against the goal of fiber specific - data. In general, a fiber_specific_ptr should - outlive the fibers that use it. -

-
-
-
- - - - - -
[Note]Note

- Care needs to be taken to ensure that any fibers still running after - an instance of fiber_specific_ptr has been destroyed - do not call any member functions on that instance. -

-

-

-
- - - Member - function get() -
-

-

-
T * get() const noexcept;
-
-
-

-
-
Returns:
-

- The pointer associated with the current fiber. -

-
Throws:
-

- Nothing. -

-
-
-
- - - - - -
[Note]Note

- The initial value associated with an instance of fiber_specific_ptr is - nullptr for each fiber. -

-

-

-
- - - Member - function operator->() -
-

-

-
T * operator->() const noexcept;
-
-
-

-
-
Requires:
-

- this->get() - is not nullptr. -

-
Returns:
-

- this->get() -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function operator*() -
-

-

-
T & operator*() const noexcept;
-
-
-

-
-
Requires:
-

- this->get() - is not nullptr. -

-
Returns:
-

- *(this->get()) -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function release() -
-

-

-
T * release();
-
-
-

-
-
Effects:
-

- Return this->get() - and store nullptr - as the pointer associated with the current fiber without invoking - the cleanup function. -

-
Postcondition:
-

- this->get()==nullptr -

-
Throws:
-

- Nothing. -

-
-
-

-

-
- - - Member - function reset() -
-

-

-
void reset( T * new_value);
-
-
-

-
-
Effects:
-

- If this->get()!=new_value and this->get() is not nullptr, - invoke delete this->get() or fn(this->get()) as appropriate. Store new_value as the pointer associated - with the current fiber. -

-
Postcondition:
-

- this->get()==new_value -

-
Throws:
-

- Exception raised during cleanup of previous value. -

-
-
-
-
- -
- - Overview -
-

- Each fiber owns a stack and manages its execution state, including all - registers and CPU flags, the instruction pointer and the stack pointer. - That means, in general, a fiber is not bound to a specific thread.[3] ,[4] -

-

- Migrating a fiber from a logical CPU with heavy workload to another logical - CPU with a lighter workload might speed up the overall execution. Note - that in the case of NUMA-architectures, it is not always advisable to - migrate data between threads. Suppose fiber f is - running on logical CPU cpu0 which belongs to NUMA - node node0. The data of f are - allocated on the physical memory located at node0. - Migrating the fiber from cpu0 to another logical - CPU cpuX which is part of a different NUMA node - nodeX might reduce the performance of the application - due to increased latency of memory access. -

-

- Only fibers that are contained in sched_algorithm's - ready queue can migrate between threads. You cannot migrate a running - fiber, nor one that is blocked. -

-

- InBoost.Fiber a fiber is migrated by - invoking context::migrate() on the context instance - for a fiber already associated with the destination thread, passing the - context for the fiber - to be migrated. -

-
- - Example - of work sharing -
-

- In the example work_sharing.cpp - multiple worker fibers are created on the main thread. Each fiber gets - a character as parameter at construction. This character is printed out - ten times. Between each iteration the fiber calls this_fiber::yield(). - That puts the fiber in the ready queue of the fiber-scheduler shared_ready_queue, - running in the current thread. The next fiber ready to be executed is - dequeued from the shared ready queue and resumed by shared_ready_queue - running on any participating thread. -

-

- All instances of shared_ready_queue share one global - concurrent queue, used as ready queue. This mechanism shares all worker - fibers between all instances of shared_ready_queue, - thus between all participating threads. -

-
- - Setup - of threads and fibers -
-

- In main() - the fiber-scheduler is installed and the worker fibers and the threads - are launched. -

-

-

-
boost::fibers::use_scheduling_algorithm< shared_ready_queue >(); 1
-
-for ( char c : std::string("abcdefghijklmnopqrstuvwxyz")) { 2
-    boost::fibers::fiber([c](){ whatevah( c); }).detach();
-    ++fiber_count; 3
-}
-barrier b( 4);
-std::thread threads[] = { 4
-    std::thread( thread, & b),
-    std::thread( thread, & b),
-    std::thread( thread, & b)
-};
-b.wait(); 5
-{
-    lock_t6 lk( mtx_count);
-    cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); 7
-} 8
-BOOST_ASSERT( 0 == fiber_count);
-for ( std::thread & t : threads) { 9
-    t.join();
-}
-
-

-

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

1

- Install the scheduling algorithm shared_ready_queue - in the main thread too, so each new fiber gets launched into the - shared pool. -

2

- Launch a number of worker fibers; each worker fiber picks up a character - that is passed as parameter to fiber-function whatevah. - Each worker fiber gets detached. -

3

- Increment fiber counter for each new fiber. -

4

- Launch a couple of threads that join the work sharing. -

5

- sync with other threads: allow them to start processing -

6

- lock_t is typedef'ed - as std::unique_lock< std::mutex > -

7

- Suspend main fiber and resume worker fibers in the meanwhile. Main - fiber gets resumed (e.g returns from condition_variable_any::wait()) if all worker fibers are complete. -

8

- Releasing lock of mtx_count is required before joining the threads, - othwerwise the other threads would be blocked inside condition_variable::wait() - and would never return (deadlock). -

9

- wait for threads to terminate -

-

- The start of the threads is synchronized with a barrier. The main fiber - of each thread (including main thread) is suspended until all worker - fibers are complete. When the main fiber returns from condition_variable::wait(), - the thread terminates: the main thread joins all other threads. -

-

-

-
void thread( barrier * b) {
-    std::ostringstream buffer;
-    buffer << "thread started " << std::this_thread::get_id() << std::endl;
-    std::cout << buffer.str() << std::flush;
-    boost::fibers::use_scheduling_algorithm< shared_ready_queue >(); 1
-    b->wait(); 2
-    lock_t lk( mtx_count);
-    cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); 3
-    BOOST_ASSERT( 0 == fiber_count);
-}
-
-

-

-
- - - - - - - - - - - - -

1

- Install the scheduling algorithm shared_ready_queue - in order to join the work sharing. -

2

- sync with other threads: allow them to start processing -

3

- Suspend main fiber and resume worker fibers in the meanwhile. Main - fiber gets resumed (e.g returns from condition_variable_any::wait()) if all worker fibers are complete. -

-

- Each worker fiber executes function whatevah() with character me - as parameter. The fiber yields in a loop and prints out a message if - it was migrated to another thread. -

-

-

-
void whatevah( char me) {
-    try {
-        std::thread::id my_thread = std::this_thread::get_id(); 1
-        {
-            std::ostringstream buffer;
-            buffer << "fiber " << me << " started on thread " << my_thread << '\n';
-            std::cout << buffer.str() << std::flush;
-        }
-        for ( unsigned i = 0; i < 10; ++i) { 2
-            boost::this_fiber::yield(); 3
-            std::thread::id new_thread = std::this_thread::get_id(); 4
-            if ( new_thread != my_thread) { 5
-                my_thread = new_thread;
-                std::ostringstream buffer;
-                buffer << "fiber " << me << " switched to thread " << my_thread << '\n';
-                std::cout << buffer.str() << std::flush;
-            }
-        }
-    } catch ( ... ) {
-    }
-    lock_t lk( mtx_count);
-    if ( 0 == --fiber_count) { 6
-        lk.unlock();
-        cnd_count.notify_all(); 7
-    }
-}
-
-

-

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -

1

- get ID of initial thread -

2

- loop ten times -

3

- yield to other fibers -

4

- get ID of current thread -

5

- test if fiber was migrated to another thread -

6

- Decrement fiber counter for each completed fiber. -

7

- Notify all fibers waiting on cnd_count. -

-
- - Scheduling - fibers -
-

- The fiber scheduler shared_ready_queue - is like round_robin, - except that it shares a common ready queue among all participating threads. - A thread participates in this pool by executing use_scheduling_algorithm() -before - any other Boost.Fiber operation. -

-

- The important point about the ready queue is that it's a class static, - common to all instances of shared_ready_queue. Fibers that are enqueued - via sched_algorithm::awakened() (fibers that are - ready to be resumed) are thus available to all threads. It is required - to reserve a separate, scheduler-specific queue for the thread's main - fiber and dispatcher fibers: these may not be shared - between threads! When we're passed either of these fibers, push it there - instead of in the shared queue: it would be Bad News for thread B to - retrieve and attempt to execute thread A's main fiber. -

-

-

-
virtual void awakened( boost::fibers::context * ctx) noexcept {
-    BOOST_ASSERT( nullptr != ctx);
-
-    if ( ctx->is_context( boost::fibers::type::pinned_context) ) { 1
-        local_queue_.push( ctx);
-    } else {
-        lock_t lk(rqueue_mtx_); 2
-        rqueue_.push( ctx);
-    }
-}
-
-

-

-
- - - - - - - - -

1

- recognize when we're passed this thread's main fiber (or an implicit - library helper fiber): never put those on the shared queue -

2

- worker fiber, enqueue on shared queue -

-

- When sched_algorithm::pick_next() gets called - inside one thread, a fiber is dequeued from rqueue_ - and will be resumed in that thread. -

-

-

-
virtual boost::fibers::context * pick_next() noexcept {
-    boost::fibers::context * ctx( nullptr);
-    lock_t lk(rqueue_mtx_);
-    if ( ! rqueue_.empty() ) { 1
-        ctx = rqueue_.front();
-        rqueue_.pop();
-        lk.unlock();
-        BOOST_ASSERT( nullptr != ctx);
-        boost::fibers::context::active()->migrate( ctx); 2
-    } else {
-        lk.unlock();
-        if ( ! local_queue_.empty() ) { 3
-            ctx = local_queue_.front();
-            local_queue_.pop();
-        }
-    }
-    return ctx;
-}
-
-

-

-
- - - - - - - - - - - - -

1

- pop an item from the ready queue -

2

- attach context to current scheduler via the active fiber of this - thread; benign if the fiber already belongs to this thread -

3

- nothing in the ready queue, return main or dispatcher fiber -

-

- The source code above is found in work_sharing.cpp. -

-
-
- -
- - Overview -
-

- One of the primary benefits of Boost.Fiber - is the ability to use asynchronous operations for efficiency, while at - the same time structuring the calling code as if - the operations were synchronous. Asynchronous operations provide completion - notification in a variety of ways, but most involve a callback function - of some kind. This section discusses tactics for interfacing Boost.Fiber with an arbitrary async operation. -

-

- For purposes of illustration, consider the following hypothetical API: -

-

-

-
class AsyncAPI {
-public:
-    // constructor acquires some resource that can be read and written
-    AsyncAPI();
-
-    // callbacks accept an int error code; 0 == success
-    typedef int errorcode;
-
-    // write callback only needs to indicate success or failure
-    void init_write( std::string const& data,
-                     std::function< void( errorcode) > const& callback);
-
-    // read callback needs to accept both errorcode and data
-    void init_read( std::function< void( errorcode, std::string const&) > const&);
-
-    // ... other operations ...
-};
-
-

-

-

- The significant points about each of init_write() and init_read() are: -

-
    -
  • - The AsyncAPI method - only initiates the operation. It returns immediately, while the requested - operation is still pending. -
  • -
  • - The method accepts a callback. When the operation completes, the - callback is called with relevant parameters (error code, data if - applicable). -
  • -
-

- We would like to wrap these asynchronous methods in functions that appear - synchronous by blocking the calling fiber until the operation completes. - This lets us use the wrapper function's return value to deliver relevant - data. -

-
- - - - - -
[Tip]Tip

- promise<> and future<> are your - friends here. -

-
- - Return - Errorcode -
-

- The AsyncAPI::init_write() - callback passes only an errorcode. - If we simply want the blocking wrapper to return that errorcode, - this is an extremely straightforward use of promise<> and - future<>: -

-

-

-
AsyncAPI::errorcode write_ec( AsyncAPI & api, std::string const& data) {
-    boost::fibers::promise< AsyncAPI::errorcode > promise;
-    boost::fibers::future< AsyncAPI::errorcode > future( promise.get_future() );
-    // In general, even though we block waiting for future::get() and therefore
-    // won't destroy 'promise' until promise::set_value() has been called, we
-    // are advised that with threads it's possible for ~promise() to be
-    // entered before promise::set_value() has returned. While that shouldn't
-    // happen with fibers::promise, a robust way to deal with the lifespan
-    // issue is to bind 'promise' into our lambda. Since promise is move-only,
-    // use initialization capture.
-    api.init_write(
-        data,
-        [&promise]( AsyncAPI::errorcode ec) mutable {
-                            promise.set_value( ec);
-                  });
-    return future.get();
-}
-
-

-

-

- All we have to do is: -

-
    -
  1. - Instantiate a promise<> of correct type. -
  2. -
  3. - Obtain its future<>. -
  4. -
  5. - Arrange for the callback to call promise::set_value(). -
  6. -
  7. - Block on future::get(). -
  8. -
-
- - - - - -
[Note]Note

- This tactic for resuming a pending fiber works even if the callback - is called on a different thread than the one on which the initiating - fiber is running. In fact, the - example program's dummy AsyncAPI - implementation illustrates that: it simulates async I/O by launching - a new thread that sleeps briefly and then calls the relevant callback. -

-
- - Success - or Exception -
-

- A wrapper more aligned with modern C++ practice would use an exception, - rather than an errorcode, - to communicate failure to its caller. This is straightforward to code - in terms of write_ec(): -

-

-

-
void write( AsyncAPI & api, std::string const& data) {
-    AsyncAPI::errorcode ec = write_ec( api, data);
-    if ( ec) {
-        throw make_exception("write", ec);
-    }
-}
-
-

-

-

- The point is that since each fiber has its own stack, you need not repeat - messy boilerplate: normal encapsulation works. -

-
- - Return - Errorcode or Data -
-

- Things get a bit more interesting when the async operation's callback - passes multiple data items of interest. One approach would be to use - std::pair<> - to capture both: -

-

-

-
std::pair< AsyncAPI::errorcode, std::string > read_ec( AsyncAPI & api) {
-    typedef std::pair< AsyncAPI::errorcode, std::string > result_pair;
-    boost::fibers::promise< result_pair > promise;
-    boost::fibers::future< result_pair > future( promise.get_future() );
-    // We promise that both 'promise' and 'future' will survive until our
-    // lambda has been called.
-    api.init_read([&promise]( AsyncAPI::errorcode ec, std::string const& data) mutable {
-                            promise.set_value( result_pair( ec, data) );
-                  });
-    return future.get();
-}
-
-

-

-

- Once you bundle the interesting data in std::pair<>, the code is effectively identical - to write_ec(). - You can call it like this: -

-

-

-
std::tie( ec, data) = read_ec( api);
-
-

-

-
- - Data - or Exception -
-

- But a more natural API for a function that obtains data is to return - only the data on success, throwing an exception on error. -

-

- As with write() - above, it's certainly possible to code a read() wrapper in terms of read_ec(). But since a given application is unlikely - to need both, let's code read() from scratch, leveraging promise::set_exception(): -

-

-

-
std::string read( AsyncAPI & api) {
-    boost::fibers::promise< std::string > promise;
-    boost::fibers::future< std::string > future( promise.get_future() );
-    // Both 'promise' and 'future' will survive until our lambda has been
-    // called.
-    api.init_read([&promise]( AsyncAPI::errorcode ec, std::string const& data) mutable {
-                           if ( ! ec) {
-                               promise.set_value( data);
-                           } else {
-                               promise.set_exception(
-                                       std::make_exception_ptr(
-                                           make_exception("read", ec) ) );
-                           }
-                  });
-    return future.get();
-}
-
-

-

-

- future::get() will do the right thing, either returning std::string or throwing an exception. -

-
- - Success/Error - Virtual Methods -
-

- One classic approach to completion notification is to define an abstract - base class with success() and error() methods. Code wishing to perform async - I/O must derive a subclass, override each of these methods and pass the - async operation a pointer to a subclass instance. The abstract base class - might look like this: -

-

-

-
// every async operation receives a subclass instance of this abstract base
-// class through which to communicate its result
-struct Response {
-    typedef std::shared_ptr< Response > ptr;
-
-    // called if the operation succeeds
-    virtual void success( std::string const& data) = 0;
-
-    // called if the operation fails
-    virtual void error( AsyncAPIBase::errorcode ec) = 0;
-};
-
-

-

-

- Now the AsyncAPI operation - might look more like this: -

-

-

-
// derive Response subclass, instantiate, pass Response::ptr
-void init_read( Response::ptr);
-
-

-

-

- We can address this by writing a one-size-fits-all PromiseResponse: -

-

-

-
class PromiseResponse: public Response {
-public:
-    // called if the operation succeeds
-    virtual void success( std::string const& data) {
-        promise_.set_value( data);
-    }
-
-    // called if the operation fails
-    virtual void error( AsyncAPIBase::errorcode ec) {
-        promise_.set_exception(
-                std::make_exception_ptr(
-                    make_exception("read", ec) ) );
-    }
-
-    boost::fibers::future< std::string > get_future() {
-        return promise_.get_future();
-    }
-
-private:
-    boost::fibers::promise< std::string >   promise_;
-};
-
-

-

-

- Now we can simply obtain the future<> from that PromiseResponse - and wait on its get(): -

-

-

-
std::string read( AsyncAPI & api) {
-    // Because init_read() requires a shared_ptr, we must allocate our
-    // ResponsePromise on the heap, even though we know its lifespan.
-    auto promisep( std::make_shared< PromiseResponse >() );
-    boost::fibers::future< std::string > future( promisep->get_future() );
-    // Both 'promisep' and 'future' will survive until our lambda has been
-    // called.
-    api.init_read( promisep);
-    return future.get();
-}
-
-

-

-

- The source code above is found in adapt_callbacks.cpp - and adapt_method_calls.cpp. -

-
- - Then - There's Boost.Asio -
-

- Since the simplest form of Boost.Asio asynchronous operation completion - token is a callback function, we could apply the same tactics for Asio - as for our hypothetical AsyncAPI - asynchronous operations. -

-

- Fortunately we need not. Boost.Asio incorporates a mechanism[5] by which the caller can customize the notification behavior - of every async operation. Therefore we can construct a completion - token which, when passed to a Boost.Asio - async operation, requests blocking for the calling fiber. -

-

- A typical Asio async function might look something like this:[6] -

-
template < ..., class CompletionToken >
-deduced_return_type
-async_something( ... , CompletionToken&& token)
-{
-    // construct handler_type instance from CompletionToken
-    handler_type<CompletionToken, ...>::type handler(token);
-    // construct async_result instance from handler_type
-    async_result<decltype(handler)> result(handler);
-
-    // ... arrange to call handler on completion ...
-    // ... initiate actual I/O operation ...
-
-    return result.get();
-}
-
-

- We will engage that mechanism, which is based on specializing Asio's - handler_type<> - template for the CompletionToken - type and the signature of the specific callback. The remainder of this - discussion will refer back to async_something() as the Asio async function under consideration. -

-

- The implementation described below uses lower-level facilities than - promise and future for two reasons: -

-
    -
  1. - The promise mechanism - interacts badly with io_service::stop(). - It produces broken_promise - exceptions. -
  2. -
  3. - If more than one thread is calling the io_service::run() - method, the implementation described below allows resuming the suspended - fiber on whichever thread gets there first with completion notification. - More on this later. -
  4. -
-

- boost::fibers::asio::yield is a completion token of this - kind. yield is an instance - of yield_t: -

-

-

-
class yield_t {
-public:
-    yield_t( bool hop) :
-        allow_hop_( hop) {
-    }
-
-    /**
-     * @code
-     * static yield_t yield;
-     * boost::system::error_code myec;
-     * func(yield[myec]);
-     * @endcode
-     * @c yield[myec] returns an instance of @c yield_t whose @c ec_ points
-     * to @c myec. The expression @c yield[myec] "binds" @c myec to that
-     * (anonymous) @c yield_t instance, instructing @c func() to store any
-     * @c error_code it might produce into @c myec rather than throwing @c
-     * boost::system::system_error.
-     */
-    yield_t operator[]( boost::system::error_code & ec) const {
-        yield_t tmp{ * this };
-        tmp.ec_ = & ec;
-        return tmp;
-    }
-
-//private:
-    // ptr to bound error_code instance if any
-    boost::system::error_code   *   ec_{ nullptr };
-    // allow calling fiber to "hop" to another thread if it could resume more
-    // quickly that way
-    bool                            allow_hop_;
-};
-
-

-

-

- yield_t is in fact only - a placeholder, a way to trigger Boost.Asio customization. It can bind - a boost::system::error_code for use by the actual - handler. -

-

- In fact there are two canonical instances of yield_t - — yield and yield_hop: -

-

-

-
// canonical instance with allow_hop_ == false
-thread_local yield_t yield{ false };
-// canonical instance with allow_hop_ == true
-thread_local yield_t yield_hop{ true };
-
-

-

-

- We'll get to the differences between these shortly. -

-

- Asio customization is engaged by specializing boost::asio::handler_type<> - for yield_t: -

-

-

-
// Handler type specialisation for fibers::asio::yield.
-// When 'yield' is passed as a completion handler which accepts only
-// error_code, use yield_handler<void>. yield_handler will take care of the
-// error_code one way or another.
-template< typename ReturnType >
-struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code) >
-{ typedef fibers::asio::detail::yield_handler< void >    type; };
-
-

-

-

- (There are actually four different specializations in detail/yield.hpp, - one for each of the four Asio async callback signatures we expect to - have to support.) -

-

- The above directs Asio to use yield_handler - as the actual handler for an async operation to which yield - is passed. There's a generic yield_handler<T> implementation and a yield_handler<void> - specialization. Let's start with the <void> - specialization: -

-

-

-
// yield_handler<void> is like yield_handler<T> without value_. In fact it's
-// just like yield_handler_base.
-template<>
-class yield_handler< void >: public yield_handler_base {
-public:
-    explicit yield_handler( yield_t const& y) :
-        yield_handler_base{ y } {
-    }
-
-    // nullary completion callback
-    void operator()() {
-        ( * this)( boost::system::error_code() );
-    }
-
-    // inherit operator()(error_code) overload from base class
-    using yield_handler_base::operator();
-};
-
-

-

-

- async_something(), - having consulted the handler_type<> traits specialization, instantiates - a yield_handler<void> - to be passed as the actual callback for the async operation. yield_handler's constructor accepts - the yield_t instance - (the yield object passed - to the async function) and passes it along to yield_handler_base: -

-

-

-
// This class encapsulates common elements between yield_handler<T> (capturing
-// a value to return from asio async function) and yield_handler<void> (no
-// such value). See yield_handler<T> and its <void> specialization below. Both
-// yield_handler<T> and yield_handler<void> are passed by value through
-// various layers of asio functions. In other words, they're potentially
-// copied multiple times. So key data such as the yield_completion instance
-// must be stored in our async_result<yield_handler<>> specialization, which
-// should be instantiated only once.
-class yield_handler_base {
-public:
-    yield_handler_base( yield_t const& y) :
-        // capture the context* associated with the running fiber
-        ctx_{ boost::fibers::context::active() },
-        // capture the passed yield_t
-        yt_{ y } {
-    }
-
-    // completion callback passing only (error_code)
-    void operator()( boost::system::error_code const& ec) {
-        BOOST_ASSERT_MSG( ycomp_,
-                          "Must inject yield_completion* "
-                          "before calling yield_handler_base::operator()()");
-        BOOST_ASSERT_MSG( yt_.ec_,
-                          "Must inject boost::system::error_code* "
-                          "before calling yield_handler_base::operator()()");
-        // If originating fiber is busy testing completed_ flag, wait until it
-        // has observed (! completed_).
-        yield_completion::lock_t lk{ ycomp_->mtx_ };
-        // Notify a subsequent yield_completion::wait() call that it need not
-        // suspend.
-        ycomp_->completed_ = true;
-        // set the error_code bound by yield_t
-        * yt_.ec_ = ec;
-        // Are we permitted to wake up the suspended fiber on this thread, the
-        // thread that called the completion handler?
-        if ( ( ! ctx_->is_context( fibers::type::pinned_context) ) && yt_.allow_hop_) {
-            // We must not migrate a pinned_context to another thread. If this
-            // isn't a pinned_context, and the application passed yield_hop
-            // rather than yield, migrate this fiber to the running thread.
-            fibers::context::active()->migrate( ctx_);
-        }
-        // either way, wake the fiber
-        fibers::context::active()->set_ready( ctx_);
-    }
-
-//private:
-    boost::fibers::context      *   ctx_;
-    yield_t                         yt_;
-    // We depend on this pointer to yield_completion, which will be injected
-    // by async_result.
-    yield_completion            *   ycomp_{ nullptr };
-};
-
-

-

-

- yield_handler_base stores - a copy of the yield_t - instance — which, as shown above, is only an error_code - and a bool. It also captures - the context* for the currently-running fiber by calling - context::active(). -

-

- You will notice that yield_handler_base - has one more data member (ycomp_) - that is initialized to nullptr - by its constructor — though its operator()() method relies on ycomp_ - being non-null. More on this in a moment. -

-

- Having constructed the yield_handler<void> instance, async_something() goes on to construct an async_result specialized for the handler_type<>::type: in this case, async_result<yield_handler<void>>. It passes the yield_handler<void> - instance to the new async_result - instance. -

-

-

-
// Without the need to handle a passed value, our yield_handler<void>
-// specialization is just like async_result_base.
-template<>
-class async_result< boost::fibers::asio::detail::yield_handler< void > > :
-    public boost::fibers::asio::detail::async_result_base {
-public:
-    typedef void type;
-
-    explicit async_result( boost::fibers::asio::detail::yield_handler< void > & h):
-        boost::fibers::asio::detail::async_result_base{ h } {
-    }
-};
-
-

-

-

- Naturally that leads us straight to async_result_base: -

-

-

-
// Factor out commonality between async_result<yield_handler<T>> and
-// async_result<yield_handler<void>>
-class async_result_base {
-public:
-    explicit async_result_base( yield_handler_base & h) {
-        // Inject ptr to our yield_completion instance into this
-        // yield_handler<>.
-        h.ycomp_ = & this->ycomp_;
-        // if yield_t didn't bind an error_code, make yield_handler_base's
-        // error_code* point to an error_code local to this object so
-        // yield_handler_base::operator() can unconditionally store through
-        // its error_code*
-        if ( ! h.yt_.ec_) {
-            h.yt_.ec_ = & ec_;
-        }
-    }
-
-    void get() {
-        // Unless yield_handler_base::operator() has already been called,
-        // suspend the calling fiber until that call.
-        ycomp_.wait();
-        // The only way our own ec_ member could have a non-default value is
-        // if our yield_handler did not have a bound error_code AND the
-        // completion callback passed a non-default error_code.
-        if ( ec_) {
-            throw_exception( boost::system::system_error{ ec_ } );
-        }
-        boost::this_fiber::interruption_point();
-    }
-
-private:
-    // If yield_t does not bind an error_code instance, store into here.
-    boost::system::error_code       ec_{};
-    // async_result_base owns the yield_completion because, unlike
-    // yield_handler<>, async_result<> is only instantiated once.
-    yield_completion                ycomp_{};
-};
-
-

-

-

- This is how yield_handler_base::ycomp_ - becomes non-null: async_result_base's - constructor injects a pointer back to its own yield_completion - member. -

-

- Recall that both of the canonical yield_t - instances yield and - yield_hop initialize - their error_code* - member ec_ to nullptr. If either of these instances - is passed to async_something() (ec_ - is still nullptr), the copy - stored in yield_handler_base - will likewise have null ec_. - async_result_base's constructor - sets yield_handler_base's - yield_t's ec_ member to point to its own error_code member. -

-

- The stage is now set. async_something() initiates the actual async operation, - arranging to call its yield_handler<void> instance on completion. Let's say, - for the sake of argument, that the actual async operation's callback - has signature void(error_code). -

-

- But since it's an async operation, control returns at once to async_something(). - async_something() - calls async_result<yield_handler<void>>::get(), - and will return its return value. -

-

- async_result<yield_handler<void>>::get() - inherits async_result_base::get(). -

-

- async_result_base::get() - immediately calls yield_completion::wait(). -

-

-

-
// Bundle a completion bool flag with a spinlock to protect it.
-struct yield_completion {
-    typedef fibers::detail::spinlock    mutex_t;
-    typedef std::unique_lock< mutex_t > lock_t;
-
-    mutex_t mtx_{};
-    bool    completed_{ false };
-
-    void wait() {
-        // yield_handler_base::operator()() will set completed_ true and
-        // attempt to wake a suspended fiber. It would be Bad if that call
-        // happened between our detecting (! completed_) and suspending.
-        lock_t lk{ mtx_ };
-        // If completed_ is already set, we're done here: don't suspend.
-        if ( ! completed_) {
-            // suspend(unique_lock<spinlock>) unlocks the lock in the act of
-            // resuming another fiber
-            fibers::context::active()->suspend( lk);
-        }
-    }
-};
-
-

-

-

- Supposing that the pending async operation has not yet completed, yield_completion::completed_ will still be false, and wait() will call context::suspend() on - the currently-running fiber. -

-

- Other fibers will now have a chance to run. -

-

- Some time later, the async operation completes. It calls yield_handler<void>::operator()(error_code const&) with an error_code - indicating either success or failure. We'll consider both cases. -

-

- yield_handler<void> - explicitly inherits operator()(error_code - const&) - from yield_handler_base. -

-

- yield_handler_base::operator()(error_code const&) first sets yield_completion::completed_ - true. This way, if async_something()'s - async operation completes immediately — if yield_handler_base::operator() is called even before async_result_base::get() - — the calling fiber will not suspend. -

-

- The actual error_code - produced by the async operation is then stored through the stored yield_t::ec_ pointer. If async_something()'s caller used (e.g.) yield[my_ec] - to bind a local error_code - instance, the actual error_code - value is stored into the caller's variable. Otherwise, it is stored into - async_result_base::ec_. -

-

- Finally we get to the distinction between yield - and yield_hop. -

-

- As described for context::is_context(), a pinned_context fiber is special to - the library and must never be passed to context::migrate(). - We must detect and avoid that case here. -

-

- The yield_t::allow_hop_ bool - indicates whether async_something()'s caller is willing to allow the running - fiber to hop to another thread (yield_hop) - or whether s/he insists that the fiber resume on the same thread (yield). -

-

- If the caller passed yield_hop - to async_something(), - and the running fiber isn't a pinned_context, - yield_handler_base::operator() - passes the context of - the original fiber — the one on which async_something() was called, captured in yield_handler_base's constructor — to - the current thread's context::migrate(). -

-

- If the running application has more than one thread calling io_service::run(), - that fiber could return from async_something() on a different thread (the one calling - yield_handler_base::operator()) - than the one on which it entered async_something(). -

-

- In any case, the fiber is marked as ready to run by passing it to context::set_ready(). - Control then returns from yield_handler_base::operator(): the callback is done. -

-

- In due course, the fiber yield_handler_base::ctx_ - is resumed. Control returns from context::suspend() to - yield_completion::wait(), - which returns to async_result_base::get(). -

-
    -
  • - If the original caller passed yield[my_ec] to async_something() to bind a local error_code - instance, then yield_handler_base::operator() stored its error_code - to the caller's my_ec - instance, leaving async_result_base::ec_ - initialized to success. -
  • -
  • - If the original caller passed yield - to async_something() without binding a local error_code variable, then yield_handler_base::operator() - stored its error_code - into async_result_base::ec_. - If in fact that error_code - is success, then all is well. -
  • -
  • - Otherwise — the original caller did not bind a local error_code and yield_handler_base::operator() was called with an error_code indicating error — async_result_base::get() - throws system_error - with that error_code. -
  • -
-

- The case in which async_something()'s completion callback has signature - void() - is similar. yield_handler<void>::operator()() invokes the machinery above with - a success error_code. -

-

- A completion callback with signature void(error_code, T) (that is: in addition to error_code, callback receives some - data item) is handled somewhat differently. For this kind of signature, - handler_type<>::type specifies yield_handler<T> (for T - other than void). -

-

- A yield_handler<T> - reserves a value_ pointer - to a value of type T: -

-

-

-
// asio uses handler_type<completion token type, signature>::type to decide
-// what to instantiate as the actual handler. Below, we specialize
-// handler_type< yield_t, ... > to indicate yield_handler<>. So when you pass
-// an instance of yield_t as an asio completion token, asio selects
-// yield_handler<> as the actual handler class.
-template< typename T >
-class yield_handler: public yield_handler_base {
-public:
-    // asio passes the completion token to the handler constructor
-    explicit yield_handler( yield_t const& y) :
-        yield_handler_base{ y } {
-    }
-
-    // completion callback passing only value (T)
-    void operator()( T t) {
-        // just like callback passing success error_code
-        (*this)( boost::system::error_code(), std::move(t) );
-    }
-
-    // completion callback passing (error_code, T)
-    void operator()( boost::system::error_code const& ec, T t) {
-        BOOST_ASSERT_MSG( value_,
-                          "Must inject value ptr "
-                          "before caling yield_handler<T>::operator()()");
-        // move the value to async_result<> instance BEFORE waking up a
-        // suspended fiber
-        * value_ = std::move( t);
-        // forward the call to base-class completion handler
-        yield_handler_base::operator()( ec);
-    }
-
-//private:
-    // pointer to destination for eventual value
-    // this must be injected by async_result before operator()() is called
-    T                           *   value_{ nullptr };
-};
-
-

-

-

- This pointer is initialized to nullptr. -

-

- When async_something() instantiates async_result<yield_handler<T>>: -

-

-

-
// asio constructs an async_result<> instance from the yield_handler specified
-// by handler_type<>::type. A particular asio async method constructs the
-// yield_handler, constructs this async_result specialization from it, then
-// returns the result of calling its get() method.
-template< typename T >
-class async_result< boost::fibers::asio::detail::yield_handler< T > > :
-    public boost::fibers::asio::detail::async_result_base {
-public:
-    // type returned by get()
-    typedef T type;
-
-    explicit async_result( boost::fibers::asio::detail::yield_handler< T > & h) :
-        boost::fibers::asio::detail::async_result_base{ h } {
-        // Inject ptr to our value_ member into yield_handler<>: result will
-        // be stored here.
-        h.value_ = & value_;
-    }
-
-    // asio async method returns result of calling get()
-    type get() {
-        boost::fibers::asio::detail::async_result_base::get();
-        return std::move( value_);
-    }
-
-private:
-    type                            value_{};
-};
-
-

-

-

- this async_result<> - specialization reserves a member of type T - to receive the passed data item, and sets yield_handler<T>::value_ - to point to its own data member. -

-

- async_result<yield_handler<T>> - overrides get(). - The override calls async_result_base::get(), so the calling fiber suspends as described - above. -

-

- yield_handler<T>::operator()(error_code, - T) - stores its passed T value - into async_result<yield_handler<T>>::value_. -

-

- Then it passes control to yield_handler_base::operator()(error_code) to deal with waking (and possibly migrating) - the original fiber as described above. -

-

- When async_result<yield_handler<T>>::get() - resumes, it returns the stored value_ - to async_something() - and ultimately to async_something()'s caller. -

-

- The case of a callback signature void(T) is handled by having yield_handler<T>::operator()(T) engage the void(error_code, T) machinery, passing a success - error_code. -

-

- The source code above is found in yield.hpp - and detail/yield.hpp. -

-
-
- -
- - Overview -
-

- Nonblocking I/O is distinct from asynchronous - I/O. A true async I/O operation promises to initiate the operation and - notify the caller on completion, usually via some sort of callback (as - described in Integrating Fibers with Asynchronous - Callbacks). -

-

- In contrast, a nonblocking I/O operation refuses to start at all if it - would be necessary to block, returning an error code such as EWOULDBLOCK. The operation - is performed only when it can complete immediately. In effect, the caller - must repeatedly retry the operation until it stops returning EWOULDBLOCK. -

-

- In a classic event-driven program, it can be something of a headache - to use nonblocking I/O. At the point where the nonblocking I/O is attempted, - a return value of EWOULDBLOCK - requires the caller to pass control back to the main event loop, arranging - to retry again on the next iteration. -

-

- Worse, a nonblocking I/O operation might partially - succeed. That means that the relevant business logic must continue receiving - control on every main loop iteration until all required data have been - processed: a doubly-nested loop, implemented as a callback-driven state - machine. -

-

- Boost.Fiber can simplify this problem - immensely. Once you have integrated with the application's main loop - as described in Sharing a Thread with Another - Main Loop, waiting for the next main-loop iteration is as simple - as calling this_fiber::yield(). -

-
- - Example - Nonblocking API -
-

- For purposes of illustration, consider this API: -

-

-

-
class NonblockingAPI {
-public:
-    NonblockingAPI();
-
-    // nonblocking operation: may return EWOULDBLOCK
-    int read( std::string & data, std::size_t desired);
-
-    ...
-};
-
-

-

-
- - Polling - for Completion -
-

- We can build a low-level wrapper around NonblockingAPI::read() that shields its caller from ever having - to deal with EWOULDBLOCK: -

-

-

-
// guaranteed not to return EWOULDBLOCK
-int read_chunk( NonblockingAPI & api, std::string & data, std::size_t desired) {
-    int error;
-    while ( EWOULDBLOCK == ( error = api.read( data, desired) ) ) {
-        // not ready yet -- try again on the next iteration of the
-        // application's main loop
-        boost::this_fiber::yield();
-    }
-    return error;
-}
-
-

-

-
- - Filling - All Desired Data -
-

- Given read_chunk(), - we can straightforwardly iterate until we have all desired data: -

-

-

-
// keep reading until desired length, EOF or error
-// may return both partial data and nonzero error
-int read_desired( NonblockingAPI & api, std::string & data, std::size_t desired) {
-    // we're going to accumulate results into 'data'
-    data.clear();
-    std::string chunk;
-    int error = 0;
-    while ( data.length() < desired &&
-           ( error = read_chunk( api, chunk, desired - data.length() ) ) == 0) {
-        data.append( chunk);
-    }
-    return error;
-}
-
-

-

-

- (Of course there are more efficient ways to accumulate - string data. That's not the point of this example.) -

-
- - Wrapping - it Up -
-

- Finally, we can define a relevant exception: -

-

-

-
// exception class augmented with both partially-read data and errorcode
-class IncompleteRead : public std::runtime_error {
-public:
-    IncompleteRead( std::string const& what, std::string const& partial, int ec) :
-        std::runtime_error( what),
-        partial_( partial),
-        ec_( ec) {
-    }
-
-    std::string get_partial() const {
-        return partial_;
-    }
-
-    int get_errorcode() const {
-        return ec_;
-    }
-
-private:
-    std::string partial_;
-    int         ec_;
-};
-
-

-

-

- and write a simple read() function that either returns all desired - data or throws IncompleteRead: -

-

-

-
// read all desired data or throw IncompleteRead
-std::string read( NonblockingAPI & api, std::size_t desired) {
-    std::string data;
-    int ec( read_desired( api, data, desired) );
-
-    // for present purposes, EOF isn't a failure
-    if ( 0 == ec || EOF == ec) {
-        return data;
-    }
-
-    // oh oh, partial read
-    std::ostringstream msg;
-    msg << "NonblockingAPI::read() error " << ec << " after "
-        << data.length() << " of " << desired << " characters";
-    throw IncompleteRead( msg.str(), data, ec);
-}
-
-

-

-

- Once we can transparently wait for the next main-loop iteration using - this_fiber::yield(), ordinary encapsulation Just Works. -

-

- The source code above is found in adapt_nonblocking.cpp. -

-
-
- - -
- - Overview -
-

- A bit of wisdom from the early days of computing still holds true today: - prefer to model program state using the instruction pointer rather than - with Boolean flags. In other words, if the program must do something - and then do something almost the same, but with minor changes... perhaps - parts of that something should be broken out as smaller separate functions, - rather than introducing flags to alter the internal behavior of a monolithic - function. -

-

- To that we would add: prefer to describe control flow using C++ native - constructs such as function calls, if, - while, for, - do et al. rather than as - chains of callbacks. -

-

- One of the great strengths of Boost.Fiber - is the flexibility it confers on the coder to restructure an application - from chains of callbacks to straightforward C++ statement sequence, even - when code in that fiber is in fact interleaved with code running in other - fibers. -

-

- There has been much recent discussion about the benefits of when_any - and when_all functionality. When dealing with asynchronous and possibly - unreliable services, these are valuable idioms. But of course when_any - and when_all are closely tied to the use of chains of callbacks. -

-

- This section presents recipes for achieving the same ends, in the context - of a fiber that wants to do something when one or more - other independent activities have completed. Accordingly, these are - wait_something() - functions rather than when_something() functions. The expectation is that - the calling fiber asks to launch those independent activities, then waits - for them, then sequentially proceeds with whatever processing depends - on those results. -

-

- The function names shown (e.g. wait_first_simple()) - are for illustrative purposes only, because all these functions have - been bundled into a single source file. Presumably, if (say) wait_first_success() - best suits your application needs, you could introduce that variant with - the name wait_any(). -

-
- - - - - -
[Note]Note

- The functions presented in this section accept variadic argument lists - of task functions. Corresponding wait_something() functions accepting a container of - task functions are left as an exercise for the interested reader. Those - should actually be simpler. Most of the complexity would arise from - overloading the same name for both purposes. -

-

- All the source code for this section is found in wait_stuff.cpp. -

-
- - Example - Task Function -
-

- We found it convenient to model an asynchronous - task using this function: -

-

-

-
template< typename T >
-T sleeper_impl( T item, int ms, bool thrw = false) {
-    std::ostringstream descb, funcb;
-    descb << item;
-    std::string desc( descb.str() );
-    funcb << "  sleeper(" << item << ")";
-    Verbose v( funcb.str() );
-
-    boost::this_fiber::sleep_for( std::chrono::milliseconds( ms) );
-    if ( thrw) {
-        throw std::runtime_error( desc);
-    }
-    return item;
-}
-
-

-

-

- with type-specific sleeper() front ends for std::string, double - and int. -

-

- Verbose simply prints - a message to std::cout on construction and destruction. -

-

- Basically: -

-
    -
  1. - sleeper() - prints a start message; -
  2. -
  3. - sleeps for the specified number of milliseconds; -
  4. -
  5. - if thrw is passed - as true, throws a string - description of the passed item; -
  6. -
  7. - else returns the passed item. -
  8. -
  9. - On the way out, sleeper() produces a stop message. -
  10. -
-

- This function will feature in the example calls to the various functions - presented below. -

-
- - -
- -

- The simplest case is when you only need to know that the first of - a set of asynchronous tasks has completed — but you don't need to obtain - a return value, and you're confident that they will not throw exceptions. -

-

- For this we introduce a Done - class to wrap a bool - variable with a condition_variable and a mutex: -

-

-

-
// Wrap canonical pattern for condition_variable + bool flag
-struct Done {
-private:
-    boost::fibers::condition_variable   cond;
-    boost::fibers::mutex                mutex;
-    bool                                ready = false;
-
-public:
-    typedef std::shared_ptr< Done >     ptr;
-
-    void wait() {
-        std::unique_lock< boost::fibers::mutex > lock( mutex);
-        cond.wait( lock, [this](){ return ready; });
-    }
-
-    void notify() {
-        {
-            std::unique_lock< boost::fibers::mutex > lock( mutex);
-            ready = true;
-        } // release mutex
-        cond.notify_one();
-    }
-};
-
-

-

-

- The pattern we follow throughout this section is to pass a std::shared_ptr<> - to the relevant synchronization object to the various tasks' fiber - functions. This eliminates nagging questions about the lifespan of - the synchronization object relative to the last of the fibers. -

-

- wait_first_simple() uses that tactic for Done: -

-

-

-
template< typename ... Fns >
-void wait_first_simple( Fns && ... functions) {
-    // Use shared_ptr because each function's fiber will bind it separately,
-    // and we're going to return before the last of them completes.
-    auto done( std::make_shared< Done >() );
-    wait_first_simple_impl( done, std::forward< Fns >( functions) ... );
-    done->wait();
-}
-
-

-

-

- wait_first_simple_impl() is an ordinary recursion over the - argument pack, capturing Done::ptr - for each new fiber: -

-

-

-
// Degenerate case: when there are no functions to wait for, return
-// immediately.
-void wait_first_simple_impl( Done::ptr) {
-}
-
-// When there's at least one function to wait for, launch it and recur to
-// process the rest.
-template< typename Fn, typename ... Fns >
-void wait_first_simple_impl( Done::ptr done, Fn && function, Fns && ... functions) {
-    boost::fibers::fiber( [done, function](){
-                              function();
-                              done->notify();
-                          }).detach();
-    wait_first_simple_impl( done, std::forward< Fns >( functions) ... );
-}
-
-

-

-

- The body of the fiber's lambda is extremely simple, as promised: - call the function, notify Done when it returns. The - first fiber to do so allows wait_first_simple() to return — which is why it's useful - to have std::shared_ptr<Done> - manage the lifespan of our Done - object rather than declaring it as a stack variable in wait_first_simple(). -

-

- This is how you might call it: -

-

-

-
wait_first_simple(
-        [](){ sleeper("wfs_long",   150); },
-        [](){ sleeper("wfs_medium", 100); },
-        [](){ sleeper("wfs_short",   50); });
-
-

-

-

- In this example, control resumes after wait_first_simple() when sleeper("wfs_short", - 50) - completes — even though the other two sleeper() fibers are still running. -

-
-
- -

- It seems more useful to add the ability to capture the return value - from the first of the task functions to complete. Again, we assume - that none will throw an exception. -

-

- One tactic would be to adapt our Done class to store the - first of the return values, rather than a simple bool. - However, we choose instead to use a unbounded_channel<>. - We'll only need to enqueue the first value, so we'll unbounded_channel::close() it - once we've retrieved that value. Subsequent push() calls will return closed. -

-

- -

-
// Assume that all passed functions have the same return type. The return type
-// of wait_first_value() is the return type of the first passed function. It is
-// simply invalid to pass NO functions.
-template< typename Fn, typename ... Fns >
-typename std::result_of< Fn() >::type
-wait_first_value( Fn && function, Fns && ... functions) {
-    typedef typename std::result_of< Fn() >::type return_t;
-    typedef boost::fibers::unbounded_channel< return_t > channel_t;
-    auto channelp( std::make_shared< channel_t >() );
-    // launch all the relevant fibers
-    wait_first_value_impl< return_t >( channelp,
-                                       std::forward< Fn >( function),
-                                       std::forward< Fns >( functions) ... );
-    // retrieve the first value
-    return_t value( channelp->value_pop() );
-    // close the channel: no subsequent push() has to succeed
-    channelp->close();
-    return value;
-}
-
-

-

-

- The meat of the wait_first_value_impl() - function is as you might expect: -

-

-

-
template< typename T, typename Fn >
-void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_channel< T > > channel,
-                            Fn && function) {
-    boost::fibers::fiber( [channel, function](){
-                              // Ignore channel_op_status returned by push():
-                              // might be closed; we simply don't care.
-                              channel->push( function() );
-                          }).detach();
-}
-
-

-

-

- It calls the passed function, pushes its return value and ignores - the push() - result. You might call it like this: -

-

-

-
std::string result = wait_first_value(
-        [](){ return sleeper("wfv_third",  150); },
-        [](){ return sleeper("wfv_second", 100); },
-        [](){ return sleeper("wfv_first",   50); });
-std::cout << "wait_first_value() => " << result << std::endl;
-assert(result == "wfv_first");
-
-

-

-
-
- -

- We may not be running in an environment in which we can guarantee - no exception will be thrown by any of our task functions. In that - case, the above implementations of wait_first_something() would be naïve: as mentioned in - the section on Fiber Management, - an uncaught exception in one of our task fibers would cause std::terminate() - to be called. -

-

- Let's at least ensure that such an exception would propagate to the - fiber awaiting the first result. We can use future<> to - transport either a return value or an exception. Therefore, we will - change wait_first_value()'s unbounded_channel<> to - hold future< - T > - items instead of simply T. -

-

- Once we have a future<> in hand, all we need do is - call future::get(), which will either return the value - or rethrow the exception. -

-

- -

-
template< typename Fn, typename ... Fns >
-typename std::result_of< Fn() >::type
-wait_first_outcome( Fn && function, Fns && ... functions) {
-    // In this case, the value we pass through the channel is actually a
-    // future -- which is already ready. future can carry either a value or an
-    // exception.
-    typedef typename std::result_of< Fn() >::type return_t;
-    typedef boost::fibers::future< return_t > future_t;
-    typedef boost::fibers::unbounded_channel< future_t > channel_t;
-    auto channelp(std::make_shared< channel_t >() );
-    // launch all the relevant fibers
-    wait_first_outcome_impl< return_t >( channelp,
-                                         std::forward< Fn >( function),
-                                         std::forward< Fns >( functions) ... );
-    // retrieve the first future
-    future_t future( channelp->value_pop() );
-    // close the channel: no subsequent push() has to succeed
-    channelp->close();
-    // either return value or throw exception
-    return future.get();
-}
-
-

-

-

- So far so good — but there's a timing issue. How should we obtain the - future<> - to unbounded_channel::push() on the channel? -

-

- We could call fibers::async(). That would certainly produce - a future<> - for the task function. The trouble is that it would return too quickly! - We only want future<> items for completed - tasks on our unbounded_channel<>. In fact, we only want the - future<> - for the one that completes first. If each fiber launched by wait_first_outcome() - were to push() - the result of calling async(), the channel would only ever report - the result of the leftmost task item — not the - one that completes most quickly. -

-

- Calling future::get() on the future returned by async() - wouldn't be right. You can only call get() once per future<> instance! And if there were - an exception, it would be rethrown inside the helper fiber at the - producer end of the channel, rather than propagated to the consumer - end. -

-

- We could call future::wait(). That would block the helper - fiber until the future<> became ready, at which point - we could push() - it to be retrieved by wait_first_outcome(). -

-

- That would work — but there's a simpler tactic that avoids creating - an extra fiber. We can wrap the task function in a packaged_task<>. - While one naturally thinks of passing a packaged_task<> to a new fiber — that is, in - fact, what async() does — in this case, we're already - running in the helper fiber at the producer end of the channel! We - can simply call the packaged_task<>. On return from that call, - the task function has completed, meaning that the future<> obtained from the packaged_task<> - is certain to be ready. At that point we can simply push() - it to the channel. -

-

- -

-
template< typename T, typename CHANNELP, typename Fn >
-void wait_first_outcome_impl( CHANNELP channel, Fn && function) {
-    boost::fibers::fiber(
-        // Use std::bind() here for C++11 compatibility. C++11 lambda capture
-        // can't move a move-only Fn type, but bind() can. Let bind() move the
-        // channel pointer and the function into the bound object, passing
-        // references into the lambda.
-        std::bind(
-            []( CHANNELP & channel,
-                typename std::decay< Fn >::type & function) {
-                // Instantiate a packaged_task to capture any exception thrown by
-                // function.
-                boost::fibers::packaged_task< T() > task( function);
-                // Immediately run this packaged_task on same fiber. We want
-                // function() to have completed BEFORE we push the future.
-                task();
-                // Pass the corresponding future to consumer. Ignore
-                // channel_op_status returned by push(): might be closed; we
-                // simply don't care.
-                channel->push( task.get_future() );
-            },
-            channel,
-            std::forward< Fn >( function)
-        )).detach();
-}
-
-

-

-

- Calling it might look like this: -

-

-

-
std::string result = wait_first_outcome(
-        [](){ return sleeper("wfos_first",   50); },
-        [](){ return sleeper("wfos_second", 100); },
-        [](){ return sleeper("wfos_third",  150); });
-std::cout << "wait_first_outcome(success) => " << result << std::endl;
-assert(result == "wfos_first");
-
-std::string thrown;
-try {
-    result = wait_first_outcome(
-            [](){ return sleeper("wfof_first",   50, true); },
-            [](){ return sleeper("wfof_second", 100); },
-            [](){ return sleeper("wfof_third",  150); });
-} catch ( std::exception const& e) {
-    thrown = e.what();
-}
-std::cout << "wait_first_outcome(fail) threw '" << thrown
-          << "'" << std::endl;
-assert(thrown == "wfof_first");
-
-

-

-
-
- -

- One scenario for when_any functionality is when we're - redundantly contacting some number of possibly-unreliable web services. - Not only might they be slow — any one of them might produce a failure - rather than the desired result. -

-

- In such a case, wait_first_outcome() - isn't the right approach. If one of the services produces an error - quickly, while another follows up with a real answer, we don't want - to prefer the error just because it arrived first! -

-

- Given the unbounded_channel< future< T - > > - we already constructed for wait_first_outcome(), though, we can readily recast - the interface function to deliver the first successful - result. -

-

- That does beg the question: what if all the - task functions throw an exception? In that case we'd probably better - know about it. -

-

- The C++ - Parallelism Draft Technical Specification proposes a std::exception_list exception capable - of delivering a collection of std::exception_ptrs. - Until that becomes universally available, let's fake up an exception_list of our own: -

-

-

-
class exception_list : public std::runtime_error {
-public:
-    exception_list( std::string const& what) :
-        std::runtime_error( what) {
-    }
-
-    typedef std::vector< std::exception_ptr >   bundle_t;
-
-    // N4407 proposed std::exception_list API
-    typedef bundle_t::const_iterator iterator;
-
-    std::size_t size() const noexcept {
-        return bundle_.size();
-    }
-
-    iterator begin() const noexcept {
-        return bundle_.begin();
-    }
-
-    iterator end() const noexcept {
-        return bundle_.end();
-    }
-
-    // extension to populate
-    void add( std::exception_ptr ep) {
-        bundle_.push_back( ep);
-    }
-
-private:
-    bundle_t bundle_;
-};
-
-

-

-

- Now we can build wait_first_success(), using wait_first_outcome_impl(). -

-

- Instead of retrieving only the first future<> from the channel, we must - now loop over future<> items. Of course we must - limit that iteration! If we launch only count - producer fibers, the (count+1) - st - unbounded_channel::pop() call would block forever. -

-

- Given a ready future<>, we can distinguish failure - by calling future::get_exception_ptr(). If - the future<> - in fact contains a result rather than an exception, get_exception_ptr() - returns nullptr. In - that case, we can confidently call future::get() to return - that result to our caller. -

-

- If the std::exception_ptr is not - nullptr, though, we - collect it into our pending exception_list - and loop back for the next future<> from the channel. -

-

- If we fall out of the loop — if every single task fiber threw an exception - — we throw the exception_list - exception into which we've been collecting those std::exception_ptrs. -

-

- -

-
template< typename Fn, typename ... Fns >
-typename std::result_of< Fn() >::type
-wait_first_success( Fn && function, Fns && ... functions) {
-    std::size_t count( 1 + sizeof ... ( functions) );
-    // In this case, the value we pass through the channel is actually a
-    // future -- which is already ready. future can carry either a value or an
-    // exception.
-    typedef typename std::result_of< typename std::decay< Fn >::type() >::type return_t;
-    typedef boost::fibers::future< return_t > future_t;
-    typedef boost::fibers::unbounded_channel< future_t > channel_t;
-    auto channelp( std::make_shared< channel_t >() );
-    // launch all the relevant fibers
-    wait_first_outcome_impl< return_t >( channelp,
-                                         std::forward< Fn >( function),
-                                         std::forward< Fns >( functions) ... );
-    // instantiate exception_list, just in case
-    exception_list exceptions("wait_first_success() produced only errors");
-    // retrieve up to 'count' results -- but stop there!
-    for ( std::size_t i = 0; i < count; ++i) {
-        // retrieve the next future
-        future_t future( channelp->value_pop() );
-        // retrieve exception_ptr if any
-        std::exception_ptr error( future.get_exception_ptr() );
-        // if no error, then yay, return value
-        if ( ! error) {
-            // close the channel: no subsequent push() has to succeed
-            channelp->close();
-            // show caller the value we got
-            return future.get();
-        }
-
-        // error is non-null: collect
-        exceptions.add( error);
-    }
-    // We only arrive here when every passed function threw an exception.
-    // Throw our collection to inform caller.
-    throw exceptions;
-}
-
-

-

-

- A call might look like this: -

-

-

-
std::string result = wait_first_success(
-            [](){ return sleeper("wfss_first",   50, true); },
-            [](){ return sleeper("wfss_second", 100); },
-            [](){ return sleeper("wfss_third",  150); });
-std::cout << "wait_first_success(success) => " << result << std::endl;
-assert(result == "wfss_second");
-
-

-

-
-
- -

- We would be remiss to ignore the case in which the various task functions - have distinct return types. That means that the value returned by - the first of them might have any one of those types. We can express - that with Boost.Variant. -

-

- To keep the example simple, we'll revert to pretending that none - of them can throw an exception. That makes wait_first_value_het() strongly resemble wait_first_value(). - We can actually reuse wait_first_value_impl(), - merely passing boost::variant<T0, T1, ...> - as the channel's value type rather than the common T! -

-

- Naturally this could be extended to use wait_first_success() - semantics instead. -

-

-

-
// No need to break out the first Fn for interface function: let the compiler
-// complain if empty.
-// Our functions have different return types, and we might have to return any
-// of them. Use a variant, expanding std::result_of<Fn()>::type for each Fn in
-// parameter pack.
-template< typename ... Fns >
-boost::variant< typename std::result_of< Fns() >::type ... >
-wait_first_value_het( Fns && ... functions) {
-    // Use unbounded_channel<boost::variant<T1, T2, ...>>; see remarks above.
-    typedef boost::variant< typename std::result_of< Fns() >::type ... > return_t;
-    typedef boost::fibers::unbounded_channel< return_t > channel_t;
-    auto channelp( std::make_shared< channel_t >() );
-    // launch all the relevant fibers
-    wait_first_value_impl< return_t >( channelp,
-                                       std::forward< Fns >( functions) ... );
-    // retrieve the first value
-    return_t value( channelp->value_pop() );
-    // close the channel: no subsequent push() has to succeed
-    channelp->close();
-    return value;
-}
-
-

-

-

- It might be called like this: -

-

-

-
boost::variant< std::string, double, int > result =
-    wait_first_value_het(
-            [](){ return sleeper("wfvh_third",  150); },
-            [](){ return sleeper(3.14,          100); },
-            [](){ return sleeper(17,             50); });
-std::cout << "wait_first_value_het() => " << result << std::endl;
-assert(boost::get< int >( result) == 17);
-
-

-

-
-
- -

- Certain topics in C++ can arouse strong passions, and exceptions - are no exception. We cannot resist mentioning — for purely informational - purposes — that when you need only the first result - from some number of concurrently-running fibers, it would be possible - to pass a shared_ptr< promise<>> to the participating - fibers, then cause the initiating fiber to call future::get() on - its future<>. The first fiber to call promise::set_value() on - that shared promise - will succeed; subsequent set_value() calls on the same promise instance will throw future_error. -

-

- Use this information at your own discretion. Beware the dark side. -

-
-
-
- - -
- -

- For the case in which we must wait for all task - functions to complete — but we don't need results (or expect exceptions) - from any of them — we can write wait_all_simple() that looks remarkably like wait_first_simple(). The difference is that - instead of our Done - class, we instantiate a barrier and call its barrier::wait(). -

-

- We initialize the barrier - with (count+1) because we are launching count fibers, plus the wait() - call within wait_all_simple() itself. -

-

-

-
template< typename ... Fns >
-void wait_all_simple( Fns && ... functions) {
-    std::size_t count( sizeof ... ( functions) );
-    // Initialize a barrier(count+1) because we'll immediately wait on it. We
-    // don't want to wake up until 'count' more fibers wait on it. Even though
-    // we'll stick around until the last of them completes, use shared_ptr
-    // anyway because it's easier to be confident about lifespan issues.
-    auto barrier( std::make_shared< boost::fibers::barrier >( count + 1) );
-    wait_all_simple_impl( barrier, std::forward< Fns >( functions) ... );
-    barrier->wait();
-}
-
-

-

-

- As stated above, the only difference between wait_all_simple_impl() and wait_first_simple_impl() - is that the former calls barrier::wait() rather than Done::notify(): -

-

-

-
template< typename Fn, typename ... Fns >
-void wait_all_simple_impl( std::shared_ptr< boost::fibers::barrier > barrier,
-                           Fn && function, Fns && ... functions) {
-    boost::fibers::fiber(
-            std::bind(
-                []( std::shared_ptr< boost::fibers::barrier > & barrier,
-                    typename std::decay< Fn >::type & function) mutable {
-                        function();
-                        barrier->wait();
-                },
-                barrier,
-                std::forward< Fn >( function)
-            )).detach();
-    wait_all_simple_impl( barrier, std::forward< Fns >( functions) ... );
-}
-
-

-

-

- You might call it like this: -

-

-

-
wait_all_simple(
-        [](){ sleeper("was_long",   150); },
-        [](){ sleeper("was_medium", 100); },
-        [](){ sleeper("was_short",   50); });
-
-

-

-

- Control will not return from the wait_all_simple() call until the last of its task - functions has completed. -

-
-
- -

- As soon as we want to collect return values from all the task functions, - we can see right away how to reuse wait_first_value()'s - channel<T> for the purpose. All we have to do is avoid closing - it after the first value! -

-

- But in fact, collecting multiple values raises an interesting question: - do we really want to wait until the slowest - of them has arrived? Wouldn't we rather process each result as soon - as it becomes available? -

-

- Fortunately we can present both APIs. Let's define wait_all_values_source() - to return shared_ptr<unbounded_channel<T>>.[7] -

-

- Given wait_all_values_source(), it's straightforward to implement - wait_all_values(): -

-

-

-
template< typename Fn, typename ... Fns >
-std::vector< typename std::result_of< Fn() >::type >
-wait_all_values( Fn && function, Fns && ... functions) {
-    std::size_t count( 1 + sizeof ... ( functions) );
-    typedef typename std::result_of< Fn() >::type return_t;
-    typedef std::vector< return_t > vector_t;
-    vector_t results;
-    results.reserve( count);
-
-    // get channel
-    std::shared_ptr< boost::fibers::unbounded_channel< return_t > > channel =
-        wait_all_values_source( std::forward< Fn >( function),
-                                std::forward< Fns >( functions) ... );
-    // fill results vector
-    return_t value;
-    while ( boost::fibers::channel_op_status::success == channel->pop(value) ) {
-        results.push_back( value);
-    }
-    // return vector to caller
-    return results;
-}
-
-

-

-

- It might be called like this: -

-

-

-
std::vector< std::string > values =
-    wait_all_values(
-            [](){ return sleeper("wav_late",   150); },
-            [](){ return sleeper("wav_middle", 100); },
-            [](){ return sleeper("wav_early",   50); });
-
-

-

-

- As you can see from the loop in wait_all_values(), instead of requiring its caller - to count values, we define wait_all_values_source() to unbounded_channel::close() the - channel when done. But how do we do that? Each producer fiber is - independent. It has no idea whether it is the last one to unbounded_channel::push() a - value. -

-

- We can address that problem with a counting - façade for the unbounded_channel<>. In fact, our façade need - only support the producer end of the channel. -

-

-

-
// Introduce a channel facade that closes the channel once a specific number
-// of items has been pushed. This allows an arbitrary consumer to read until
-// 'closed' without itself having to count items.
-template< typename T >
-class nchannel {
-public:
-    nchannel( std::shared_ptr< boost::fibers::unbounded_channel< T > > cp,
-              std::size_t lm):
-        channel_( cp),
-        limit_( lm) {
-        assert(channel_);
-        if ( 0 == limit_) {
-            channel_->close();
-        }
-    }
-
-    boost::fibers::channel_op_status push( T && va) {
-        boost::fibers::channel_op_status ok =
-            channel_->push( std::forward< T >( va) );
-        if ( ok == boost::fibers::channel_op_status::success &&
-             --limit_ == 0) {
-            // after the 'limit_'th successful push, close the channel
-            channel_->close();
-        }
-        return ok;
-    }
-
-private:
-    std::shared_ptr< boost::fibers::unbounded_channel< T > >    channel_;
-    std::size_t                                                 limit_;
-};
-
-

-

-

- Armed with nchannel<>, we can implement wait_all_values_source(). - It starts just like wait_first_value(). - The difference is that we wrap the unbounded_channel<T> with an nchannel<T> to pass to the producer fibers. -

-

- Then, of course, instead of popping the first value, closing the - channel and returning it, we simply return the shared_ptr<unbounded_channel<T>>. -

-

-

-
// Return a shared_ptr<unbounded_channel<T>> from which the caller can
-// retrieve each new result as it arrives, until 'closed'.
-template< typename Fn, typename ... Fns >
-std::shared_ptr< boost::fibers::unbounded_channel< typename std::result_of< Fn() >::type > >
-wait_all_values_source( Fn && function, Fns && ... functions) {
-    std::size_t count( 1 + sizeof ... ( functions) );
-    typedef typename std::result_of< Fn() >::type return_t;
-    typedef boost::fibers::unbounded_channel< return_t > channel_t;
-    // make the channel
-    auto channelp( std::make_shared< channel_t >() );
-    // and make an nchannel facade to close it after 'count' items
-    auto ncp( std::make_shared< nchannel< return_t > >( channelp, count) );
-    // pass that nchannel facade to all the relevant fibers
-    wait_all_values_impl< return_t >( ncp,
-                                      std::forward< Fn >( function),
-                                      std::forward< Fns >( functions) ... );
-    // then return the channel for consumer
-    return channelp;
-}
-
-

-

-

- For example: -

-

-

-
std::shared_ptr< boost::fibers::unbounded_channel< std::string > > channel =
-    wait_all_values_source(
-            [](){ return sleeper("wavs_third",  150); },
-            [](){ return sleeper("wavs_second", 100); },
-            [](){ return sleeper("wavs_first",   50); });
-std::string value;
-while ( boost::fibers::channel_op_status::success == channel->pop(value) ) {
-    std::cout << "wait_all_values_source() => '" << value
-              << "'" << std::endl;
-}
-
-

-

-

- wait_all_values_impl() really is just like wait_first_value_impl() - except for the use of nchannel<T> rather than unbounded_channel<T>: -

-

-

-
template< typename T, typename Fn >
-void wait_all_values_impl( std::shared_ptr< nchannel< T > > channel,
-                           Fn && function) {
-    boost::fibers::fiber( [channel, function](){
-                              channel->push(function());
-                          }).detach();
-}
-
-

-

-
-
- -

- Naturally, just as with wait_first_outcome(), - we can elaborate wait_all_values() and wait_all_values_source() - by passing future< T - > instead of plain T. -

-

- wait_all_until_error() pops that future< T - > and calls its future::get(): -

-

-

-
template< typename Fn, typename ... Fns >
-std::vector< typename std::result_of< Fn() >::type >
-wait_all_until_error( Fn && function, Fns && ... functions) {
-    std::size_t count( 1 + sizeof ... ( functions) );
-    typedef typename std::result_of< Fn() >::type return_t;
-    typedef typename boost::fibers::future< return_t > future_t;
-    typedef std::vector< return_t > vector_t;
-    vector_t results;
-    results.reserve( count);
-
-    // get channel
-    std::shared_ptr<
-        boost::fibers::unbounded_channel< future_t > > channel(
-            wait_all_until_error_source( std::forward< Fn >( function),
-                                         std::forward< Fns >( functions) ... ) );
-    // fill results vector
-    future_t future;
-    while ( boost::fibers::channel_op_status::success == channel->pop( future) ) {
-        results.push_back( future.get() );
-    }
-    // return vector to caller
-    return results;
-}
-
-

-

-

- For example: -

-

-

-
std::string thrown;
-try {
-    std::vector< std::string > values = wait_all_until_error(
-            [](){ return sleeper("waue_late",   150); },
-            [](){ return sleeper("waue_middle", 100, true); },
-            [](){ return sleeper("waue_early",   50); });
-} catch ( std::exception const& e) {
-    thrown = e.what();
-}
-std::cout << "wait_all_until_error(fail) threw '" << thrown
-          << "'" << std::endl;
-
-

-

-

- Naturally this complicates - the API for wait_all_until_error_source(). The caller must both retrieve - a future< - T > - and call its get() method. It would, of course, be - possible to return a façade over the consumer end of the channel - that would implicitly perform the get() and return a simple T (or throw). -

-

- The implementation is just as you would expect. Notice, however, - that we can reuse wait_first_outcome_impl(), - passing the nchannel<T> rather than unbounded_channel<T>. -

-

-

-
// Return a shared_ptr<unbounded_channel<future<T>>> from which the caller can
-// get() each new result as it arrives, until 'closed'.
-template< typename Fn, typename ... Fns >
-std::shared_ptr<
-    boost::fibers::unbounded_channel<
-        boost::fibers::future<
-            typename std::result_of< Fn() >::type > > >
-wait_all_until_error_source( Fn && function, Fns && ... functions) {
-    std::size_t count( 1 + sizeof ... ( functions) );
-    typedef typename std::result_of< Fn() >::type return_t;
-    typedef boost::fibers::future< return_t > future_t;
-    typedef boost::fibers::unbounded_channel< future_t > channel_t;
-    // make the channel
-    auto channelp( std::make_shared< channel_t >() );
-    // and make an nchannel facade to close it after 'count' items
-    auto ncp( std::make_shared< nchannel< future_t > >( channelp, count) );
-    // pass that nchannel facade to all the relevant fibers
-    wait_first_outcome_impl< return_t >( ncp,
-                                         std::forward< Fn >( function),
-                                         std::forward< Fns >( functions) ... );
-    // then return the channel for consumer
-    return channelp;
-}
-
-

-

-

- For example: -

-

-

-
typedef boost::fibers::future< std::string > future_t;
-std::shared_ptr< boost::fibers::unbounded_channel< future_t > > channel =
-    wait_all_until_error_source(
-            [](){ return sleeper("wauess_third",  150); },
-            [](){ return sleeper("wauess_second", 100); },
-            [](){ return sleeper("wauess_first",   50); });
-future_t future;
-while ( boost::fibers::channel_op_status::success == channel->pop( future) ) {
-    std::string value( future.get() );
-    std::cout << "wait_all_until_error_source(success) => '" << value
-              << "'" << std::endl;
-}
-
-

-

-
-
- -

- Given wait_all_until_error_source(), - it might be more reasonable to make a wait_all_...() that collects all - errors instead of presenting only the first: -

-

-

-
template< typename Fn, typename ... Fns >
-std::vector< typename std::result_of< Fn() >::type >
-wait_all_collect_errors( Fn && function, Fns && ... functions) {
-    std::size_t count( 1 + sizeof ... ( functions) );
-    typedef typename std::result_of< Fn() >::type return_t;
-    typedef typename boost::fibers::future< return_t > future_t;
-    typedef std::vector< return_t > vector_t;
-    vector_t results;
-    results.reserve( count);
-    exception_list exceptions("wait_all_collect_errors() exceptions");
-
-    // get channel
-    std::shared_ptr<
-        boost::fibers::unbounded_channel< future_t > > channel(
-            wait_all_until_error_source( std::forward< Fn >( function),
-                                         std::forward< Fns >( functions) ... ) );
-    // fill results and/or exceptions vectors
-    future_t future;
-    while ( boost::fibers::channel_op_status::success == channel->pop( future) ) {
-        std::exception_ptr exp = future.get_exception_ptr();
-        if ( ! exp) {
-            results.push_back( future.get() );
-        } else {
-            exceptions.add( exp);
-        }
-    }
-    // if there were any exceptions, throw
-    if ( exceptions.size() ) {
-        throw exceptions;
-    }
-    // no exceptions: return vector to caller
-    return results;
-}
-
-

-

-

- The implementation is a simple variation on wait_first_success(), - using the same exception_list - exception class. -

-
-
- -

- But what about the case when we must wait for all results of different - types? -

-

- We can present an API that is frankly quite cool. Consider a sample - struct: -

-

-

-
struct Data {
-    std::string str;
-    double      inexact;
-    int         exact;
-
-    friend std::ostream& operator<<( std::ostream& out, Data const& data);
-    ...
-};
-
-

-

-

- Let's fill its members from task functions all running concurrently: -

-

-

-
Data data = wait_all_members< Data >(
-        [](){ return sleeper("wams_left", 100); },
-        [](){ return sleeper(3.14,        150); },
-        [](){ return sleeper(17,          50); });
-std::cout << "wait_all_members<Data>(success) => " << data << std::endl;
-
-

-

-

- Note that for this case, we abandon the notion of capturing the earliest - result first, and so on: we must fill exactly the passed struct in - left-to-right order. -

-

- That permits a beautifully simple implementation: -

-

-

-
// Explicitly pass Result. This can be any type capable of being initialized
-// from the results of the passed functions, such as a struct.
-template< typename Result, typename ... Fns >
-Result wait_all_members( Fns && ... functions) {
-    // Run each of the passed functions on a separate fiber, passing all their
-    // futures to helper function for processing.
-    return wait_all_members_get< Result >(
-            boost::fibers::async( std::forward< Fns >( functions) ) ... );
-}
-
-

-

-

-

-
template< typename Result, typename ... Futures >
-Result wait_all_members_get( Futures && ... futures) {
-    // Fetch the results from the passed futures into Result's initializer
-    // list. It's true that the get() calls here will block the implicit
-    // iteration over futures -- but that doesn't matter because we won't be
-    // done until the slowest of them finishes anyway. As results are
-    // processed in argument-list order rather than order of completion, the
-    // leftmost get() to throw an exception will cause that exception to
-    // propagate to the caller.
-    return Result{ futures.get() ... };
-}
-
-

-

-

- It is tempting to try to implement wait_all_members() as a one-liner like this: -

-
return Result{ boost::fibers::async(functions).get()... };
-
-

- The trouble with this tactic is that it would serialize all the task - functions. The runtime makes a single pass through functions, calling fibers::async() for - each and then immediately calling future::get() on its returned - future<>. - That blocks the implicit loop. The above is almost equivalent to - writing: -

-
return Result{ functions()... };
-
-

- in which, of course, there is no concurrency at all. -

-

- Passing the argument pack through a function-call boundary (wait_all_members_get()) - forces the runtime to make two passes: one in - wait_all_members() to collect the future<>s from all the async() - calls, the second in wait_all_members_get() to fetch each of the results. -

-

- As noted in comments, within the wait_all_members_get() parameter pack expansion pass, - the blocking behavior of get() becomes irrelevant. Along the way, - we will hit the get() for the slowest task function; - after that every subsequent get() will complete in trivial time. -

-

- By the way, we could also use this same API to fill a vector or other - collection: -

-

-

-
// If we don't care about obtaining results as soon as they arrive, and we
-// prefer a result vector in passed argument order rather than completion
-// order, wait_all_members() is another possible implementation of
-// wait_all_until_error().
-auto strings = wait_all_members< std::vector< std::string > >(
-        [](){ return sleeper("wamv_left",   150); },
-        [](){ return sleeper("wamv_middle", 100); },
-        [](){ return sleeper("wamv_right",   50); });
-std::cout << "wait_all_members<vector>() =>";
-for ( std::string const& str : strings) {
-    std::cout << " '" << str << "'";
-}
-std::cout << std::endl;
-
-

-

-
-
-
-
- -
- - Overview -
-

- As always with cooperative concurrency, it is important not to let any - one fiber monopolize the processor too long: that could starve - other ready fibers. This section discusses a couple of solutions. -

-
- - Event-Driven - Program -
-

- Consider a classic event-driven program, organized around a main loop - that fetches and dispatches incoming I/O events. You are introducing - Boost.Fiber because certain asynchronous - I/O sequences are logically sequential, and for those you want to write - and maintain code that looks and acts sequential. -

-

- You are launching fibers on the application's main thread because certain - of their actions will affect its user interface, and the application's - UI framework permits UI operations only on the main thread. Or perhaps - those fibers need access to main-thread data, and it would be too expensive - in runtime (or development time) to robustly defend every such data item - with thread synchronization primitives. -

-

- You must ensure that the application's main loop itself - doesn't monopolize the processor: that the fibers it launches will get - the CPU cycles they need. -

-

- The solution is the same as for any fiber that might claim the CPU for - an extended time: introduce calls to this_fiber::yield(). - The most straightforward approach is to call yield() on every iteration of your existing - main loop. In effect, this unifies the application's main loop with - Boost.Fiber's internal main loop. yield() - allows the fiber manager to run any fibers that have become ready since - the previous iteration of the application's main loop. When these fibers - have had a turn, control passes to the thread's main fiber, which returns - from yield() - and resumes the application's main loop. -

-
- - Integrating - with Boost.Asio -
-

- More challenging is when the application's main loop is embedded in some - other library or framework. Such an application will typically, after - performing all necessary setup, pass control to some form of run() - function from which control does not return until application shutdown. -

-

- A Boost.Asio - program might call io_service::run() - in this way. -

-

- The trick here is to arrange to pass control to this_fiber::yield() frequently. - You can use an Asio - timer for this purpose. Instantiate the timer, arranging to call - a handler function when the timer expires: -

-

- [run_service] -

-

- The handler function calls yield(), then resets the timer and arranges - to wake up again on expiration: -

-

- [timer_handler] -

-

- Then instead of directly calling io_service::run(), your application would call the above - run_service(io_service&) - wrapper. -

-

- Since, in this example, we always pass control to the fiber manager via - yield(), - the calling fiber is never blocked. Therefore there is always at least - one ready fiber. Therefore the fiber manager never sleeps. -

-

- Using std::chrono::seconds(0) for - every keepalive timer interval would be unfriendly - to other threads. When all I/O is pending and all fibers are blocked, - the io_service and the fiber manager would simply spin the CPU, passing - control back and forth to each other. Resetting the timer for keepalive_iterval allows tuning the - responsiveness of this thread relative to others in the same way as when - Boost.Fiber is running without Boost.Asio. -

-

- The source code above is found in round_robin.hpp. -

-
-
- -

- Performance measurements were taken using std::chrono::highresolution_clock, - with overhead corrections. The code was compiled using the build options: - variant = release, optimization = speed [8]. -

-

- The columns labeled fiber (atomics) - were compiled with default fiber synchronization, capable of synchronizing - fibers running on different threads. The columns labeled fiber - (raw) were compiled with BOOST_FIBERS_NO_ATOMICS. -

-
-

Table 1.1. Overhead of join (contains fiber-context destruction, fiber-stack - deallocation)

-
------- - - - - - - - - - - - - - - -
-

- thread -

-
-

- fiber (atomics) -

-
-

- fiber (raw) -

-
-

- tbb -

-
-

- qthread -

-
-

- 18 µs -

-
-

- 950 ns -

-
-

- 900 ns -

-
-

- 570 ns -

-
-

- 620 ns -

-
-
-

- (from overhead_join.cpp) -

-
-

Table 1.2. Overhead of detach

-
----- - - - - - - - - - - -
-

- thread -

-
-

- fiber (atomics) -

-
-

- fiber (raw) -

-
-

- 126 ns -

-
-

- 21 ns -

-
-

- 20 ns -

-
-
-

- (from overhead_detach.cpp) -

-
-

Table 1.3. Overhead of yield

-
----- - - - - - - - - - - -
-

- thread -

-
-

- fiber (atomics) -

-
-

- fiber (raw) -

-
-

- 1.5 µs -

-
-

- 310 ns -

-
-

- 330 ns -

-
-
-

- (from overhead_yield.cpp) -

-
-

Table 1.4. Overhead of waiting on a future

-
----- - - - - - - - - - - -
-

- thread -

-
-

- fiber (atomics) -

-
-

- fiber (raw) -

-
-

- 16 µs -

-
-

- 1.40 µs -

-
-

- 1.38 µs -

-
-
-

- (from overhead_future.cpp) -

-
-

Table 1.5. Overhead of fiber creation (contains fiber-stack allocation and - preparation, fiber-context construction, scheduler handling)

-
----- - - - - - - - - - - -
-

- thread -

-
-

- fiber (atomics) -

-
-

- fiber (raw) -

-
-

- 18 µs -

-
-

- 450 ns -

-
-

- 445 ns -

-
-
-

- (from overhead_create.cpp) -

-
-

Table 1.6. Scaling of creating and joining

-
------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

- average of -

-
-

- thread -

-
-

- fiber (atomics) -

-
-

- fiber (raw) -

-
-

- 10 -

-
-

- 8.21 µs -

-
-

- 1.96 µs -

-
-

- 1.85 µs -

-
-

- 50 -

-
-

- 6.67 µs -

-
-

- 1.40 µs -

-
-

- 1.27 µs -

-
-

- 100 -

-
-

- 6.79 µs -

-
-

- 1.84 µs -

-
-

- 1.81 µs -

-
-

- 500 -

-
-

- 8.25 µs -

-
-

- 1.13 µs -

-
-

- 1.10 µs -

-
-

- 1000 -

-
-

- 7.71 µs -

-
-

- 1.46 µs -

-
-

- 1.26 µs -

-
-

- 5000 -

-
-

- 5.67 µs -

-
-

- 2.11 µs -

-
-

- 1.90 µs -

-
-

- 10000 -

-
-

- 5.25 µs -

-
-

- 2.36 µs -

-
-

- 1.89 µs -

-
-
-

- (from scale_join.cpp) -

-

- Numbers of the microbenchmark - syknet from Alexander Temerev [9]: -

-
-

Table 1.7. performance of N=100000 actors/goroutines/fibers

-
------- - - - - - - - - - - - - - - -
-

- Haskell | stack-1.0.4 -

-
-

- fiber (single threaded/raw) | gcc-5.2.1 -

-
-

- fiber (single threaded/atomics) | gcc-5.2.1 -

-
-

- Erlang | erts-7.0 -

-
-

- Go | go1.4.2 -

-
-

- 58ms - 108ms -

-
-

- 205ms - 263ms -

-
-

- 221ms - 278ms -

-
-

- 237ms- 470ms -

-
-

- 614ms - 883ms -

-
-
-
-
-
- -
- - Overview -
-

- As noted in the Scheduling section, - by default Boost.Fiber uses its own - round_robin scheduler for each thread. To control the - way Boost.Fiber schedules ready fibers - on a particular thread, in general you must follow several steps. This - section discusses those steps, whereas Scheduling - serves as a reference for the classes involved. -

-

- The library's fiber manager keeps track of suspended (blocked) fibers. - Only when a fiber becomes ready to run is it passed to the scheduler. - Of course, if there are fewer than two ready fibers, the scheduler's - job is trivial. Only when there are two or more ready fibers does the - particular scheduler implementation start to influence the overall sequence - of fiber execution. -

-

- In this section we illustrate a simple custom scheduler that honors an - integer fiber priority. We will implement it such that a fiber with higher - priority is preferred over a fiber with lower priority. Any fibers with - equal priority values are serviced on a round-robin basis. -

-

- The full source code for the examples below is found in priority.cpp. -

-
- - Custom - Property Class -
-

- The first essential point is that we must associate an integer priority - with each fiber.[10] -

-

- One might suggest deriving a custom fiber subclass to store - such properties. There are a couple of reasons for the present mechanism. -

-
    -
  1. - Boost.Fiber provides a number of - different ways to launch a fiber. (Consider fibers::async().) - Higher-level libraries might introduce additional such wrapper functions. - A custom scheduler must associate its custom properties with every - fiber in the thread, not only the ones explicitly launched by instantiating - a custom fiber subclass. -
  2. -
  3. - Consider a large existing program that launches fibers in many different - places in the code. We discover a need to introduce a custom scheduler - for a particular thread. If supporting that scheduler's custom properties - required a particular fiber - subclass, we would have to hunt down and modify every place that - launches a fiber on that thread. -
  4. -
  5. - The fiber class is actually just a handle to internal context data. - A subclass of fiber - would not add data to context. -
  6. -
-

- The present mechanism allows you to drop in a custom scheduler - with its attendant custom properties without altering - the rest of your application. -

-

- Instead of deriving a custom scheduler fiber properties subclass from - fiber, you must instead derive it from fiber_properties. -

-

-

-
class priority_props : public boost::fibers::fiber_properties {
-public:
-    priority_props( boost::fibers::context * ctx):
-        fiber_properties( ctx), 1
-        priority_( 0) {
-    }
-
-    int get_priority() const {
-        return priority_; 2
-    }
-
-    // Call this method to alter priority, because we must notify
-    // priority_scheduler of any change.
-    void set_priority( int p) { 3
-        // Of course, it's only worth reshuffling the queue and all if we're
-        // actually changing the priority.
-        if ( p != priority_) {
-            priority_ = p;
-            notify();
-        }
-    }
-
-    // The fiber name of course is solely for purposes of this example
-    // program; it has nothing to do with implementing scheduler priority.
-    // This is a public data member -- not requiring set/get access methods --
-    // because we need not inform the scheduler of any change.
-    std::string name; 4
-private:
-    int priority_;
-};
-
-

-

-
- - - - - - - - - - - - - - - - -

1

- Your subclass constructor must accept a context* - and pass it to the fiber_properties - constructor. -

2

- Provide read access methods at your own discretion. -

3

- It's important to call notify() on any change in a property that - can affect the scheduler's behavior. Therefore, such modifications - should only be performed through an access method. -

4

- A property that does not affect the scheduler does not need access - methods. -

-
- - Custom - Scheduler Class -
-

- Now we can derive a custom scheduler from sched_algorithm_with_properties<>, - specifying our custom property class priority_props - as the template parameter. -

-

-

-
class priority_scheduler :
-    public boost::fibers::sched_algorithm_with_properties< priority_props > {
-private:
-    typedef boost::fibers::scheduler::ready_queue_t1   rqueue_t;
-
-    rqueue_t                                rqueue_;
-    std::mutex                  mtx_{};
-    std::condition_variable     cnd_{};
-    bool                        flag_{ false };
-
-public:
-    priority_scheduler() :
-        rqueue_() {
-    }
-
-    // For a subclass of sched_algorithm_with_properties<>, it's important to
-    // override the correct awakened() overload.
-    2virtual void awakened( boost::fibers::context * ctx, priority_props & props) noexcept {
-        int ctx_priority = props.get_priority(); 3
-        // With this scheduler, fibers with higher priority values are
-        // preferred over fibers with lower priority values. But fibers with
-        // equal priority values are processed in round-robin fashion. So when
-        // we're handed a new context*, put it at the end of the fibers
-        // with that same priority. In other words: search for the first fiber
-        // in the queue with LOWER priority, and insert before that one.
-        rqueue_t::iterator i( std::find_if( rqueue_.begin(), rqueue_.end(),
-            [ctx_priority,this]( boost::fibers::context & c)
-            { return properties( &c ).get_priority() < ctx_priority; }));
-        // Now, whether or not we found a fiber with lower priority,
-        // insert this new fiber here.
-        rqueue_.insert( i, * ctx);
-    }
-
-    4virtual boost::fibers::context * pick_next() noexcept {
-        // if ready queue is empty, just tell caller
-        if ( rqueue_.empty() ) {
-            return nullptr;
-        }
-        boost::fibers::context * ctx( & rqueue_.front() );
-        rqueue_.pop_front();
-        return ctx;
-    }
-
-    5virtual bool has_ready_fibers() const noexcept {
-        return ! rqueue_.empty();
-    }
-
-    6virtual void property_change( boost::fibers::context * ctx, priority_props & props) noexcept {
-        // Although our priority_props class defines multiple properties, only
-        // one of them (priority) actually calls notify() when changed. The
-        // point of a property_change() override is to reshuffle the ready
-        // queue according to the updated priority value.
-
-        // 'ctx' might not be in our queue at all, if caller is changing the
-        // priority of (say) the running fiber. If it's not there, no need to
-        // move it: we'll handle it next time it hits awakened().
-        if ( ! ctx->ready_is_linked()) { 7
-            return;
-        }
-
-        // Found ctx: unlink it
-        ctx->ready_unlink();
-
-        // Here we know that ctx was in our ready queue, but we've unlinked
-        // it. We happen to have a method that will (re-)add a context* to the
-        // right place in the ready queue.
-        awakened( ctx, props);
-    }
-
-    void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept {
-        if ( (std::chrono::steady_clock::time_point::max)() == time_point) {
-            std::unique_lock< std::mutex > lk( mtx_);
-            cnd_.wait( lk, [this](){ return flag_; });
-            flag_ = false;
-        } else {
-            std::unique_lock< std::mutex > lk( mtx_);
-            cnd_.wait_until( lk, time_point, [this](){ return flag_; });
-            flag_ = false;
-        }
-    }
-
-    void notify() noexcept {
-        std::unique_lock< std::mutex > lk( mtx_);
-        flag_ = true;
-        lk.unlock();
-        cnd_.notify_all();
-    }
-};
-
-

-

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -

1

- See ready_queue_t. -

2

- You must override the sched_algorithm_with_properties::awakened() - method. - This is how your scheduler receives notification of a fiber that - has become ready to run. -

3

- props is the instance - of priority_props associated with the passed fiber ctx. -

4

- You must override the sched_algorithm_with_properties::pick_next() - method. - This is how your scheduler actually advises the fiber manager of - the next fiber to run. -

5

- You must override sched_algorithm_with_properties::has_ready_fibers() - to - inform the fiber manager of the state of your ready queue. -

6

- Overriding sched_algorithm_with_properties::property_change() - is - optional. This override handles the case in which the running fiber - changes the priority of another ready fiber: a fiber already in our - queue. In that case, move the updated fiber within the queue. -

7

- Your property_change() override must be able to handle - the case in which the passed ctx - is not in your ready queue. It might be running, or it might be blocked. -

-

- Our example priority_scheduler - doesn't override sched_algorithm_with_properties::new_properties(): - we're content with allocating priority_props - instances on the heap. -

-
- - Replace - Default Scheduler -
-

- You must call use_scheduling_algorithm() at the - start of each thread on which you want Boost.Fiber - to use your custom scheduler rather than its own default round_robin. - Specifically, you must call use_scheduling_algorithm() before performing any other Boost.Fiber operations on that thread. -

-

-

-
int main( int argc, char *argv[]) {
-    // make sure we use our priority_scheduler rather than default round_robin
-    boost::fibers::use_scheduling_algorithm< priority_scheduler >();
-    ...
-}
-
-

-

-
- - Use - Properties -
-

- The running fiber can access its own fiber_properties subclass - instance by calling this_fiber::properties(). Although - properties<>() - is a nullary function, you must pass, as a template parameter, the fiber_properties subclass. -

-

-

-
boost::this_fiber::properties< priority_props >().name = "main";
-
-

-

-

- Given a fiber instance still connected with a running fiber - (that is, not fiber::detach()ed), you may access that fiber's - properties using fiber::properties(). As with this_fiber::properties<>(), - you must pass your fiber_properties - subclass as the template parameter. -

-

-

-
template< typename Fn >
-boost::fibers::fiber launch( Fn && func, std::string const& name, int priority) {
-    boost::fibers::fiber fiber( func);
-    priority_props & props( fiber.properties< priority_props >() );
-    props.name = name;
-    props.set_priority( priority);
-    return fiber;
-}
-
-

-

-

- Launching a new fiber schedules that fiber as ready, but does not - immediately enter its fiber-function. The current - fiber retains control until it blocks (or yields, or terminates) for - some other reason. As shown in the launch() function above, it is reasonable to - launch a fiber and immediately set relevant properties -- such as, for - instance, its priority. Your custom scheduler can then make use of this - information next time the fiber manager calls sched_algorithm_with_properties::pick_next(). -

-
-
- -
- - distinction - between coroutines and fibers -
-

- The fiber library extends the coroutine library by adding a scheduler - and synchronization mechanisms. -

-
    -
  • - a coroutine yields -
  • -
  • - a fiber blocks -
  • -
-

- When a coroutine yields, it passes control directly to its caller (or, - in the case of symmetric coroutines, a designated other coroutine). When - a fiber blocks, it implicitly passes control to the fiber scheduler. - Coroutines have no scheduler because they need no scheduler.[11]. -

-
- - what - about transactional memory -
-

- GCC supports transactional memory since version 4.7. Unfortunately tests - show that transactional memory is slower (ca. 4x) than spinlocks using - atomics. Once transactional memory is improved (supporting hybrid tm), - spinlocks will be replaced by __transaction_atomic{} statements surrounding - the critical sections. -

-
- - synchronization - between fibers running in different threads -
-

- Synchronization classes from Boost.Thread - block the entire thread. In contrast, the synchronization classes from - Boost.Fiber block only specific fibers, - so that the scheduler can still keep the thread busy running other fibers - in the meantime. The synchronization classes from Boost.Fiber - are designed to be thread-safe, i.e. it is possible to synchronize fibers - running in different threads as well as fibers running in the same thread. - (However, there is a build option to disable cross-thread fiber synchronization - support; see this description.) -

-
- - spurious - wakeup -
-

- Spurious wakeup can happen when using std::condition_variable: the condition - variable appears to be have been signaled while the awaited condition - may still be false. Spurious wakeup can happen repeatedly and is caused - on some multiprocessor systems where making std::condition_variable - wakeup completely predictable would slow down all std::condition_variable - operations.[12] -

-

- condition_variable is not subject to spurious - wakeup. Nonetheless it is prudent to test the business-logic condition - in a wait() - loop — or, equivalently, use one of the wait( lock, predicate - ) overloads. -

-

- See also No Spurious - Wakeups. -

-
- - migrating - fibers between threads -
-

- Support for migrating fibers between threads has been integrated. The - user-defined scheduler must call context::migrate() on - a fiber-context on the destination thread, passing migrate() the fiber-context to migrate. (For - more information about custom schedulers, see Customization.) - Examples work_sharing - and work_stealing in - directory examples might - be used as a blueprint. -

-

- See also Migrating fibers between threads. -

-
- - support - for Boost.Asio -
-

- Support for Boost.Asio's - async-result is not part of the official API. However, - to integrate with a boost::asio::io_service, - see Sharing a Thread with Another Main Loop. - To interface smoothly with an arbitrary Asio async I/O operation, see - Then There's Boost.Asio. -

-
- - tested - compilers -
-

- The library was tested with GCC-5.1.1, Clang-3.6.0 and MSVC-14.0 in c++11-mode. -

-
- - supported - architectures -
-

- Boost.Fiber depends on Boost.Context - - the list of supported architectures can be found here. -

-
-
- -

- I'd like to thank Agustín Bergé, Eugene Yakubovich, Giovanni Piero - Deretta and especially Nat Goodspeed. -

-
-
- -
- - Installing - the Fiber library -
-

- As Fiber is not yet officially part of Boost, it is necessary to embed - it in an existing Boost - source tree. -

-

- The downloaded - Fiber library can be placed into an existing Boost source tree - by moving the top-level Fiber directory to libs/fiber - under the top-level Boost directory, then further moving libs/fiber/include/boost/fiber (in other words, the Fiber library's - include/boost/fiber directory) to boost/fiber - under the top-level Boost directory. -

-

- On a Posix system such as Linux or OS X, you may use symlinks instead. -

-

- Create a symlink from the Boost directory's libs/fiber - to the top-level Fiber directory, e.g.: -

-
cd ~/boost_1_61_0
-ln -s ~/boost-fiber-master libs/fiber
-
-

- Then create a symlink from the Boost directory's boost/fiber - to the Fiber library's include/boost/fiber - directory: -

-
cd boost
-ln -s ../libs/fiber/include/boost/fiber fiber
-
-

- For some versions of the Boost.Build system, it was important to use - a relative symlink of that form for boost/fiber. -

-
- - Running - Tests -
-

- Once the Fiber library has been overlaid (or symlinked) into the Boost - source tree this way, the Boost.Build system can build it like any other - Boost library. In particular: -

-
cd ~/boost_1_61_0
-./bootstrap.sh
-./b2 libs/fiber/test
-
-

- On Windows, the commands would look more like: -

-
cd /D %HOMEDRIVE%%HOMEPATH%\boost_1_61_0
-bootstrap
-b2 libs\fiber\test
-
-
-
-

-

[2] - The current implementation wakes fibers in FIFO order: the first - to call wait() - wakes first, and so forth. But it is perilous to rely on the order - in which the various fibers will reach the wait() call. -

-

[3] - The main fiber on each thread, that is, the fiber on - which the thread is launched, cannot migrate to any other thread. Also - Boost.Fiber implicitly creates a dispatcher - fiber for each thread — this cannot migrate either. -

-

[4] - Of course it would be problematic to migrate a fiber that relies on - thread-local storage. -

-

[5] - This mechanism has been proposed as a conventional way to allow the - caller of an async function to specify completion handling: N4045. -

-

[6] - per N4045 -

-

[7] - We could have used either bounded_channel<> or - unbounded_channel<>. We chose unbounded_channel<> - on the assumption that its simpler semantics imply a cheaper implementation. -

-

[8] - Intel Core2 Q6700, x86_64, 3GHz -

-

[9] - Intel Core2 Q6700, x86_64, 3GHz -

-

[10] - A previous version of the Fiber library implicitly tracked an int priority - for each fiber, even though the default scheduler ignored it. This - has been dropped, since the library now supports arbitrary scheduler-specific - fiber properties. -

- -

[12] - David R. Butenhof Programming with POSIX Threads -

-
-
- - - -
-
-
-PrevUpHome -
- - diff --git a/doc/html/fiber/stack/segmented_stack.html b/doc/html/fiber/stack/segmented_stack.html deleted file mode 100644 index c2d324e9..00000000 --- a/doc/html/fiber/stack/segmented_stack.html +++ /dev/null @@ -1,129 +0,0 @@ - - - -Class segmented_stack - - - - - - - - - - - - - - - -
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
-
-
-PrevUpHomeNext -
-
- -

- Boost.Fiber supports usage of a segmented_stack, - i.e. the stack grows on demand. The fiber is created with a minimal stack - size which will be increased as required. Class segmented_stack models - the stack-allocator concept. - In contrast to protected_fixedsize_stack and - fixedsize_stack it creates a stack which grows on demand. -

-
- - - - - -
[Note]Note

- Segmented stacks are currently only supported by gcc - from version 4.7 and clang - from version 3.4 onwards. In order to - use a segmented_stack Boost.Fiber - must be built with property segmented-stacks, - e.g. toolset=gcc segmented-stacks=on at - b2/bjam command line. -

-
#include <boost/fiber/segmented_stack.hpp>
-
-struct segmented_stack {
-    segmented_stack(std::size_t stack_size = traits_type::default_size());
-
-    stack_context allocate();
-
-    void deallocate( stack_context &);
-}
-
-
- - stack_context allocate() -
-
-

-
-
Preconditions:
-

- traits_type::minimum_size() - <= size - and traits_type::is_unbounded() - || ( - traits_type::maximum_size() - >= size). -

-
Effects:
-

- Allocates memory of at least size - bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture - (the stack grows downwards/upwards) the stored address is the highest/lowest - address of the stack. -

-
-
-
- - void deallocate( stack_context - & sctx) -
-
-

-
-
Preconditions:
-

- sctx.sp is valid, traits_type::minimum_size() <= sctx.size and traits_type::is_unbounded() || ( traits_type::maximum_size() >= sctx.size). -

-
Effects:
-

- Deallocates the stack space. -

-
-
-
- - - - - -
[Note]Note

- If the library is compiled for segmented stacks, segmented_stack is - the only available stack allocator. -

-
- - - -
-
-
-PrevUpHomeNext -
- - diff --git a/doc/html/fiber/synchronization.html b/doc/html/fiber/synchronization.html index 5589c715..e9ed7c92 100644 --- a/doc/html/fiber/synchronization.html +++ b/doc/html/fiber/synchronization.html @@ -30,7 +30,7 @@
Mutex Types
Condition Variables
Barriers
-
Channels
+
Queues
Futures
Future
diff --git a/doc/html/fiber/synchronization/barriers.html b/doc/html/fiber/synchronization/barriers.html index e54d936e..f37be697 100644 --- a/doc/html/fiber/synchronization/barriers.html +++ b/doc/html/fiber/synchronization/barriers.html @@ -7,7 +7,7 @@ - + @@ -20,7 +20,7 @@

-PrevUpHomeNext +PrevUpHomeNext

@@ -195,7 +195,7 @@
-PrevUpHomeNext +PrevUpHomeNext
diff --git a/doc/html/fiber/synchronization/futures.html b/doc/html/fiber/synchronization/futures.html index 319d4b99..56838f93 100644 --- a/doc/html/fiber/synchronization/futures.html +++ b/doc/html/fiber/synchronization/futures.html @@ -6,7 +6,7 @@ - + @@ -20,7 +20,7 @@
-PrevUpHomeNext +PrevUpHomeNext

@@ -135,7 +135,7 @@ are
-PrevUpHomeNext +PrevUpHomeNext
diff --git a/doc/html/fiber/synchronization/channels.html b/doc/html/fiber/synchronization/queues.html similarity index 63% rename from doc/html/fiber/synchronization/channels.html rename to doc/html/fiber/synchronization/queues.html index b0c0dc39..0a4495f0 100644 --- a/doc/html/fiber/synchronization/channels.html +++ b/doc/html/fiber/synchronization/queues.html @@ -1,7 +1,7 @@ -Channels +Queues @@ -24,44 +24,44 @@

Boost.Fiber provides a bounded and a unbounded - channel suitable to synchonize fibers via message passing. + queue suitable to synchonize fibers via message passing.

-
typedef boost::fibers::unbounded_channel< int > channel_t;
+
typedef boost::fibers::unbounded_queue< int > queue_t;
 
-void send( channel_t & channel) {
+void send( queue_t & queue) {
     for ( int i = 0; i < 5; ++i) {
-        channel.push( i);
+        queue.push( i);
     }
-    channel.close();
+    queue.close();
 }
 
-void recv( channel_t & channel) {
+void recv( queue_t & queue) {
     int i;
-    while ( boost::fibers::channel_op_status::success == channel.pop(i) ) {
+    while ( boost::fibers::queue_op_status::success == queue.pop(i) ) {
         std::cout << "received " << i << std::endl;
     }
 }
 
-channel_t channel;
-boost::fibers::fiber f1( std::bind( send, ref( channel) ) );
-boost::fibers::fiber f2( std::bind( recv, ref( channel) ) );
+queue_t queue;
+boost::fibers::fiber f1( std::bind( send, ref( queue) ) );
+boost::fibers::fiber f2( std::bind( recv, ref( queue) ) );
 
 f1.join();
 f2.join();
 
-
- - Enumeration - channel_op_status +
+ + Enumeration + queue_op_status

- channel operations return the state of the channel. + queue operations return the state of the queue.

-
enum class channel_op_status {
+
enum class queue_op_status {
     success,
     empty,
     full,
@@ -70,8 +70,8 @@
 };
 
- - success + + success

@@ -83,47 +83,47 @@

- - empty + + empty

Effects:

- channel is empty, operation failed. + queue is empty, operation failed.

- - full + + full

Effects:

- channel is full, operation failed. + queue is full, operation failed.

- - closed + + closed

Effects:

- channel is closed, operation failed. + queue is closed, operation failed.

- - timeout + + timeout

@@ -137,42 +137,42 @@

- - - Template - unbounded_channel<> + + + Template + unbounded_queue<>

-
#include <boost/fiber/unbounded_channel.hpp>
+
#include <boost/fiber/unbounded_queue.hpp>
 
 namespace boost {
 namespace fibers {
 
 template< typename T, typename Allocator = std::allocator< T > >
-class unbounded_channel {
+class unbounded_queue {
 public:
     typedef T   value_type;
 
-    explicit unbounded_channel( Allocator const& alloc = Allocator() ) noexcept;
+    explicit unbounded_queue( Allocator const& alloc = Allocator() ) noexcept;
 
-    unbounded_channel( unbounded_channel const& other) = delete;
-    unbounded_channel & operator=( unbounded_channel const& other) = delete;
+    unbounded_queue( unbounded_queue const& other) = delete;
+    unbounded_queue & operator=( unbounded_queue const& other) = delete;
 
     void close() noexcept;
 
-    channel_op_status push( value_type const& va);
-    channel_op_status push( value_type && va);
+    queue_op_status push( value_type const& va);
+    queue_op_status push( value_type && va);
 
-    channel_op_status pop( value_type & va);
+    queue_op_status pop( value_type & va);
     value_type value_pop();
-    channel_op_status try_pop( value_type & va);
+    queue_op_status try_pop( value_type & va);
     template< typename Rep, typename Period >
-    channel_op_status pop_wait_for(
+    queue_op_status pop_wait_for(
         value_type & va,
         std::chrono::duration< Rep, Period > const& timeout_duration);
     template< typename Clock, typename Duration >
-    channel_op_status pop_wait_until(
+    queue_op_status pop_wait_until(
         value_type & va,
         std::chrono::time_point< Clock, Duration > const& timeout_time);
 };
@@ -180,17 +180,17 @@
 }}
 
- - Constructor + + Constructor
-
explicit unbounded_channel( Allocator const& alloc = Allocator() ) noexcept;
+
explicit unbounded_queue( Allocator const& alloc = Allocator() ) noexcept;
 

Effects:

- Constructs an object of class unbounded_channel. + Constructs an object of class unbounded_queue. Internal nodes are allocated using alloc - C++11-allocators are supported.

@@ -208,9 +208,9 @@

- - - Member + + + Member function close()

@@ -222,7 +222,7 @@

Effects:

- Deactivates the channel. No values can be put after calling this->close(). + Deactivates the queue. No values can be put after calling this->close(). Fibers blocked in this->pop(), this->pop_wait_for() or this->pop_wait_until() will return closed. Fibers blocked in this->value_pop() will receive an exception.

@@ -241,23 +241,23 @@

- - - Member + + + Member function push()

-
channel_op_status push( value_type const& va);
-channel_op_status push( value_type && va);
+
queue_op_status push( value_type const& va);
+queue_op_status push( value_type && va);
 

Effects:

- If channel is closed, returns closed. - Otherwise enqueues the value in the channel, wakes up a fiber blocked + If queue is closed, returns closed. + Otherwise enqueues the value in the queue, wakes up a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() @@ -273,24 +273,24 @@

- - - Member + + + Member function pop()

-
channel_op_status pop( value_type & va);
+
queue_op_status pop( value_type & va);
 

Effects:

- Dequeues a value from the channel. If the channel is empty, the fiber - gets suspended until at least one new item is push()ed (return value success + Dequeues a value from the queue. If the queue is empty, the fiber gets + suspended until at least one new item is push()ed (return value success and va contains dequeued - value) or the channel gets close()d (return value closed). + value) or the queue gets close()d (return value closed).

Throws:

@@ -301,9 +301,9 @@

- - - Member + + + Member function value_pop()

@@ -315,8 +315,8 @@

Effects:

- Dequeues a value from the channel. If the channel is empty, the fiber - gets suspended until at least one new item is push()ed or the channel gets close()d + Dequeues a value from the queue. If the queue is empty, the fiber gets + suspended until at least one new item is push()ed or the queue gets close()d (which throws an exception).

Throws:
@@ -333,22 +333,22 @@

- - - Member + + + Member function try_pop()

-
channel_op_status try_pop( value_type & va);
+
queue_op_status try_pop( value_type & va);
 

Effects:

- If channel is empty, returns empty. - If channel is closed, returns closed. + If queue is empty, returns empty. + If queue is closed, returns closed. Otherwise it returns success and va contains the dequeued value. @@ -362,15 +362,15 @@

- - - Member + + + Member function pop_wait_for()

template< typename Rep, typename Period >
-channel_op_status pop_wait_for(
+queue_op_status pop_wait_for(
     value_type & va,
     std::chrono::duration< Rep, Period > const& timeout_duration)
 
@@ -381,12 +381,12 @@

Accepts std::chrono::duration and internally computes a timeout time as (system time + timeout_duration). - If channel is not empty, immediately dequeues a value from the channel. + If queue is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is push()ed (return value success and va contains dequeued - value), or the channel gets close()d (return value closed), + value), or the queue gets close()d (return value closed), or the system time reaches the computed timeout time (return value timeout).

@@ -399,15 +399,15 @@

- - - Member + + + Member function pop_wait_until()

template< typename Clock, typename Duration >
-channel_op_status pop_wait_until(
+queue_op_status pop_wait_until(
     value_type & va,
     std::chrono::time_point< Clock, Duration > const& timeout_time)
 
@@ -419,12 +419,12 @@ Accepts a std::chrono::time_point< Clock, Duration >. - If channel is not empty, immediately dequeues a value from the channel. + If queue is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is push()ed (return value success and va contains dequeued - value), or the channel gets close()d (return value closed), + value), or the queue gets close()d (return value closed), or the system time reaches the passed time_point (return value timeout).

@@ -437,74 +437,74 @@

- - - Template - bounded_channel<> + + + Template + bounded_queue<>

-
#include <boost/fiber/bounded_channel.hpp>
+
#include <boost/fiber/bounded_queue.hpp>
 
 namespace boost {
 namespace fibers {
 
 template< typename T, typename Allocator = std::allocator< T > >
-class bounded_channel {
+class bounded_queue {
 public:
     typedef T   value_type;
 
-    bounded_channel( std::size_t wm, Allocator const& alloc = Allocator() );
-    bounded_channel( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() );
+    bounded_queue( std::size_t wm, Allocator const& alloc = Allocator() );
+    bounded_queue( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() );
 
-    bounded_channel( bounded_channel const& other) = delete;
-    bounded_channel & operator=( bounded_channel const& other) = delete;
+    bounded_queue( bounded_queue const& other) = delete;
+    bounded_queue & operator=( bounded_queue const& other) = delete;
 
     std::size_t upper_bound() const noexcept;
     std::size_t lower_bound() const noexcept;
 
     void close() noexcept;
 
-    channel_op_status push( value_type const& va);
-    channel_op_status push( value_type && va);
+    queue_op_status push( value_type const& va);
+    queue_op_status push( value_type && va);
     template< typename Rep, typename Period >
-    channel_op_status push_wait_for(
+    queue_op_status push_wait_for(
         value_type const& va,
         std::chrono::duration< Rep, Period > const& timeout_duration);
-    channel_op_status push_wait_for( value_type && va,
+    queue_op_status push_wait_for( value_type && va,
         std::chrono::duration< Rep, Period > const& timeout_duration);
     template< typename Clock, typename Duration >
-    channel_op_status push_wait_until(
+    queue_op_status push_wait_until(
         value_type const& va,
         std::chrono::time_point< Clock, Duration > const& timeout_time);
     template< typename Clock, typename Duration >
-    channel_op_status push_wait_until(
+    queue_op_status push_wait_until(
         value_type && va,
         std::chrono::time_point< Clock, Duration > const& timeout_time);
-    channel_op_status try_push( value_type const& va);
-    channel_op_status try_push( value_type && va);
+    queue_op_status try_push( value_type const& va);
+    queue_op_status try_push( value_type && va);
 
-    channel_op_status pop( value_type & va);
+    queue_op_status pop( value_type & va);
     value_type value_pop();
     template< typename Rep, typename Period >
-    channel_op_status pop_wait_for(
+    queue_op_status pop_wait_for(
         value_type & va,
         std::chrono::duration< Rep, Period > const& timeout_duration);
     template< typename Clock, typename Duration >
-    channel_op_status pop_wait_until(
+    queue_op_status pop_wait_until(
         value_type & va,
         std::chrono::time_point< Clock, Duration > const& timeout_time);
-    channel_op_status try_pop( value_type & va);
+    queue_op_status try_pop( value_type & va);
 };
 
 }}
 
- - Constructor + + Constructor
-
bounded_channel( std::size_t wm, Allocator const& alloc = Allocator() );
-bounded_channel( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() );
+
bounded_queue( std::size_t wm, Allocator const& alloc = Allocator() );
+bounded_queue( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() );
 

@@ -516,12 +516,12 @@

Effects:

- Constructs an object of class bounded_channel. - The constructor with two arguments constructs an object of class bounded_channel with a high-watermark + Constructs an object of class bounded_queue. + The constructor with two arguments constructs an object of class bounded_queue with a high-watermark of hwm and a low-watermark of lwm items. The constructor with one std::size_t argument is effectively the - same as bounded_channel(wm, (wm-1), alloc). + same as bounded_queue(wm, (wm-1), alloc). Internal nodes are allocated using alloc - C++11-allocators are supported.

@@ -536,15 +536,15 @@

Notes:

- Once the number of values in the channel reaches hwm, + Once the number of values in the queue reaches hwm, any call to push(), push_wait_for() or push_wait_until() will block until the number of values - in the channel is at most lwm. + in the queue is at most lwm. That is, if lwm < (hwm-1), - the channel can be in a state in which push(), push_wait_for() or push_wait_until() calls will block (channel is full) - even though the number of values in the channel is less than hwm. + the queue can be in a state in which push(), push_wait_for() or push_wait_until() calls will block (queue is full) + even though the number of values in the queue is less than hwm.

See also:

@@ -556,9 +556,9 @@

- - - Member + + + Member function upper_bound()

@@ -581,9 +581,9 @@

- - - Member + + + Member function lower_bound()

@@ -606,9 +606,9 @@

- - - Member + + + Member function close()

@@ -620,7 +620,7 @@

Effects:

- Deactivates the channel. No values can be put after calling this->close(). + Deactivates the queue. No values can be put after calling this->close(). Fibers blocked in this->pop(), this->pop_wait_for() or this->pop_wait_until() will return closed. Fibers blocked in this->value_pop() will receive an exception.

@@ -639,28 +639,32 @@

- - - Member - function push() + + + Member function + push()

-
channel_op_status push( value_type const& va);
-channel_op_status push( value_type && va);
+
queue_op_status push( value_type const& va);
+queue_op_status push( value_type && va);
 

Effects:

- If channel is closed, returns closed. - If channel is not full, enqueues the value in the channel, wakes up - a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. + If queue is closed, returns closed. + If queue is not full, enqueues the value in the queue, wakes up a fiber + blocked on this->pop(), + this->value_pop(), + this->pop_wait_for() + or this->pop_wait_until() + and returns success. Otherwise the calling fiber is suspended until the number of values - in the channel drops to lwm + in the queue drops to lwm (return value success)or - the channel is close()d (return value closed). + the queue is close()d (return value closed).

Throws:

@@ -671,20 +675,20 @@

- - - Member + + + Member function push_wait_for()

template< typename Rep, typename Period >
-channel_op_status push_wait_for(
+queue_op_status push_wait_for(
     value_type const& va,
     std::chrono::duration< Rep, Period > const& timeout_duration);
 
 template< typename Rep, typename Period >
-channel_op_status push_wait_for(
+queue_op_status push_wait_for(
     value_type && va,
     std::chrono::duration< Rep, Period > const& timeout_duration);
 
@@ -695,13 +699,17 @@

Accepts std::chrono::duration and internally computes a time_point as (system time + timeout_duration). - If channel is closed, returns closed. - If channel is not full, enqueues the value in the channel, wakes up - a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. + If queue is closed, returns closed. + If queue is not full, enqueues the value in the queue, wakes up a fiber + blocked on this->pop(), + this->value_pop(), + this->pop_wait_for() + or this->pop_wait_until() + and returns success. Otherwise the calling fiber is suspended until the number of values - in the channel drops to lwm + in the queue drops to lwm (return value success), - the channel is close()d (return value closed), + the queue is close()d (return value closed), or the system time reaches the computed time_point (return value timeout).

Throws:
@@ -713,20 +721,20 @@

- - - Member + + + Member function push_wait_until()

template< typename Clock, typename Duration >
-channel_op_status push_wait_until(
+queue_op_status push_wait_until(
     value_type const& va,
     std::chrono::time_point< Clock, Duration > const& timeout_time);
 
 template< typename Clock, typename Duration >
-channel_op_status push_wait_until(
+queue_op_status push_wait_until(
     value_type && va,
     std::chrono::time_point< Clock, Duration > const& timeout_time);
 
@@ -736,16 +744,12 @@
Effects:

Accepts an absolute timeout_time - in any supported time_point type. If channel is closed, returns closed. If channel is not full, enqueues - the value in the channel, wakes up a fiber blocked on this->pop(), - this->value_pop(), - this->pop_wait_for() - or this->pop_wait_until() - and returns success. + in any supported time_point type. If queue is closed, returns closed. If queue is not full, enqueues + the value in the queue, wakes up a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() or this->pop_wait_until() and returns success. Otherwise the calling fiber is suspended until the number of values - in the channel drops to lwm + in the queue drops to lwm (return value success), - the channel is close()d (return value closed), + the queue is close()d (return value closed), or the system time reaches the passed time_point (return value timeout).

Throws:
@@ -757,24 +761,24 @@

- - - Member + + + Member function try_push()

-
channel_op_status try_push( value_type const& va);
-channel_op_status try_push( value_type && va);
+
queue_op_status try_push( value_type const& va);
+queue_op_status try_push( value_type && va);
 

Effects:

- If channel is full, returns full. - If channel is closed, returns closed. - Otherwise enqueues the value in the channel, wakes up a fiber blocked + If queue is full, returns full. + If queue is closed, returns closed. + Otherwise enqueues the value in the queue, wakes up a fiber blocked on this->pop(), this->value_pop(), this->pop_wait_for() @@ -790,25 +794,25 @@

- - - Member - function pop() + + + Member function + pop()

-
channel_op_status pop( value_type & va);
+
queue_op_status pop( value_type & va);
 

Effects:

- Dequeues a value from the channel. If the channel is empty, the fiber - gets suspended until at least one new item is push()ed (return value success + Dequeues a value from the queue. If the queue is empty, the fiber gets + suspended until at least one new item is push()ed (return value success and va contains dequeued - value) or the channel gets close()d (return value closed). - Once the number of items remaining in the channel drops to lwm, any fibers blocked on push(), + value) or the queue gets close()d (return value closed). + Once the number of items remaining in the queue drops to lwm, any fibers blocked on push(), push_wait_for() or push_wait_until() may resume.

@@ -821,9 +825,9 @@

- - - Member + + + Member function value_pop()

@@ -835,10 +839,10 @@

Effects:

- Dequeues a value from the channel. If the channel is empty, the fiber - gets suspended until at least one new item is push()ed or the channel gets close()d + Dequeues a value from the queue. If the queue is empty, the fiber gets + suspended until at least one new item is push()ed or the queue gets close()d (which throws an exception). Once the number of items remaining in - the channel drops to lwm, + the queue drops to lwm, any fibers blocked on push(), push_wait_for() or push_wait_until() may resume.

Throws:
@@ -855,25 +859,25 @@

- - - Member + + + Member function try_pop()

-
channel_op_status try_pop( value_type & va);
+
queue_op_status try_pop( value_type & va);
 

Effects:

- If channel is empty, returns empty. - If channel is closed, returns closed. + If queue is empty, returns empty. + If queue is closed, returns closed. Otherwise it returns success and va contains the - dequeued value. Once the number of items remaining in the channel drops + dequeued value. Once the number of items remaining in the queue drops to lwm, any fibers blocked on push(), push_wait_for() @@ -888,15 +892,15 @@

- - - Member + + + Member function pop_wait_for()

template< typename Rep, typename Period >
-channel_op_status pop_wait_for(
+queue_op_status pop_wait_for(
     value_type & va,
     std::chrono::duration< Rep, Period > const& timeout_duration)
 
@@ -907,15 +911,15 @@

Accepts std::chrono::duration and internally computes a timeout time as (system time + timeout_duration). - If channel is not empty, immediately dequeues a value from the channel. + If queue is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is push()ed (return value success and va contains dequeued - value), or the channel gets close()d (return value closed), + value), or the queue gets close()d (return value closed), or the system time reaches the computed timeout time (return value timeout). Once the - number of items remaining in the channel drops to lwm, + number of items remaining in the queue drops to lwm, any fibers blocked on push(), push_wait_for() or push_wait_until() may resume.

Throws:
@@ -927,15 +931,15 @@

- - - Member + + + Member function pop_wait_until()

template< typename Clock, typename Duration >
-channel_op_status pop_wait_until(
+queue_op_status pop_wait_until(
     value_type & va,
     std::chrono::time_point< Clock, Duration > const& timeout_time)
 
@@ -947,15 +951,15 @@ Accepts a std::chrono::time_point< Clock, Duration >. - If channel is not empty, immediately dequeues a value from the channel. + If queue is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is push()ed (return value success and va contains dequeued - value), or the channel gets close()d (return value closed), + value), or the queue gets close()d (return value closed), or the system time reaches the passed time_point (return value timeout). - Once the number of items remaining in the channel drops to lwm, any fibers blocked on push(), + Once the number of items remaining in the queue drops to lwm, any fibers blocked on push(), push_wait_for() or push_wait_until() may resume.

diff --git a/doc/html/fiber/when_any/when_all_functionality/wait_all__collecting_all_exceptions.html b/doc/html/fiber/when_any/when_all_functionality/wait_all__collecting_all_exceptions.html index 8affe173..227cd0d4 100644 --- a/doc/html/fiber/when_any/when_all_functionality/wait_all__collecting_all_exceptions.html +++ b/doc/html/fiber/when_any/when_all_functionality/wait_all__collecting_all_exceptions.html @@ -45,14 +45,14 @@ results.reserve( count); exception_list exceptions("wait_all_collect_errors() exceptions"); - // get channel + // get queue std::shared_ptr< - boost::fibers::unbounded_channel< future_t > > channel( + boost::fibers::unbounded_queue< future_t > > queue( wait_all_until_error_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ) ); // fill results and/or exceptions vectors future_t future; - while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { + while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { std::exception_ptr exp = future.get_exception_ptr(); if ( ! exp) { results.push_back( future.get() ); diff --git a/doc/html/fiber/when_any/when_all_functionality/when_all__return_values.html b/doc/html/fiber/when_any/when_all_functionality/when_all__return_values.html index 8b5100fa..9f1a2352 100644 --- a/doc/html/fiber/when_any/when_all_functionality/when_all__return_values.html +++ b/doc/html/fiber/when_any/when_all_functionality/when_all__return_values.html @@ -30,8 +30,8 @@

As soon as we want to collect return values from all the task functions, we can see right away how to reuse wait_first_value()'s - channel<T> for the purpose. All we have to do is avoid closing it - after the first value! + queue<T> for the purpose. All we have to do is avoid closing it after + the first value!

But in fact, collecting multiple values raises an interesting question: @@ -40,7 +40,7 @@ available?

- Fortunately we can present both APIs. Let's define wait_all_values_source() to return shared_ptr<unbounded_channel<T>>.[6] + Fortunately we can present both APIs. Let's define wait_all_values_source() to return shared_ptr<unbounded_queue<T>>.[6]

Given wait_all_values_source(), it's straightforward to implement wait_all_values(): @@ -56,13 +56,13 @@ vector_t results; results.reserve( count); - // get channel - std::shared_ptr< boost::fibers::unbounded_channel< return_t > > channel = + // get queue + std::shared_ptr< boost::fibers::unbounded_queue< return_t > > queue = wait_all_values_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // fill results vector return_t value; - while ( boost::fibers::channel_op_status::success == channel->pop(value) ) { + while ( boost::fibers::queue_op_status::success == queue->pop(value) ) { results.push_back( value); } // return vector to caller @@ -86,83 +86,83 @@

As you can see from the loop in wait_all_values(), instead of requiring its caller to count - values, we define wait_all_values_source() to unbounded_channel::close() the - channel when done. But how do we do that? Each producer fiber is independent. - It has no idea whether it is the last one to unbounded_channel::push() a + values, we define wait_all_values_source() to unbounded_queue::close() the + queue when done. But how do we do that? Each producer fiber is independent. + It has no idea whether it is the last one to unbounded_queue::push() a value.

- We can address that problem with a counting - façade for the unbounded_channel<>. In fact, our façade need only - support the producer end of the channel. + We can address that problem with a counting façade + for the unbounded_queue<>. In fact, our façade need only + support the producer end of the queue.

-
// Introduce a channel facade that closes the channel once a specific number
+
// Introduce a queue facade that closes the queue once a specific number
 // of items has been pushed. This allows an arbitrary consumer to read until
 // 'closed' without itself having to count items.
 template< typename T >
-class nchannel {
+class nqueue {
 public:
-    nchannel( std::shared_ptr< boost::fibers::unbounded_channel< T > > cp,
+    nqueue( std::shared_ptr< boost::fibers::unbounded_queue< T > > cp,
               std::size_t lm):
-        channel_( cp),
+        queue_( cp),
         limit_( lm) {
-        assert(channel_);
+        assert(queue_);
         if ( 0 == limit_) {
-            channel_->close();
+            queue_->close();
         }
     }
 
-    boost::fibers::channel_op_status push( T && va) {
-        boost::fibers::channel_op_status ok =
-            channel_->push( std::forward< T >( va) );
-        if ( ok == boost::fibers::channel_op_status::success &&
+    boost::fibers::queue_op_status push( T && va) {
+        boost::fibers::queue_op_status ok =
+            queue_->push( std::forward< T >( va) );
+        if ( ok == boost::fibers::queue_op_status::success &&
              --limit_ == 0) {
-            // after the 'limit_'th successful push, close the channel
-            channel_->close();
+            // after the 'limit_'th successful push, close the queue
+            queue_->close();
         }
         return ok;
     }
 
 private:
-    std::shared_ptr< boost::fibers::unbounded_channel< T > >    channel_;
+    std::shared_ptr< boost::fibers::unbounded_queue< T > >    queue_;
     std::size_t                                                 limit_;
 };
 

- Armed with nchannel<>, we can implement wait_all_values_source(). + Armed with nqueue<>, we can implement wait_all_values_source(). It starts just like wait_first_value(). The difference is that we wrap - the unbounded_channel<T> - with an nchannel<T> + the unbounded_queue<T> + with an nqueue<T> to pass to the producer fibers.

- Then, of course, instead of popping the first value, closing the channel - and returning it, we simply return the shared_ptr<unbounded_channel<T>>. + Then, of course, instead of popping the first value, closing the queue + and returning it, we simply return the shared_ptr<unbounded_queue<T>>.

-
// Return a shared_ptr<unbounded_channel<T>> from which the caller can
+
// Return a shared_ptr<unbounded_queue<T>> from which the caller can
 // retrieve each new result as it arrives, until 'closed'.
 template< typename Fn, typename ... Fns >
-std::shared_ptr< boost::fibers::unbounded_channel< typename std::result_of< Fn() >::type > >
+std::shared_ptr< boost::fibers::unbounded_queue< typename std::result_of< Fn() >::type > >
 wait_all_values_source( Fn && function, Fns && ... functions) {
     std::size_t count( 1 + sizeof ... ( functions) );
     typedef typename std::result_of< Fn() >::type return_t;
-    typedef boost::fibers::unbounded_channel< return_t > channel_t;
-    // make the channel
-    auto channelp( std::make_shared< channel_t >() );
-    // and make an nchannel facade to close it after 'count' items
-    auto ncp( std::make_shared< nchannel< return_t > >( channelp, count) );
-    // pass that nchannel facade to all the relevant fibers
+    typedef boost::fibers::unbounded_queue< return_t > queue_t;
+    // make the queue
+    auto queuep( std::make_shared< queue_t >() );
+    // and make an nqueue facade to close it after 'count' items
+    auto ncp( std::make_shared< nqueue< return_t > >( queuep, count) );
+    // pass that nqueue facade to all the relevant fibers
     wait_all_values_impl< return_t >( ncp,
                                       std::forward< Fn >( function),
                                       std::forward< Fns >( functions) ... );
-    // then return the channel for consumer
-    return channelp;
+    // then return the queue for consumer
+    return queuep;
 }
 

@@ -172,13 +172,13 @@

-
std::shared_ptr< boost::fibers::unbounded_channel< std::string > > channel =
+
std::shared_ptr< boost::fibers::unbounded_queue< std::string > > queue =
     wait_all_values_source(
             [](){ return sleeper("wavs_third",  150); },
             [](){ return sleeper("wavs_second", 100); },
             [](){ return sleeper("wavs_first",   50); });
 std::string value;
-while ( boost::fibers::channel_op_status::success == channel->pop(value) ) {
+while ( boost::fibers::queue_op_status::success == queue->pop(value) ) {
     std::cout << "wait_all_values_source() => '" << value
               << "'" << std::endl;
 }
@@ -187,15 +187,15 @@
         

wait_all_values_impl() really is just like wait_first_value_impl() - except for the use of nchannel<T> rather than unbounded_channel<T>: + except for the use of nqueue<T> rather than unbounded_queue<T>:

template< typename T, typename Fn >
-void wait_all_values_impl( std::shared_ptr< nchannel< T > > channel,
+void wait_all_values_impl( std::shared_ptr< nqueue< T > > queue,
                            Fn && function) {
-    boost::fibers::fiber( [channel, function](){
-                              channel->push(function());
+    boost::fibers::fiber( [queue, function](){
+                              queue->push(function());
                           }).detach();
 }
 
@@ -204,8 +204,8 @@


[6] - We could have used either bounded_channel<> or - unbounded_channel<>. We chose unbounded_channel<> + We could have used either bounded_queue<> or + unbounded_queue<>. We chose unbounded_queue<> on the assumption that its simpler semantics imply a cheaper implementation.

diff --git a/doc/html/fiber/when_any/when_all_functionality/when_all_until_first_exception.html b/doc/html/fiber/when_any/when_all_functionality/when_all_until_first_exception.html index 45d45e31..0f46b749 100644 --- a/doc/html/fiber/when_any/when_all_functionality/when_all_until_first_exception.html +++ b/doc/html/fiber/when_any/when_all_functionality/when_all_until_first_exception.html @@ -49,14 +49,14 @@ vector_t results; results.reserve( count); - // get channel + // get queue std::shared_ptr< - boost::fibers::unbounded_channel< future_t > > channel( + boost::fibers::unbounded_queue< future_t > > queue( wait_all_until_error_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ) ); // fill results vector future_t future; - while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { + while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { results.push_back( future.get() ); } // return vector to caller @@ -90,37 +90,37 @@ T > and call its get() method. It would, of course, be possible to return a façade over the consumer - end of the channel that would implicitly perform the get() and return a simple T + end of the queue that would implicitly perform the get() and return a simple T (or throw).

The implementation is just as you would expect. Notice, however, that we - can reuse wait_first_outcome_impl(), passing the nchannel<T> rather than unbounded_channel<T>. + can reuse wait_first_outcome_impl(), passing the nqueue<T> rather than unbounded_queue<T>.

-
// Return a shared_ptr<unbounded_channel<future<T>>> from which the caller can
+
// Return a shared_ptr<unbounded_queue<future<T>>> from which the caller can
 // get() each new result as it arrives, until 'closed'.
 template< typename Fn, typename ... Fns >
 std::shared_ptr<
-    boost::fibers::unbounded_channel<
+    boost::fibers::unbounded_queue<
         boost::fibers::future<
             typename std::result_of< Fn() >::type > > >
 wait_all_until_error_source( Fn && function, Fns && ... functions) {
     std::size_t count( 1 + sizeof ... ( functions) );
     typedef typename std::result_of< Fn() >::type return_t;
     typedef boost::fibers::future< return_t > future_t;
-    typedef boost::fibers::unbounded_channel< future_t > channel_t;
-    // make the channel
-    auto channelp( std::make_shared< channel_t >() );
-    // and make an nchannel facade to close it after 'count' items
-    auto ncp( std::make_shared< nchannel< future_t > >( channelp, count) );
-    // pass that nchannel facade to all the relevant fibers
+    typedef boost::fibers::unbounded_queue< future_t > queue_t;
+    // make the queue
+    auto queuep( std::make_shared< queue_t >() );
+    // and make an nqueue facade to close it after 'count' items
+    auto ncp( std::make_shared< nqueue< future_t > >( queuep, count) );
+    // pass that nqueue facade to all the relevant fibers
     wait_first_outcome_impl< return_t >( ncp,
                                          std::forward< Fn >( function),
                                          std::forward< Fns >( functions) ... );
-    // then return the channel for consumer
-    return channelp;
+    // then return the queue for consumer
+    return queuep;
 }
 

@@ -131,13 +131,13 @@

typedef boost::fibers::future< std::string > future_t;
-std::shared_ptr< boost::fibers::unbounded_channel< future_t > > channel =
+std::shared_ptr< boost::fibers::unbounded_queue< future_t > > queue =
     wait_all_until_error_source(
             [](){ return sleeper("wauess_third",  150); },
             [](){ return sleeper("wauess_second", 100); },
             [](){ return sleeper("wauess_first",   50); });
 future_t future;
-while ( boost::fibers::channel_op_status::success == channel->pop( future) ) {
+while ( boost::fibers::queue_op_status::success == queue->pop( future) ) {
     std::string value( future.get() );
     std::cout << "wait_all_until_error_source(success) => '" << value
               << "'" << std::endl;
diff --git a/doc/html/fiber/when_any/when_any/when_any__heterogeneous_types.html b/doc/html/fiber/when_any/when_any/when_any__heterogeneous_types.html
index e7274d0e..09a5acfc 100644
--- a/doc/html/fiber/when_any/when_any/when_any__heterogeneous_types.html
+++ b/doc/html/fiber/when_any/when_any/when_any__heterogeneous_types.html
@@ -36,7 +36,7 @@
           To keep the example simple, we'll revert to pretending that none of them
           can throw an exception. That makes wait_first_value_het() strongly resemble wait_first_value().
           We can actually reuse wait_first_value_impl(),
-          merely passing boost::variant<T0, T1, ...> as the channel's value type rather
+          merely passing boost::variant<T0, T1, ...> as the queue's value type rather
           than the common T!
         

@@ -53,17 +53,17 @@ template< typename ... Fns > boost::variant< typename std::result_of< Fns() >::type ... > wait_first_value_het( Fns && ... functions) { - // Use unbounded_channel<boost::variant<T1, T2, ...>>; see remarks above. + // Use unbounded_queue<boost::variant<T1, T2, ...>>; see remarks above. typedef boost::variant< typename std::result_of< Fns() >::type ... > return_t; - typedef boost::fibers::unbounded_channel< return_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< return_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_value_impl< return_t >( channelp, + wait_first_value_impl< return_t >( queuep, std::forward< Fns >( functions) ... ); // retrieve the first value - return_t value( channelp->value_pop() ); - // close the channel: no subsequent push() has to succeed - channelp->close(); + return_t value( queuep->value_pop() ); + // close the queue: no subsequent push() has to succeed + queuep->close(); return value; }

diff --git a/doc/html/fiber/when_any/when_any/when_any__produce_first_outcome__whether_result_or_exception.html b/doc/html/fiber/when_any/when_any/when_any__produce_first_outcome__whether_result_or_exception.html index 3db7db5b..5bf818bd 100644 --- a/doc/html/fiber/when_any/when_any/when_any__produce_first_outcome__whether_result_or_exception.html +++ b/doc/html/fiber/when_any/when_any/when_any__produce_first_outcome__whether_result_or_exception.html @@ -36,7 +36,7 @@

Let's at least ensure that such an exception would propagate to the fiber awaiting the first result. We can use future<> to transport - either a return value or an exception. Therefore, we will change wait_first_value()'s unbounded_channel<> to + either a return value or an exception. Therefore, we will change wait_first_value()'s unbounded_queue<> to hold future< T > items instead of simply T. @@ -52,21 +52,21 @@

template< typename Fn, typename ... Fns >
 typename std::result_of< Fn() >::type
 wait_first_outcome( Fn && function, Fns && ... functions) {
-    // In this case, the value we pass through the channel is actually a
+    // In this case, the value we pass through the queue is actually a
     // future -- which is already ready. future can carry either a value or an
     // exception.
     typedef typename std::result_of< Fn() >::type return_t;
     typedef boost::fibers::future< return_t > future_t;
-    typedef boost::fibers::unbounded_channel< future_t > channel_t;
-    auto channelp(std::make_shared< channel_t >() );
+    typedef boost::fibers::unbounded_queue< future_t > queue_t;
+    auto queuep(std::make_shared< queue_t >() );
     // launch all the relevant fibers
-    wait_first_outcome_impl< return_t >( channelp,
+    wait_first_outcome_impl< return_t >( queuep,
                                          std::forward< Fn >( function),
                                          std::forward< Fns >( functions) ... );
     // retrieve the first future
-    future_t future( channelp->value_pop() );
-    // close the channel: no subsequent push() has to succeed
-    channelp->close();
+    future_t future( queuep->value_pop() );
+    // close the queue: no subsequent push() has to succeed
+    queuep->close();
     // either return value or throw exception
     return future.get();
 }
@@ -75,17 +75,17 @@
         

So far so good — but there's a timing issue. How should we obtain the future<> - to unbounded_channel::push() on the channel? + to unbounded_queue::push() on the queue?

We could call fibers::async(). That would certainly produce a future<> for the task function. The trouble is that it would return too quickly! We only want future<> - items for completed tasks on our unbounded_channel<>. In fact, we only want the future<> + items for completed tasks on our unbounded_queue<>. In fact, we only want the future<> for the one that completes first. If each fiber launched by wait_first_outcome() were to push() - the result of calling async(), the channel would only ever report the + the result of calling async(), the queue would only ever report the result of the leftmost task item — not the one that completes most quickly.

@@ -93,7 +93,7 @@ Calling future::get() on the future returned by async() wouldn't be right. You can only call get() once per future<> instance! And if there were an exception, it would be rethrown inside the helper fiber at the producer - end of the channel, rather than propagated to the consumer end. + end of the queue, rather than propagated to the consumer end.

We could call future::wait(). That would block the helper fiber @@ -106,24 +106,24 @@ While one naturally thinks of passing a packaged_task<> to a new fiber — that is, in fact, what async() does — in this case, we're already running in the helper fiber at the producer - end of the channel! We can simply call the packaged_task<>. + end of the queue! We can simply call the packaged_task<>. On return from that call, the task function has completed, meaning that the future<> obtained from the packaged_task<> is certain to be ready. At that - point we can simply push() it to the channel. + point we can simply push() it to the queue.

-
template< typename T, typename CHANNELP, typename Fn >
-void wait_first_outcome_impl( CHANNELP channel, Fn && function) {
+
template< typename T, typename QUEUEP, typename Fn >
+void wait_first_outcome_impl( QUEUEP queue, Fn && function) {
     boost::fibers::fiber(
         // Use std::bind() here for C++11 compatibility. C++11 lambda capture
         // can't move a move-only Fn type, but bind() can. Let bind() move the
-        // channel pointer and the function into the bound object, passing
+        // queue pointer and the function into the bound object, passing
         // references into the lambda.
         std::bind(
-            []( CHANNELP & channel,
+            []( QUEUEP & queue,
                 typename std::decay< Fn >::type & function) {
                 // Instantiate a packaged_task to capture any exception thrown by
                 // function.
@@ -132,11 +132,11 @@
                 // function() to have completed BEFORE we push the future.
                 task();
                 // Pass the corresponding future to consumer. Ignore
-                // channel_op_status returned by push(): might be closed; we
+                // queue_op_status returned by push(): might be closed; we
                 // simply don't care.
-                channel->push( task.get_future() );
+                queue->push( task.get_future() );
             },
-            channel,
+            queue,
             std::forward< Fn >( function)
         )).detach();
 }
diff --git a/doc/html/fiber/when_any/when_any/when_any__produce_first_success.html b/doc/html/fiber/when_any/when_any/when_any__produce_first_success.html
index 4eff7844..38190183 100644
--- a/doc/html/fiber/when_any/when_any/when_any__produce_first_success.html
+++ b/doc/html/fiber/when_any/when_any/when_any__produce_first_success.html
@@ -40,7 +40,7 @@
           first!
         

- Given the unbounded_channel< future< T > > + Given the unbounded_queue< future< T > > we already constructed for wait_first_outcome(), though, we can readily recast the interface function to deliver the first successful result.

@@ -95,10 +95,10 @@ Now we can build wait_first_success(), using wait_first_outcome_impl().

- Instead of retrieving only the first future<> from the channel, we must now loop + Instead of retrieving only the first future<> from the queue, we must now loop over future<> items. Of course we must limit that iteration! If we launch only count producer fibers, the (count+1)st -unbounded_channel::pop() call +unbounded_queue::pop() call would block forever.

@@ -113,7 +113,7 @@ If the std::exception_ptr is not nullptr, though, we collect it into our pending exception_list - and loop back for the next future<> from the channel. + and loop back for the next future<> from the queue.

If we fall out of the loop — if every single task fiber threw an exception @@ -127,15 +127,15 @@ typename std::result_of< Fn() >::type wait_first_success( Fn && function, Fns && ... functions) { std::size_t count( 1 + sizeof ... ( functions) ); - // In this case, the value we pass through the channel is actually a + // In this case, the value we pass through the queue is actually a // future -- which is already ready. future can carry either a value or an // exception. typedef typename std::result_of< typename std::decay< Fn >::type() >::type return_t; typedef boost::fibers::future< return_t > future_t; - typedef boost::fibers::unbounded_channel< future_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< future_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_outcome_impl< return_t >( channelp, + wait_first_outcome_impl< return_t >( queuep, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // instantiate exception_list, just in case @@ -143,13 +143,13 @@ // retrieve up to 'count' results -- but stop there! for ( std::size_t i = 0; i < count; ++i) { // retrieve the next future - future_t future( channelp->value_pop() ); + future_t future( queuep->value_pop() ); // retrieve exception_ptr if any std::exception_ptr error( future.get_exception_ptr() ); // if no error, then yay, return value if ( ! error) { - // close the channel: no subsequent push() has to succeed - channelp->close(); + // close the queue: no subsequent push() has to succeed + queuep->close(); // show caller the value we got return future.get(); } diff --git a/doc/html/fiber/when_any/when_any/when_any__return_value.html b/doc/html/fiber/when_any/when_any/when_any__return_value.html index 5f237130..14c873a6 100644 --- a/doc/html/fiber/when_any/when_any/when_any__return_value.html +++ b/doc/html/fiber/when_any/when_any/when_any__return_value.html @@ -35,8 +35,8 @@

One tactic would be to adapt our Done class to store the first of the return values, rather than a simple bool. - However, we choose instead to use a unbounded_channel<>. - We'll only need to enqueue the first value, so we'll unbounded_channel::close() it + However, we choose instead to use a unbounded_queue<>. + We'll only need to enqueue the first value, so we'll unbounded_queue::close() it once we've retrieved that value. Subsequent push() calls will return closed.

@@ -49,16 +49,16 @@ typename std::result_of< Fn() >::type wait_first_value( Fn && function, Fns && ... functions) { typedef typename std::result_of< Fn() >::type return_t; - typedef boost::fibers::unbounded_channel< return_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< return_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_value_impl< return_t >( channelp, + wait_first_value_impl< return_t >( queuep, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // retrieve the first value - return_t value( channelp->value_pop() ); - // close the channel: no subsequent push() has to succeed - channelp->close(); + return_t value( queuep->value_pop() ); + // close the queue: no subsequent push() has to succeed + queuep->close(); return value; }

@@ -70,12 +70,12 @@

template< typename T, typename Fn >
-void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_channel< T > > channel,
+void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_queue< T > > queue,
                             Fn && function) {
-    boost::fibers::fiber( [channel, function](){
-                              // Ignore channel_op_status returned by push():
+    boost::fibers::fiber( [queue, function](){
+                              // Ignore queue_op_status returned by push():
                               // might be closed; we simply don't care.
-                              channel->push( function() );
+                              queue->push( function() );
                           }).detach();
 }
 
diff --git a/doc/html/fiber_HTML.manifest b/doc/html/fiber_HTML.manifest index 3553621a..eacfad81 100644 --- a/doc/html/fiber_HTML.manifest +++ b/doc/html/fiber_HTML.manifest @@ -10,7 +10,7 @@ fiber/synchronization.html fiber/synchronization/mutex_types.html fiber/synchronization/conditions.html fiber/synchronization/barriers.html -fiber/synchronization/channels.html +fiber/synchronization/queues.html fiber/synchronization/futures.html fiber/synchronization/futures/future.html fiber/synchronization/futures/promise.html diff --git a/doc/html/index.html b/doc/html/index.html index 15abfddd..d8f83089 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -51,7 +51,7 @@
Mutex Types
Condition Variables
Barriers
-
Channels
+
Queues
Futures
Future
@@ -133,7 +133,7 @@
- +

Last revised: October 10, 2016 at 09:16:00 GMT

Last revised: October 13, 2016 at 17:19:25 GMT


diff --git a/doc/channel.qbk b/doc/queue.qbk similarity index 52% rename from doc/channel.qbk rename to doc/queue.qbk index 1f3437d9..b75a4a3c 100644 --- a/doc/channel.qbk +++ b/doc/queue.qbk @@ -5,40 +5,40 @@ http://www.boost.org/LICENSE_1_0.txt ] -[section:channels Channels] +[section:queues Queues] -__boost_fiber__ provides a bounded and a unbounded channel suitable to +__boost_fiber__ provides a bounded and a unbounded queue suitable to synchonize fibers via message passing. - typedef boost::fibers::unbounded_channel< int > channel_t; + typedef boost::fibers::unbounded_queue< int > queue_t; - void send( channel_t & channel) { + void send( queue_t & queue) { for ( int i = 0; i < 5; ++i) { - channel.push( i); + queue.push( i); } - channel.close(); + queue.close(); } - void recv( channel_t & channel) { + void recv( queue_t & queue) { int i; - while ( boost::fibers::channel_op_status::success == channel.pop(i) ) { + while ( boost::fibers::queue_op_status::success == queue.pop(i) ) { std::cout << "received " << i << std::endl; } } - channel_t channel; - boost::fibers::fiber f1( std::bind( send, ref( channel) ) ); - boost::fibers::fiber f2( std::bind( recv, ref( channel) ) ); + queue_t queue; + boost::fibers::fiber f1( std::bind( send, ref( queue) ) ); + boost::fibers::fiber f2( std::bind( recv, ref( queue) ) ); f1.join(); f2.join(); -[#class_channel_op_status] -[heading Enumeration `channel_op_status`] +[#class_queue_op_status] +[heading Enumeration `queue_op_status`] -channel operations return the state of the channel. +queue operations return the state of the queue. - enum class channel_op_status { + enum class queue_op_status { success, empty, full, @@ -53,17 +53,17 @@ channel operations return the state of the channel. [heading `empty`] [variablelist -[[Effects:] [channel is empty, operation failed.]] +[[Effects:] [queue is empty, operation failed.]] ] [heading `full`] [variablelist -[[Effects:] [channel is full, operation failed.]] +[[Effects:] [queue is full, operation failed.]] ] [heading `closed`] [variablelist -[[Effects:] [channel is closed, operation failed.]] +[[Effects:] [queue is closed, operation failed.]] ] [heading `timeout`] @@ -71,37 +71,37 @@ channel operations return the state of the channel. [[Effects:] [The operation did not become ready before specified timeout elapsed.]] ] -[template_heading unbounded_channel] +[template_heading unbounded_queue] - #include + #include namespace boost { namespace fibers { template< typename T, typename __Allocator__ = __allocator__ > - class unbounded_channel { + class unbounded_queue { public: typedef T value_type; - explicit unbounded_channel( __Allocator__ const& alloc = Allocator() ) noexcept; + explicit unbounded_queue( __Allocator__ const& alloc = Allocator() ) noexcept; - unbounded_channel( unbounded_channel const& other) = delete; - unbounded_channel & operator=( unbounded_channel const& other) = delete; + unbounded_queue( unbounded_queue const& other) = delete; + unbounded_queue & operator=( unbounded_queue const& other) = delete; void close() noexcept; - channel_op_status push( value_type const& va); - channel_op_status push( value_type && va); + queue_op_status push( value_type const& va); + queue_op_status push( value_type && va); - channel_op_status pop( value_type & va); + queue_op_status pop( value_type & va); value_type value_pop(); - channel_op_status try_pop( value_type & va); + queue_op_status try_pop( value_type & va); template< typename Rep, typename Period > - channel_op_status pop_wait_for( + queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Clock, typename Duration > - channel_op_status pop_wait_until( + queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time); }; @@ -110,22 +110,22 @@ channel operations return the state of the channel. [heading Constructor] - explicit unbounded_channel( __Allocator__ const& alloc = Allocator() ) noexcept; + explicit unbounded_queue( __Allocator__ const& alloc = Allocator() ) noexcept; [variablelist -[[Effects:] [Constructs an object of class `unbounded_channel`. +[[Effects:] [Constructs an object of class `unbounded_queue`. Internal nodes are allocated using `alloc` - C++11-allocators are supported.]] [[Throws:] [Nothing.]] [[See also:] [__Allocator__ concept, __allocator__]] ] -[template xchannel_close[cls] +[template xqueue_close[cls] [member_heading [cls]..close] void close() noexcept; [variablelist -[[Effects:] [Deactivates the channel. No values can be put after calling +[[Effects:] [Deactivates the queue. No values can be put after calling `this->close()`. Fibers blocked in `this->pop()`, `this->pop_wait_for()` or `this->pop_wait_until()` will return `closed`. Fibers blocked in `this->value_pop()` will receive an exception.]] @@ -134,191 +134,191 @@ or `this->pop_wait_until()` will return `closed`. Fibers blocked in that no more values will arrive.]] ] ] -[xchannel_close unbounded_channel] +[xqueue_close unbounded_queue] -[template xchannel_push_effects[enqueues] If channel is closed, returns -`closed`. [enqueues] the value in the channel, wakes up a fiber +[template xqueue_push_effects[enqueues] If queue is closed, returns +`closed`. [enqueues] the value in the queue, wakes up a fiber blocked on `this->pop()`, `this->value_pop()`, `this->pop_wait_for()` or `this->pop_wait_until()` and returns `success`.] -[member_heading unbounded_channel..push] +[member_heading unbounded_queue..push] - channel_op_status push( value_type const& va); - channel_op_status push( value_type && va); + queue_op_status push( value_type const& va); + queue_op_status push( value_type && va); [variablelist -[[Effects:] [[xchannel_push_effects Otherwise enqueues]]] +[[Effects:] [[xqueue_push_effects Otherwise enqueues]]] [[Throws:] [Exceptions thrown by memory allocation and copying or moving `va`.]] ] -[template xchannel_pop[cls unblocking] +[template xqueue_pop[cls unblocking] [member_heading [cls]..pop] - channel_op_status pop( value_type & va); + queue_op_status pop( value_type & va); [variablelist -[[Effects:] [Dequeues a value from the channel. If the channel is empty, the +[[Effects:] [Dequeues a value from the queue. If the queue is empty, the fiber gets suspended until at least one new item is `push()`ed (return value -`success` and `va` contains dequeued value) or the channel gets `close()`d +`success` and `va` contains dequeued value) or the queue gets `close()`d (return value `closed`)[unblocking]]] [[Throws:] [Nothing]] ] ] -[xchannel_pop unbounded_channel .] +[xqueue_pop unbounded_queue .] -[template xchannel_value_pop[cls unblocking] +[template xqueue_value_pop[cls unblocking] [member_heading [cls]..value_pop] value_type value_pop(); [variablelist -[[Effects:] [Dequeues a value from the channel. If the channel is empty, the -fiber gets suspended until at least one new item is `push()`ed or the channel +[[Effects:] [Dequeues a value from the queue. If the queue is empty, the +fiber gets suspended until at least one new item is `push()`ed or the queue gets `close()`d (which throws an exception)[unblocking]]] [[Throws:] [`fiber_error` if `*this` is closed]] [[Error conditions:] [`std::errc::operation_not_permitted`]] ] ] -[xchannel_value_pop unbounded_channel .] +[xqueue_value_pop unbounded_queue .] -[template xchannel_try_pop[cls unblocking] +[template xqueue_try_pop[cls unblocking] [member_heading [cls]..try_pop] - channel_op_status try_pop( value_type & va); + queue_op_status try_pop( value_type & va); [variablelist -[[Effects:] [If channel is empty, returns `empty`. If channel is closed, +[[Effects:] [If queue is empty, returns `empty`. If queue is closed, returns `closed`. Otherwise it returns `success` and `va` contains the dequeued value[unblocking]]] [[Throws:] [Exceptions thrown by copy- or move-operations.]] ] ] -[xchannel_try_pop unbounded_channel .] +[xqueue_try_pop unbounded_queue .] -[template xchannel_pop_wait_until_effects[endtime unblocking] If channel -is not empty, immediately dequeues a value from the channel. Otherwise +[template xqueue_pop_wait_until_effects[endtime unblocking] If queue +is not empty, immediately dequeues a value from the queue. Otherwise the fiber gets suspended until at least one new item is `push()`ed (return -value `success` and `va` contains dequeued value), or the channel gets +value `success` and `va` contains dequeued value), or the queue gets `close()`d (return value `closed`), or the system time reaches [endtime] (return value `timeout`)[unblocking]] -[template xchannel_pop_wait_for[cls unblocking] +[template xqueue_pop_wait_for[cls unblocking] [member_heading [cls]..pop_wait_for] template< typename Rep, typename Period > - channel_op_status pop_wait_for( + queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration) [variablelist [[Effects:] [Accepts `std::chrono::duration` and internally computes a timeout time as (system time + `timeout_duration`). -[xchannel_pop_wait_until_effects the computed timeout time..[unblocking]]]] +[xqueue_pop_wait_until_effects the computed timeout time..[unblocking]]]] [[Throws:] [timeout-related exceptions.]] ] ] -[xchannel_pop_wait_for unbounded_channel .] +[xqueue_pop_wait_for unbounded_queue .] -[template xchannel_pop_wait_until[cls unblocking] +[template xqueue_pop_wait_until[cls unblocking] [member_heading [cls]..pop_wait_until] template< typename Clock, typename Duration > - channel_op_status pop_wait_until( + queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time) [variablelist [[Effects:] [Accepts a `std::chrono::time_point< Clock, Duration >`. -[xchannel_pop_wait_until_effects the passed `time_point`..[unblocking]]]] +[xqueue_pop_wait_until_effects the passed `time_point`..[unblocking]]]] [[Throws:] [timeout-related exceptions.]] ] ] -[xchannel_pop_wait_until unbounded_channel .] +[xqueue_pop_wait_until unbounded_queue .] -[template_heading bounded_channel] +[template_heading bounded_queue] - #include + #include namespace boost { namespace fibers { template< typename T, typename __Allocator__ = __allocator__ > - class bounded_channel { + class bounded_queue { public: typedef T value_type; - bounded_channel( std::size_t wm, __Allocator__ const& alloc = Allocator() ); - bounded_channel( std::size_t hwm, std::size_t lwm, __Allocator__ const& alloc = Allocator() ); + bounded_queue( std::size_t wm, __Allocator__ const& alloc = Allocator() ); + bounded_queue( std::size_t hwm, std::size_t lwm, __Allocator__ const& alloc = Allocator() ); - bounded_channel( bounded_channel const& other) = delete; - bounded_channel & operator=( bounded_channel const& other) = delete; + bounded_queue( bounded_queue const& other) = delete; + bounded_queue & operator=( bounded_queue const& other) = delete; std::size_t upper_bound() const noexcept; std::size_t lower_bound() const noexcept; void close() noexcept; - channel_op_status push( value_type const& va); - channel_op_status push( value_type && va); + queue_op_status push( value_type const& va); + queue_op_status push( value_type && va); template< typename Rep, typename Period > - channel_op_status push_wait_for( + queue_op_status push_wait_for( value_type const& va, std::chrono::duration< Rep, Period > const& timeout_duration); - channel_op_status push_wait_for( value_type && va, + queue_op_status push_wait_for( value_type && va, std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Clock, typename Duration > - channel_op_status push_wait_until( + queue_op_status push_wait_until( value_type const& va, std::chrono::time_point< Clock, Duration > const& timeout_time); template< typename Clock, typename Duration > - channel_op_status push_wait_until( + queue_op_status push_wait_until( value_type && va, std::chrono::time_point< Clock, Duration > const& timeout_time); - channel_op_status try_push( value_type const& va); - channel_op_status try_push( value_type && va); + queue_op_status try_push( value_type const& va); + queue_op_status try_push( value_type && va); - channel_op_status pop( value_type & va); + queue_op_status pop( value_type & va); value_type value_pop(); template< typename Rep, typename Period > - channel_op_status pop_wait_for( + queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Clock, typename Duration > - channel_op_status pop_wait_until( + queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time); - channel_op_status try_pop( value_type & va); + queue_op_status try_pop( value_type & va); }; }} [heading Constructor] - bounded_channel( std::size_t wm, __Allocator__ const& alloc = Allocator() ); - bounded_channel( std::size_t hwm, std::size_t lwm, __Allocator__ const& alloc = Allocator() ); + bounded_queue( std::size_t wm, __Allocator__ const& alloc = Allocator() ); + bounded_queue( std::size_t hwm, std::size_t lwm, __Allocator__ const& alloc = Allocator() ); [variablelist [[Preconditions:] [`hwm > lwm`]] -[[Effects:] [Constructs an object of class `bounded_channel`. The constructor -with two arguments constructs an object of class `bounded_channel` with a +[[Effects:] [Constructs an object of class `bounded_queue`. The constructor +with two arguments constructs an object of class `bounded_queue` with a high-watermark of `hwm` and a low-watermark of `lwm` items. The constructor -with one `std::size_t` argument is effectively the same as `bounded_channel(wm, (wm-1), alloc)`. +with one `std::size_t` argument is effectively the same as `bounded_queue(wm, (wm-1), alloc)`. Internal nodes are allocated using `alloc` - C++11-allocators are supported.]] [[Throws:] [`fiber_error`]] [[Error Conditions:] [ [*invalid_argument]: if `lwm >= hwm`.]] -[[Notes:] [Once the number of values in the channel reaches `hwm`, any call to +[[Notes:] [Once the number of values in the queue reaches `hwm`, any call to `push()`, `push_wait_for()` or `push_wait_until()` will block until the number -of values in the channel is at most `lwm`. That is, if `lwm < (hwm-1)`, the -channel can be in a state in which `push()`, `push_wait_for()` or `push_wait_until()` -calls will block (channel is full) even though the number of values -in the channel is less than `hwm`.]] +of values in the queue is at most `lwm`. That is, if `lwm < (hwm-1)`, the +queue can be in a state in which `push()`, `push_wait_for()` or `push_wait_until()` +calls will block (queue is full) even though the number of values +in the queue is less than `hwm`.]] [[See also:] [__Allocator__ concept, __allocator__]] ] -[member_heading bounded_channel..upper_bound] +[member_heading bounded_queue..upper_bound] std::size_t upper_bound() const noexcept; @@ -327,7 +327,7 @@ in the channel is less than `hwm`.]] [[Throws:] [Nothing.]] ] -[member_heading bounded_channel..lower_bound] +[member_heading bounded_queue..lower_bound] std::size_t lower_bound() const noexcept; @@ -336,85 +336,85 @@ in the channel is less than `hwm`.]] [[Throws:] [Nothing.]] ] -[xchannel_close bounded_channel] +[xqueue_close bounded_queue] -[template bounded_channel_push_effects[or] [xchannel_push_effects If channel +[template bounded_queue_push_effects[or] [xqueue_push_effects If queue is not full, enqueues] Otherwise the calling fiber is suspended until -the number of values in the channel drops to `lwm` (return value -`success`)[or] the channel is `close()`d (return value `closed`)] +the number of values in the queue drops to `lwm` (return value +`success`)[or] the queue is `close()`d (return value `closed`)] -[member_heading bounded_channel..push] +[member_heading bounded_queue..push] - channel_op_status push( value_type const& va); - channel_op_status push( value_type && va); + queue_op_status push( value_type const& va); + queue_op_status push( value_type && va); [variablelist -[[Effects:] [[bounded_channel_push_effects or].]] +[[Effects:] [[bounded_queue_push_effects or].]] [[Throws:] [exceptions thrown by memory allocation and copying or moving `va`.]] ] -[member_heading bounded_channel..push_wait_for] +[member_heading bounded_queue..push_wait_for] template< typename Rep, typename Period > - channel_op_status push_wait_for( + queue_op_status push_wait_for( value_type const& va, std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Rep, typename Period > - channel_op_status push_wait_for( + queue_op_status push_wait_for( value_type && va, std::chrono::duration< Rep, Period > const& timeout_duration); [variablelist [[Effects:] [Accepts `std::chrono::duration` and internally computes a time_point as (system time + `timeout_duration`). -[bounded_channel_push_effects ,], or the system time reaches the computed +[bounded_queue_push_effects ,], or the system time reaches the computed time_point (return value `timeout`).]] [[Throws:] [exceptions thrown by memory allocation and copying or moving `va` or timeout-related exceptions.]] ] -[member_heading bounded_channel..push_wait_until] +[member_heading bounded_queue..push_wait_until] template< typename Clock, typename Duration > - channel_op_status push_wait_until( + queue_op_status push_wait_until( value_type const& va, std::chrono::time_point< Clock, Duration > const& timeout_time); template< typename Clock, typename Duration > - channel_op_status push_wait_until( + queue_op_status push_wait_until( value_type && va, std::chrono::time_point< Clock, Duration > const& timeout_time); [variablelist [[Effects:] [Accepts an absolute `timeout_time` in any supported time_point -type. [bounded_channel_push_effects ,], or the system time reaches the passed +type. [bounded_queue_push_effects ,], or the system time reaches the passed time_point (return value `timeout`).]] [[Throws:] [exceptions thrown by memory allocation and copying or moving `va` or timeout-related exceptions.]] ] -[member_heading bounded_channel..try_push] +[member_heading bounded_queue..try_push] - channel_op_status try_push( value_type const& va); - channel_op_status try_push( value_type && va); + queue_op_status try_push( value_type const& va); + queue_op_status try_push( value_type && va); [variablelist -[[Effects:] [If channel is full, returns `full`. -[xchannel_push_effects Otherwise enqueues]]] +[[Effects:] [If queue is full, returns `full`. +[xqueue_push_effects Otherwise enqueues]]] [[Throws:] [Exceptions thrown by memory allocation and copying or moving `va`.]] ] [template bounded_pop_unblocking[] Once the number of items remaining in the -channel drops to `lwm`, any fibers blocked on `push()`, `push_wait_for()` +queue drops to `lwm`, any fibers blocked on `push()`, `push_wait_for()` or `push_wait_until()` may resume.] -[xchannel_pop bounded_channel... [bounded_pop_unblocking]] -[xchannel_value_pop bounded_channel... [bounded_pop_unblocking]] -[xchannel_try_pop bounded_channel... [bounded_pop_unblocking]] -[xchannel_pop_wait_for bounded_channel... [bounded_pop_unblocking]] -[xchannel_pop_wait_until bounded_channel... [bounded_pop_unblocking]] +[xqueue_pop bounded_queue... [bounded_pop_unblocking]] +[xqueue_value_pop bounded_queue... [bounded_pop_unblocking]] +[xqueue_try_pop bounded_queue... [bounded_pop_unblocking]] +[xqueue_pop_wait_for bounded_queue... [bounded_pop_unblocking]] +[xqueue_pop_wait_until bounded_queue... [bounded_pop_unblocking]] [endsect] diff --git a/doc/when_any.qbk b/doc/when_any.qbk index 6785994a..99d63973 100644 --- a/doc/when_any.qbk +++ b/doc/when_any.qbk @@ -137,8 +137,8 @@ an exception. One tactic would be to adapt our [link wait_done `Done`] class to store the first of the return values, rather than a simple `bool`. However, we choose -instead to use a [template_link unbounded_channel]. We'll only need to enqueue -the first value, so we'll [member_link unbounded_channel..close] it once we've +instead to use a [template_link unbounded_queue]. We'll only need to enqueue +the first value, so we'll [member_link unbounded_queue..close] it once we've retrieved that value. Subsequent `push()` calls will return `closed`. [#wait_first_value] @@ -166,7 +166,7 @@ of our task fibers would cause `std::terminate()` to be called. Let's at least ensure that such an exception would propagate to the fiber awaiting the first result. We can use [template_link future] to transport either a return value or an exception. Therefore, we will change [link -wait_first_value `wait_first_value()`]'s [template_link unbounded_channel] to +wait_first_value `wait_first_value()`]'s [template_link unbounded_queue] to hold `future< T >` items instead of simply `T`. Once we have a `future<>` in hand, all we need do is call [member_link @@ -176,21 +176,21 @@ future..get], which will either return the value or rethrow the exception. [wait_first_outcome] So far so good [mdash] but there's a timing issue. How should we obtain the -`future<>` to [member_link unbounded_channel..push] on the channel? +`future<>` to [member_link unbounded_queue..push] on the queue? We could call [ns_function_link fibers..async]. That would certainly produce a `future<>` for the task function. The trouble is that it would return too quickly! We only want `future<>` items for ['completed] tasks on our -`unbounded_channel<>`. In fact, we only want the `future<>` for the one that +`unbounded_queue<>`. In fact, we only want the `future<>` for the one that completes first. If each fiber launched by `wait_first_outcome()` were to -`push()` the result of calling `async()`, the channel would only ever report +`push()` the result of calling `async()`, the queue would only ever report the result of the leftmost task item [mdash] ['not] the one that completes most quickly. Calling [member_link future..get] on the future returned by `async()` wouldn't be right. You can only call `get()` once per `future<>` instance! And if there were an exception, it would be rethrown inside the helper fiber at the -producer end of the channel, rather than propagated to the consumer end. +producer end of the queue, rather than propagated to the consumer end. We could call [member_link future..wait]. That would block the helper fiber until the `future<>` became ready, at which point we could `push()` it to be @@ -200,10 +200,10 @@ That would work [mdash] but there's a simpler tactic that avoids creating an ext fiber. We can wrap the task function in a [template_link packaged_task]. While one naturally thinks of passing a `packaged_task<>` to a new fiber [mdash] that is, in fact, what `async()` does [mdash] in this case, we're already running in the -helper fiber at the producer end of the channel! We can simply ['call] the +helper fiber at the producer end of the queue! We can simply ['call] the `packaged_task<>`. On return from that call, the task function has completed, meaning that the `future<>` obtained from the `packaged_task<>` is certain to -be ready. At that point we can simply `push()` it to the channel. +be ready. At that point we can simply `push()` it to the queue. [#wait_first_outcome_impl] [wait_first_outcome_impl] @@ -225,7 +225,7 @@ right approach. If one of the services produces an error quickly, while another follows up with a real answer, we don't want to prefer the error just because it arrived first! -Given the `unbounded_channel< future< T > >` we already constructed for +Given the `unbounded_queue< future< T > >` we already constructed for `wait_first_outcome()`, though, we can readily recast the interface function to deliver the first ['successful] result. @@ -245,10 +245,10 @@ an `exception_list` of our own: Now we can build `wait_first_success()`, using [link wait_first_outcome_impl `wait_first_outcome_impl()`]. -Instead of retrieving only the first `future<>` from the channel, we must now +Instead of retrieving only the first `future<>` from the queue, we must now loop over `future<>` items. Of course we must limit that iteration! If we launch only `count` producer fibers, the `(count+1)`[superscript st] -[member_link unbounded_channel..pop] call would block forever. +[member_link unbounded_queue..pop] call would block forever. Given a ready `future<>`, we can distinguish failure by calling [member_link future..get_exception_ptr]. If the `future<>` in fact contains a result rather @@ -258,7 +258,7 @@ caller. If the `std::exception_ptr` is ['not] `nullptr`, though, we collect it into our pending `exception_list` and loop back for the next `future<>` from the -channel. +queue. If we fall out of the loop [mdash] if every single task fiber threw an exception [mdash] we throw the `exception_list` exception into which we've @@ -283,7 +283,7 @@ To keep the example simple, we'll revert to pretending that none of them can throw an exception. That makes `wait_first_value_het()` strongly resemble [link wait_first_value `wait_first_value()`]. We can actually reuse [link wait_first_value_impl `wait_first_value_impl()`], merely passing -`boost::variant` as the channel's value type rather than the +`boost::variant` as the queue's value type rather than the common `T`! Naturally this could be extended to use [link wait_first_success @@ -346,7 +346,7 @@ its task functions has completed. As soon as we want to collect return values from all the task functions, we can see right away how to reuse [link wait_first_value `wait_first_value()`]'s -channel for the purpose. All we have to do is avoid closing it after the +queue for the purpose. All we have to do is avoid closing it after the first value! But in fact, collecting multiple values raises an interesting question: do we @@ -354,9 +354,9 @@ But in fact, collecting multiple values raises an interesting question: do we rather process each result as soon as it becomes available? Fortunately we can present both APIs. Let's define `wait_all_values_source()` -to return `shared_ptr>`.[footnote We could have used -either [template_link bounded_channel] or [template_link unbounded_channel]. -We chose `unbounded_channel<>` on the assumption that its simpler semantics +to return `shared_ptr>`.[footnote We could have used +either [template_link bounded_queue] or [template_link unbounded_queue]. +We chose `unbounded_queue<>` on the assumption that its simpler semantics imply a cheaper implementation.] [#wait_all_values] @@ -371,25 +371,25 @@ It might be called like this: As you can see from the loop in `wait_all_values()`, instead of requiring its caller to count values, we define `wait_all_values_source()` to [member_link -unbounded_channel..close] the channel when done. But how do we do that? Each +unbounded_queue..close] the queue when done. But how do we do that? Each producer fiber is independent. It has no idea whether it is the last one to -[member_link unbounded_channel..push] a value. +[member_link unbounded_queue..push] a value. -[#wait_nchannel] +[#wait_nqueue] We can address that problem with a counting façade for the -`unbounded_channel<>`. In fact, our façade need only support the producer end of -the channel. +`unbounded_queue<>`. In fact, our façade need only support the producer end of +the queue. -[wait_nchannel] +[wait_nqueue] [#wait_all_values_source] -Armed with `nchannel<>`, we can implement `wait_all_values_source()`. It +Armed with `nqueue<>`, we can implement `wait_all_values_source()`. It starts just like [link wait_first_value `wait_first_value()`]. The difference -is that we wrap the `unbounded_channel` with an `nchannel` to pass to +is that we wrap the `unbounded_queue` with an `nqueue` to pass to the producer fibers. -Then, of course, instead of popping the first value, closing the channel and -returning it, we simply return the `shared_ptr>`. +Then, of course, instead of popping the first value, closing the queue and +returning it, we simply return the `shared_ptr>`. [wait_all_values_source] @@ -399,8 +399,8 @@ For example: [#wait_all_values_impl] `wait_all_values_impl()` really is just like [link wait_first_value_impl -`wait_first_value_impl()`] except for the use of `nchannel` rather than -`unbounded_channel`: +`wait_first_value_impl()`] except for the use of `nqueue` rather than +`unbounded_queue`: [wait_all_values_impl] @@ -426,12 +426,12 @@ For example: Naturally this complicates the API for `wait_all_until_error_source()`. The caller must both retrieve a `future< T >` and call its `get()` method. It would, of course, be possible to return a façade over the consumer end of the -channel that would implicitly perform the `get()` and return a simple `T` (or +queue that would implicitly perform the `get()` and return a simple `T` (or throw). The implementation is just as you would expect. Notice, however, that we can reuse [link wait_first_outcome_impl `wait_first_outcome_impl()`], passing the -`nchannel` rather than `unbounded_channel`. +`nqueue` rather than `unbounded_queue`. [wait_all_until_error_source] diff --git a/examples/asio/ps/publisher.cpp b/examples/asio/ps/publisher.cpp index 55728826..76a63d77 100644 --- a/examples/asio/ps/publisher.cpp +++ b/examples/asio/ps/publisher.cpp @@ -23,7 +23,7 @@ enum { int main( int argc, char* argv[]) { try { if ( 3 != argc) { - std::cerr << "Usage: publisher \n"; + std::cerr << "Usage: publisher \n"; return EXIT_FAILURE; } boost::asio::io_service io_service; @@ -33,9 +33,9 @@ int main( int argc, char* argv[]) { tcp::socket s( io_service); boost::asio::connect( s, iterator); char msg[max_length]; - std::string channel( argv[2]); + std::string queue( argv[2]); std::memset( msg, '\0', max_length); - std::memcpy( msg, channel.c_str(), channel.size() ); + std::memcpy( msg, queue.c_str(), queue.size() ); boost::asio::write( s, boost::asio::buffer( msg, max_length) ); for (;;) { std::cout << "publish: "; diff --git a/examples/asio/ps/server.cpp b/examples/asio/ps/server.cpp index f4c4db8e..aeb58f23 100644 --- a/examples/asio/ps/server.cpp +++ b/examples/asio/ps/server.cpp @@ -25,18 +25,18 @@ const std::size_t max_length = 1024; class subscriber_session; typedef std::shared_ptr< subscriber_session > subscriber_session_ptr; -// a channel has n subscribers (subscriptions) -// this class holds a list of subcribers for one channel +// a queue has n subscribers (subscriptions) +// this class holds a list of subcribers for one queue class subscriptions { public: ~subscriptions(); - // subscribe to this channel + // subscribe to this queue void subscribe( subscriber_session_ptr const& s) { subscribers_.insert( s); } - // unsubscribe from this channel + // unsubscribe from this queue void unsubscribe( subscriber_session_ptr const& s) { subscribers_.erase(s); } @@ -49,86 +49,86 @@ private: std::set< subscriber_session_ptr > subscribers_; }; -// a class to register channels and to subsribe clients to this channels +// a class to register queues and to subsribe clients to this queues class registry : private boost::noncopyable { private: - typedef std::map< std::string, std::shared_ptr< subscriptions > > channels_cont; - typedef channels_cont::iterator channels_iter; + typedef std::map< std::string, std::shared_ptr< subscriptions > > queues_cont; + typedef queues_cont::iterator queues_iter; boost::fibers::mutex mtx_; - channels_cont channels_; + queues_cont queues_; - void register_channel_( std::string const& channel) { - if ( channels_.end() != channels_.find( channel) ) { - throw std::runtime_error("channel already exists"); + void register_queue_( std::string const& queue) { + if ( queues_.end() != queues_.find( queue) ) { + throw std::runtime_error("queue already exists"); } - channels_[channel] = std::make_shared< subscriptions >(); - std::cout << "new channel '" << channel << "' registered" << std::endl; + queues_[queue] = std::make_shared< subscriptions >(); + std::cout << "new queue '" << queue << "' registered" << std::endl; } - void unregister_channel_( std::string const& channel) { - channels_.erase( channel); - std::cout << "channel '" << channel << "' unregistered" << std::endl; + void unregister_queue_( std::string const& queue) { + queues_.erase( queue); + std::cout << "queue '" << queue << "' unregistered" << std::endl; } - void subscribe_( std::string const& channel, subscriber_session_ptr s) { - channels_iter iter = channels_.find( channel); - if ( channels_.end() == iter ) { - throw std::runtime_error("channel does not exist"); + void subscribe_( std::string const& queue, subscriber_session_ptr s) { + queues_iter iter = queues_.find( queue); + if ( queues_.end() == iter ) { + throw std::runtime_error("queue does not exist"); } iter->second->subscribe( s); - std::cout << "new subscription to channel '" << channel << "'" << std::endl; + std::cout << "new subscription to queue '" << queue << "'" << std::endl; } - void unsubscribe_( std::string const& channel, subscriber_session_ptr s) { - channels_iter iter = channels_.find( channel); - if ( channels_.end() != iter ) { + void unsubscribe_( std::string const& queue, subscriber_session_ptr s) { + queues_iter iter = queues_.find( queue); + if ( queues_.end() != iter ) { iter->second->unsubscribe( s); } } - void publish_( std::string const& channel, std::string const& msg) { - channels_iter iter = channels_.find( channel); - if ( channels_.end() == iter ) { - throw std::runtime_error("channel does not exist"); + void publish_( std::string const& queue, std::string const& msg) { + queues_iter iter = queues_.find( queue); + if ( queues_.end() == iter ) { + throw std::runtime_error("queue does not exist"); } iter->second->publish( msg); - std::cout << "message '" << msg << "' to publish on channel '" << channel << "'" << std::endl; + std::cout << "message '" << msg << "' to publish on queue '" << queue << "'" << std::endl; } public: - // add a channel to registry - void register_channel( std::string const& channel) { + // add a queue to registry + void register_queue( std::string const& queue) { std::unique_lock< boost::fibers::mutex > lk( mtx_); - register_channel_( channel); + register_queue_( queue); } - // remove a channel from registry - void unregister_channel( std::string const& channel) { + // remove a queue from registry + void unregister_queue( std::string const& queue) { std::unique_lock< boost::fibers::mutex > lk( mtx_); - unregister_channel_( channel); + unregister_queue_( queue); } - // subscribe to a channel - void subscribe( std::string const& channel, subscriber_session_ptr s) { + // subscribe to a queue + void subscribe( std::string const& queue, subscriber_session_ptr s) { std::unique_lock< boost::fibers::mutex > lk( mtx_); - subscribe_( channel, s); + subscribe_( queue, s); } - // unsubscribe from a channel - void unsubscribe( std::string const& channel, subscriber_session_ptr s) { + // unsubscribe from a queue + void unsubscribe( std::string const& queue, subscriber_session_ptr s) { std::unique_lock< boost::fibers::mutex > lk( mtx_); - unsubscribe_( channel, s); + unsubscribe_( queue, s); } - // publish a message to all subscribers registerd to the channel - void publish( std::string const& channel, std::string const& msg) { + // publish a message to all subscribers registerd to the queue + void publish( std::string const& queue, std::string const& msg) { std::unique_lock< boost::fibers::mutex > lk( mtx_); - publish_( channel, msg); + publish_( queue, msg); } }; -// a subscriber subscribes to a given channel in order to receive messages published on this channel +// a subscriber subscribes to a given queue in order to receive messages published on this queue class subscriber_session : public std::enable_shared_from_this< subscriber_session > { public: explicit subscriber_session( std::shared_ptr< boost::asio::io_service > const& io_service, registry & reg) : @@ -142,10 +142,10 @@ public: // this function is executed inside the fiber void run() { - std::string channel; + std::string queue; try { boost::system::error_code ec; - // read first message == channel name + // read first message == queue name // async_ready() returns if the the complete message is read // until this the fiber is suspended until the complete message // is read int the given buffer 'data' @@ -154,13 +154,13 @@ public: boost::asio::buffer( data_), boost::fibers::asio::yield[ec]); if ( ec) { - throw std::runtime_error("no channel from subscriber"); + throw std::runtime_error("no queue from subscriber"); } - // first message ist equal to the channel name the publisher + // first message ist equal to the queue name the publisher // publishes to - channel = data_; - // subscribe to new channel - reg_.subscribe( channel, shared_from_this() ); + queue = data_; + // subscribe to new queue + reg_.subscribe( queue, shared_from_this() ); // read published messages for (;;) { // wait for a conditon-variable for new messages @@ -191,12 +191,12 @@ public: std::cout << "subscriber::run(): '" << data << "' written" << std::endl; } } catch ( std::exception const& e) { - std::cerr << "subscriber [" << channel << "] failed: " << e.what() << std::endl; + std::cerr << "subscriber [" << queue << "] failed: " << e.what() << std::endl; } // close socket socket_.close(); - // unregister channel - reg_.unsubscribe( channel, shared_from_this() ); + // unregister queue + reg_.unsubscribe( queue, shared_from_this() ); } // called from publisher_session (running in other fiber) @@ -230,8 +230,8 @@ subscriptions::publish( std::string const& msg) { } } -// a publisher publishes messages on its channel -// subscriber might register to this channel to get the published messages +// a publisher publishes messages on its queue +// subscriber might register to this queue to get the published messages class publisher_session : public std::enable_shared_from_this< publisher_session > { public: explicit publisher_session( std::shared_ptr< boost::asio::io_service > const& io_service, registry & reg) : @@ -245,12 +245,12 @@ public: // this function is executed inside the fiber void run() { - std::string channel; + std::string queue; try { boost::system::error_code ec; // fixed size message char data[max_length]; - // read first message == channel name + // read first message == queue name // async_ready() returns if the the complete message is read // until this the fiber is suspended until the complete message // is read int the given buffer 'data' @@ -259,13 +259,13 @@ public: boost::asio::buffer( data), boost::fibers::asio::yield[ec]); if ( ec) { - throw std::runtime_error("no channel from publisher"); + throw std::runtime_error("no queue from publisher"); } - // first message ist equal to the channel name the publisher + // first message ist equal to the queue name the publisher // publishes to - channel = data; - // register the new channel - reg_.register_channel( channel); + queue = data; + // register the new queue + reg_.register_queue( queue); // start publishing messages for (;;) { // read message from publisher asyncronous @@ -281,15 +281,15 @@ public: throw boost::system::system_error( ec); //some other error } // publish message to all subscribers - reg_.publish( channel, std::string( data) ); + reg_.publish( queue, std::string( data) ); } } catch ( std::exception const& e) { - std::cerr << "publisher [" << channel << "] failed: " << e.what() << std::endl; + std::cerr << "publisher [" << queue << "] failed: " << e.what() << std::endl; } // close socket socket_.close(); - // unregister channel - reg_.unregister_channel( channel); + // unregister queue + reg_.unregister_queue( queue); } private: @@ -362,7 +362,7 @@ int main( int argc, char* argv[]) { std::shared_ptr< boost::asio::io_service > io_service = std::make_shared< boost::asio::io_service >(); // register asio scheduler boost::fibers::use_scheduling_algorithm< boost::fibers::asio::round_robin >( io_service); - // registry for channels and its subscription + // registry for queues and its subscription registry reg; // create an acceptor for publishers, run it as fiber boost::fibers::fiber( diff --git a/examples/asio/ps/subscriber.cpp b/examples/asio/ps/subscriber.cpp index 7acabe70..04e583e2 100644 --- a/examples/asio/ps/subscriber.cpp +++ b/examples/asio/ps/subscriber.cpp @@ -23,7 +23,7 @@ enum { int main( int argc, char* argv[]) { try { if ( 3 != argc) { - std::cerr << "Usage: subscriber \n"; + std::cerr << "Usage: subscriber \n"; return EXIT_FAILURE; } boost::asio::io_service io_service; @@ -33,9 +33,9 @@ int main( int argc, char* argv[]) { tcp::socket s( io_service); boost::asio::connect( s, iterator); char msg[max_length]; - std::string channel( argv[2]); + std::string queue( argv[2]); std::memset( msg, '\0', max_length); - std::memcpy( msg, channel.c_str(), channel.size() ); + std::memcpy( msg, queue.c_str(), queue.size() ); boost::asio::write( s, boost::asio::buffer( msg, max_length) ); for (;;) { char reply[max_length]; diff --git a/examples/ping_pong.cpp b/examples/ping_pong.cpp index 4ab22bd6..f7ae835c 100644 --- a/examples/ping_pong.cpp +++ b/examples/ping_pong.cpp @@ -9,7 +9,7 @@ #include -typedef boost::fibers::unbounded_channel< std::string > fifo_t; +typedef boost::fibers::unbounded_queue< std::string > fifo_t; inline void ping( fifo_t & recv_buf, fifo_t & send_buf) diff --git a/examples/wait_stuff.cpp b/examples/wait_stuff.cpp index 16bb8f79..a9f14485 100644 --- a/examples/wait_stuff.cpp +++ b/examples/wait_stuff.cpp @@ -188,27 +188,27 @@ Example wfs( runner, "wait_first_simple()", [](){ // When there's only one function, call this overload //[wait_first_value_impl template< typename T, typename Fn > -void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_channel< T > > channel, +void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_queue< T > > queue, Fn && function) { - boost::fibers::fiber( [channel, function](){ - // Ignore channel_op_status returned by push(): + boost::fibers::fiber( [queue, function](){ + // Ignore queue_op_status returned by push(): // might be closed; we simply don't care. - channel->push( function() ); + queue->push( function() ); }).detach(); } //] // When there are two or more functions, call this overload template< typename T, typename Fn0, typename Fn1, typename ... Fns > -void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_channel< T > > channel, +void wait_first_value_impl( std::shared_ptr< boost::fibers::unbounded_queue< T > > queue, Fn0 && function0, Fn1 && function1, Fns && ... functions) { // process the first function using the single-function overload - wait_first_value_impl< T >( channel, + wait_first_value_impl< T >( queue, std::forward< Fn0 >( function0) ); // then recur to process the rest - wait_first_value_impl< T >( channel, + wait_first_value_impl< T >( queue, std::forward< Fn1 >( function1), std::forward< Fns >( functions) ... ); } @@ -221,16 +221,16 @@ template< typename Fn, typename ... Fns > typename std::result_of< Fn() >::type wait_first_value( Fn && function, Fns && ... functions) { typedef typename std::result_of< Fn() >::type return_t; - typedef boost::fibers::unbounded_channel< return_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< return_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_value_impl< return_t >( channelp, + wait_first_value_impl< return_t >( queuep, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // retrieve the first value - return_t value( channelp->value_pop() ); - // close the channel: no subsequent push() has to succeed - channelp->close(); + return_t value( queuep->value_pop() ); + // close the queue: no subsequent push() has to succeed + queuep->close(); return value; } //] @@ -252,15 +252,15 @@ Example wfv( runner, "wait_first_value()", [](){ *****************************************************************************/ // When there's only one function, call this overload. //[wait_first_outcome_impl -template< typename T, typename CHANNELP, typename Fn > -void wait_first_outcome_impl( CHANNELP channel, Fn && function) { +template< typename T, typename QUEUEP, typename Fn > +void wait_first_outcome_impl( QUEUEP queue, Fn && function) { boost::fibers::fiber( // Use std::bind() here for C++11 compatibility. C++11 lambda capture // can't move a move-only Fn type, but bind() can. Let bind() move the - // channel pointer and the function into the bound object, passing + // queue pointer and the function into the bound object, passing // references into the lambda. std::bind( - []( CHANNELP & channel, + []( QUEUEP & queue, typename std::decay< Fn >::type & function) { // Instantiate a packaged_task to capture any exception thrown by // function. @@ -269,27 +269,27 @@ void wait_first_outcome_impl( CHANNELP channel, Fn && function) { // function() to have completed BEFORE we push the future. task(); // Pass the corresponding future to consumer. Ignore - // channel_op_status returned by push(): might be closed; we + // queue_op_status returned by push(): might be closed; we // simply don't care. - channel->push( task.get_future() ); + queue->push( task.get_future() ); }, - channel, + queue, std::forward< Fn >( function) )).detach(); } //] // When there are two or more functions, call this overload -template< typename T, typename CHANNELP, typename Fn0, typename Fn1, typename ... Fns > -void wait_first_outcome_impl( CHANNELP channel, +template< typename T, typename QUEUEP, typename Fn0, typename Fn1, typename ... Fns > +void wait_first_outcome_impl( QUEUEP queue, Fn0 && function0, Fn1 && function1, Fns && ... functions) { // process the first function using the single-function overload - wait_first_outcome_impl< T >( channel, + wait_first_outcome_impl< T >( queue, std::forward< Fn0 >( function0) ); // then recur to process the rest - wait_first_outcome_impl< T >( channel, + wait_first_outcome_impl< T >( queue, std::forward< Fn1 >( function1), std::forward< Fns >( functions) ... ); } @@ -301,21 +301,21 @@ void wait_first_outcome_impl( CHANNELP channel, template< typename Fn, typename ... Fns > typename std::result_of< Fn() >::type wait_first_outcome( Fn && function, Fns && ... functions) { - // In this case, the value we pass through the channel is actually a + // In this case, the value we pass through the queue is actually a // future -- which is already ready. future can carry either a value or an // exception. typedef typename std::result_of< Fn() >::type return_t; typedef boost::fibers::future< return_t > future_t; - typedef boost::fibers::unbounded_channel< future_t > channel_t; - auto channelp(std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< future_t > queue_t; + auto queuep(std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_outcome_impl< return_t >( channelp, + wait_first_outcome_impl< return_t >( queuep, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // retrieve the first future - future_t future( channelp->value_pop() ); - // close the channel: no subsequent push() has to succeed - channelp->close(); + future_t future( queuep->value_pop() ); + // close the queue: no subsequent push() has to succeed + queuep->close(); // either return value or throw exception return future.get(); } @@ -394,15 +394,15 @@ template< typename Fn, typename ... Fns > typename std::result_of< Fn() >::type wait_first_success( Fn && function, Fns && ... functions) { std::size_t count( 1 + sizeof ... ( functions) ); - // In this case, the value we pass through the channel is actually a + // In this case, the value we pass through the queue is actually a // future -- which is already ready. future can carry either a value or an // exception. typedef typename std::result_of< typename std::decay< Fn >::type() >::type return_t; typedef boost::fibers::future< return_t > future_t; - typedef boost::fibers::unbounded_channel< future_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< future_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_outcome_impl< return_t >( channelp, + wait_first_outcome_impl< return_t >( queuep, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // instantiate exception_list, just in case @@ -410,13 +410,13 @@ wait_first_success( Fn && function, Fns && ... functions) { // retrieve up to 'count' results -- but stop there! for ( std::size_t i = 0; i < count; ++i) { // retrieve the next future - future_t future( channelp->value_pop() ); + future_t future( queuep->value_pop() ); // retrieve exception_ptr if any std::exception_ptr error( future.get_exception_ptr() ); // if no error, then yay, return value if ( ! error) { - // close the channel: no subsequent push() has to succeed - channelp->close(); + // close the queue: no subsequent push() has to succeed + queuep->close(); // show caller the value we got return future.get(); } @@ -472,17 +472,17 @@ Example wfss( runner, "wait_first_success()", [](){ template< typename ... Fns > boost::variant< typename std::result_of< Fns() >::type ... > wait_first_value_het( Fns && ... functions) { - // Use unbounded_channel>; see remarks above. + // Use unbounded_queue>; see remarks above. typedef boost::variant< typename std::result_of< Fns() >::type ... > return_t; - typedef boost::fibers::unbounded_channel< return_t > channel_t; - auto channelp( std::make_shared< channel_t >() ); + typedef boost::fibers::unbounded_queue< return_t > queue_t; + auto queuep( std::make_shared< queue_t >() ); // launch all the relevant fibers - wait_first_value_impl< return_t >( channelp, + wait_first_value_impl< return_t >( queuep, std::forward< Fns >( functions) ... ); // retrieve the first value - return_t value( channelp->value_pop() ); - // close the channel: no subsequent push() has to succeed - channelp->close(); + return_t value( queuep->value_pop() ); + // close the queue: no subsequent push() has to succeed + queuep->close(); return value; } //] @@ -556,36 +556,36 @@ Example was( runner, "wait_all_simple()", [](){ /***************************************************************************** * when_all, return values *****************************************************************************/ -//[wait_nchannel -// Introduce a channel facade that closes the channel once a specific number +//[wait_nqueue +// Introduce a queue facade that closes the queue once a specific number // of items has been pushed. This allows an arbitrary consumer to read until // 'closed' without itself having to count items. template< typename T > -class nchannel { +class nqueue { public: - nchannel( std::shared_ptr< boost::fibers::unbounded_channel< T > > cp, + nqueue( std::shared_ptr< boost::fibers::unbounded_queue< T > > cp, std::size_t lm): - channel_( cp), + queue_( cp), limit_( lm) { - assert(channel_); + assert(queue_); if ( 0 == limit_) { - channel_->close(); + queue_->close(); } } - boost::fibers::channel_op_status push( T && va) { - boost::fibers::channel_op_status ok = - channel_->push( std::forward< T >( va) ); - if ( ok == boost::fibers::channel_op_status::success && + boost::fibers::queue_op_status push( T && va) { + boost::fibers::queue_op_status ok = + queue_->push( std::forward< T >( va) ); + if ( ok == boost::fibers::queue_op_status::success && --limit_ == 0) { - // after the 'limit_'th successful push, close the channel - channel_->close(); + // after the 'limit_'th successful push, close the queue + queue_->close(); } return ok; } private: - std::shared_ptr< boost::fibers::unbounded_channel< T > > channel_; + std::shared_ptr< boost::fibers::unbounded_queue< T > > queue_; std::size_t limit_; }; //] @@ -593,47 +593,47 @@ private: // When there's only one function, call this overload //[wait_all_values_impl template< typename T, typename Fn > -void wait_all_values_impl( std::shared_ptr< nchannel< T > > channel, +void wait_all_values_impl( std::shared_ptr< nqueue< T > > queue, Fn && function) { - boost::fibers::fiber( [channel, function](){ - channel->push(function()); + boost::fibers::fiber( [queue, function](){ + queue->push(function()); }).detach(); } //] // When there are two or more functions, call this overload template< typename T, typename Fn0, typename Fn1, typename ... Fns > -void wait_all_values_impl( std::shared_ptr< nchannel< T > > channel, +void wait_all_values_impl( std::shared_ptr< nqueue< T > > queue, Fn0 && function0, Fn1 && function1, Fns && ... functions) { // process the first function using the single-function overload - wait_all_values_impl< T >( channel, std::forward< Fn0 >( function0) ); + wait_all_values_impl< T >( queue, std::forward< Fn0 >( function0) ); // then recur to process the rest - wait_all_values_impl< T >( channel, + wait_all_values_impl< T >( queue, std::forward< Fn1 >( function1), std::forward< Fns >( functions) ... ); } //[wait_all_values_source -// Return a shared_ptr> from which the caller can +// Return a shared_ptr> from which the caller can // retrieve each new result as it arrives, until 'closed'. template< typename Fn, typename ... Fns > -std::shared_ptr< boost::fibers::unbounded_channel< typename std::result_of< Fn() >::type > > +std::shared_ptr< boost::fibers::unbounded_queue< typename std::result_of< Fn() >::type > > wait_all_values_source( Fn && function, Fns && ... functions) { std::size_t count( 1 + sizeof ... ( functions) ); typedef typename std::result_of< Fn() >::type return_t; - typedef boost::fibers::unbounded_channel< return_t > channel_t; - // make the channel - auto channelp( std::make_shared< channel_t >() ); - // and make an nchannel facade to close it after 'count' items - auto ncp( std::make_shared< nchannel< return_t > >( channelp, count) ); - // pass that nchannel facade to all the relevant fibers + typedef boost::fibers::unbounded_queue< return_t > queue_t; + // make the queue + auto queuep( std::make_shared< queue_t >() ); + // and make an nqueue facade to close it after 'count' items + auto ncp( std::make_shared< nqueue< return_t > >( queuep, count) ); + // pass that nqueue facade to all the relevant fibers wait_all_values_impl< return_t >( ncp, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); - // then return the channel for consumer - return channelp; + // then return the queue for consumer + return queuep; } //] @@ -650,13 +650,13 @@ wait_all_values( Fn && function, Fns && ... functions) { vector_t results; results.reserve( count); - // get channel - std::shared_ptr< boost::fibers::unbounded_channel< return_t > > channel = + // get queue + std::shared_ptr< boost::fibers::unbounded_queue< return_t > > queue = wait_all_values_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // fill results vector return_t value; - while ( boost::fibers::channel_op_status::success == channel->pop(value) ) { + while ( boost::fibers::queue_op_status::success == queue->pop(value) ) { results.push_back( value); } // return vector to caller @@ -666,13 +666,13 @@ wait_all_values( Fn && function, Fns && ... functions) { Example wav( runner, "wait_all_values()", [](){ //[wait_all_values_source_ex - std::shared_ptr< boost::fibers::unbounded_channel< std::string > > channel = + std::shared_ptr< boost::fibers::unbounded_queue< std::string > > queue = wait_all_values_source( [](){ return sleeper("wavs_third", 150); }, [](){ return sleeper("wavs_second", 100); }, [](){ return sleeper("wavs_first", 50); }); std::string value; - while ( boost::fibers::channel_op_status::success == channel->pop(value) ) { + while ( boost::fibers::queue_op_status::success == queue->pop(value) ) { std::cout << "wait_all_values_source() => '" << value << "'" << std::endl; } @@ -696,28 +696,28 @@ Example wav( runner, "wait_all_values()", [](){ * when_all, throw first exception *****************************************************************************/ //[wait_all_until_error_source -// Return a shared_ptr>> from which the caller can +// Return a shared_ptr>> from which the caller can // get() each new result as it arrives, until 'closed'. template< typename Fn, typename ... Fns > std::shared_ptr< - boost::fibers::unbounded_channel< + boost::fibers::unbounded_queue< boost::fibers::future< typename std::result_of< Fn() >::type > > > wait_all_until_error_source( Fn && function, Fns && ... functions) { std::size_t count( 1 + sizeof ... ( functions) ); typedef typename std::result_of< Fn() >::type return_t; typedef boost::fibers::future< return_t > future_t; - typedef boost::fibers::unbounded_channel< future_t > channel_t; - // make the channel - auto channelp( std::make_shared< channel_t >() ); - // and make an nchannel facade to close it after 'count' items - auto ncp( std::make_shared< nchannel< future_t > >( channelp, count) ); - // pass that nchannel facade to all the relevant fibers + typedef boost::fibers::unbounded_queue< future_t > queue_t; + // make the queue + auto queuep( std::make_shared< queue_t >() ); + // and make an nqueue facade to close it after 'count' items + auto ncp( std::make_shared< nqueue< future_t > >( queuep, count) ); + // pass that nqueue facade to all the relevant fibers wait_first_outcome_impl< return_t >( ncp, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); - // then return the channel for consumer - return channelp; + // then return the queue for consumer + return queuep; } //] @@ -736,14 +736,14 @@ wait_all_until_error( Fn && function, Fns && ... functions) { vector_t results; results.reserve( count); - // get channel + // get queue std::shared_ptr< - boost::fibers::unbounded_channel< future_t > > channel( + boost::fibers::unbounded_queue< future_t > > queue( wait_all_until_error_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ) ); // fill results vector future_t future; - while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { + while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { results.push_back( future.get() ); } // return vector to caller @@ -754,20 +754,20 @@ wait_all_until_error( Fn && function, Fns && ... functions) { Example waue( runner, "wait_all_until_error()", [](){ //[wait_all_until_error_source_ex typedef boost::fibers::future< std::string > future_t; - std::shared_ptr< boost::fibers::unbounded_channel< future_t > > channel = + std::shared_ptr< boost::fibers::unbounded_queue< future_t > > queue = wait_all_until_error_source( [](){ return sleeper("wauess_third", 150); }, [](){ return sleeper("wauess_second", 100); }, [](){ return sleeper("wauess_first", 50); }); future_t future; - while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { + while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { std::string value( future.get() ); std::cout << "wait_all_until_error_source(success) => '" << value << "'" << std::endl; } //] - channel = wait_all_until_error_source( + queue = wait_all_until_error_source( [](){ return sleeper("wauesf_third", 150); }, [](){ return sleeper("wauesf_second", 100, true); }, [](){ return sleeper("wauesf_first", 50); }); @@ -775,7 +775,7 @@ Example waue( runner, "wait_all_until_error()", [](){ std::string thrown; //<- try { - while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { + while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { std::string value( future.get() ); std::cout << "wait_all_until_error_source(fail) => '" << value << "'" << std::endl; @@ -827,14 +827,14 @@ wait_all_collect_errors( Fn && function, Fns && ... functions) { results.reserve( count); exception_list exceptions("wait_all_collect_errors() exceptions"); - // get channel + // get queue std::shared_ptr< - boost::fibers::unbounded_channel< future_t > > channel( + boost::fibers::unbounded_queue< future_t > > queue( wait_all_until_error_source( std::forward< Fn >( function), std::forward< Fns >( functions) ... ) ); // fill results and/or exceptions vectors future_t future; - while ( boost::fibers::channel_op_status::success == channel->pop( future) ) { + while ( boost::fibers::queue_op_status::success == queue->pop( future) ) { std::exception_ptr exp = future.get_exception_ptr(); if ( ! exp) { results.push_back( future.get() ); diff --git a/include/boost/fiber/all.hpp b/include/boost/fiber/all.hpp index 21620a99..113d1212 100644 --- a/include/boost/fiber/all.hpp +++ b/include/boost/fiber/all.hpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -29,6 +29,6 @@ #include #include #include -#include +#include #endif // BOOST_FIBERS_H diff --git a/include/boost/fiber/bounded_channel.hpp b/include/boost/fiber/bounded_queue.hpp similarity index 86% rename from include/boost/fiber/bounded_channel.hpp rename to include/boost/fiber/bounded_queue.hpp index 716080bd..2f064300 100644 --- a/include/boost/fiber/bounded_channel.hpp +++ b/include/boost/fiber/bounded_queue.hpp @@ -5,8 +5,8 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_FIBERS_BOUNDED_CHANNEL_H -#define BOOST_FIBERS_BOUNDED_CHANNEL_H +#ifndef BOOST_FIBERS_BOUNDED_QUEUE_H +#define BOOST_FIBERS_BOUNDED_QUEUE_H #include #include @@ -25,7 +25,7 @@ #include #include #include -#include +#include #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX @@ -37,7 +37,7 @@ namespace fibers { template< typename T, typename Allocator = std::allocator< T > > -class bounded_channel { +class bounded_queue { public: typedef T value_type; @@ -126,10 +126,10 @@ private: return count_ >= hwm_; } - channel_op_status push_( ptr_t new_node, + queue_op_status push_( ptr_t new_node, std::unique_lock< boost::fibers::mutex > & lk) { if ( is_closed_() ) { - return channel_op_status::closed; + return queue_op_status::closed; } not_full_cond_.wait( lk, [this](){ @@ -138,39 +138,39 @@ private: return push_and_notify_( new_node, lk); } - channel_op_status try_push_( ptr_t new_node, + queue_op_status try_push_( ptr_t new_node, std::unique_lock< boost::fibers::mutex > & lk) noexcept { if ( is_closed_() ) { - return channel_op_status::closed; + return queue_op_status::closed; } if ( is_full_() ) { - return channel_op_status::full; + return queue_op_status::full; } return push_and_notify_( new_node, lk); } template< typename Clock, typename Duration > - channel_op_status push_wait_until_( ptr_t new_node, + queue_op_status push_wait_until_( ptr_t new_node, std::chrono::time_point< Clock, Duration > const& timeout_time, std::unique_lock< boost::fibers::mutex > & lk) { if ( is_closed_() ) { - return channel_op_status::closed; + return queue_op_status::closed; } if ( ! not_full_cond_.wait_until( lk, timeout_time, [this](){ return ! is_full_(); })) { - return channel_op_status::timeout; + return queue_op_status::timeout; } return push_and_notify_( new_node, lk); } - channel_op_status push_and_notify_( ptr_t new_node, + queue_op_status push_and_notify_( ptr_t new_node, std::unique_lock< boost::fibers::mutex > & lk) noexcept { push_tail_( new_node); lk.unlock(); not_empty_cond_.notify_one(); - return channel_op_status::success; + return queue_op_status::success; } void push_tail_( ptr_t new_node) noexcept { @@ -208,7 +208,7 @@ private: } public: - bounded_channel( std::size_t hwm, std::size_t lwm, + bounded_queue( std::size_t hwm, std::size_t lwm, Allocator const& alloc = Allocator() ) : alloc_{ alloc }, tail_{ & head_ }, @@ -216,7 +216,7 @@ public: lwm_{ lwm } { if ( hwm_ <= lwm_) { throw fiber_error( std::make_error_code( std::errc::invalid_argument), - "boost fiber: high-watermark is less than or equal to low-watermark for bounded_channel"); + "boost fiber: high-watermark is less than or equal to low-watermark for bounded_queue"); } if ( 0 == hwm) { throw fiber_error( std::make_error_code( std::errc::invalid_argument), @@ -224,7 +224,7 @@ public: } } - bounded_channel( std::size_t wm, + bounded_queue( std::size_t wm, Allocator const& alloc = Allocator() ) : alloc_{ alloc }, tail_{ & head_ }, @@ -236,8 +236,8 @@ public: } } - bounded_channel( bounded_channel const&) = delete; - bounded_channel & operator=( bounded_channel const&) = delete; + bounded_queue( bounded_queue const&) = delete; + bounded_queue & operator=( bounded_queue const&) = delete; std::size_t upper_bound() const noexcept { return hwm_; @@ -252,7 +252,7 @@ public: close_( lk); } - channel_op_status push( value_type const& va) { + queue_op_status push( value_type const& va) { typename allocator_traits_t::pointer ptr{ allocator_traits_t::allocate( alloc_, 1) }; try { @@ -265,7 +265,7 @@ public: return push_( { detail::convert( ptr) }, lk); } - channel_op_status push( value_type && va) { + queue_op_status push( value_type && va) { typename allocator_traits_t::pointer ptr{ allocator_traits_t::allocate( alloc_, 1) }; try { @@ -280,21 +280,21 @@ public: } template< typename Rep, typename Period > - channel_op_status push_wait_for( value_type const& va, + queue_op_status push_wait_for( value_type const& va, std::chrono::duration< Rep, Period > const& timeout_duration) { return push_wait_until( va, std::chrono::steady_clock::now() + timeout_duration); } template< typename Rep, typename Period > - channel_op_status push_wait_for( value_type && va, + queue_op_status push_wait_for( value_type && va, std::chrono::duration< Rep, Period > const& timeout_duration) { return push_wait_until( std::forward< value_type >( va), std::chrono::steady_clock::now() + timeout_duration); } template< typename Clock, typename Duration > - channel_op_status push_wait_until( value_type const& va, + queue_op_status push_wait_until( value_type const& va, std::chrono::time_point< Clock, Duration > const& timeout_time) { typename allocator_traits_t::pointer ptr{ allocator_traits_t::allocate( alloc_, 1) }; @@ -309,7 +309,7 @@ public: } template< typename Clock, typename Duration > - channel_op_status push_wait_until( value_type && va, + queue_op_status push_wait_until( value_type && va, std::chrono::time_point< Clock, Duration > const& timeout_time) { typename allocator_traits_t::pointer ptr{ allocator_traits_t::allocate( alloc_, 1) }; @@ -324,7 +324,7 @@ public: return push_wait_until_( { detail::convert( ptr) }, timeout_time, lk); } - channel_op_status try_push( value_type const& va) { + queue_op_status try_push( value_type const& va) { typename allocator_traits_t::pointer ptr{ allocator_traits_t::allocate( alloc_, 1) }; try { @@ -337,7 +337,7 @@ public: return try_push_( { detail::convert( ptr) }, lk); } - channel_op_status try_push( value_type && va) { + queue_op_status try_push( value_type && va) { typename allocator_traits_t::pointer ptr{ allocator_traits_t::allocate( alloc_, 1) }; try { @@ -351,17 +351,17 @@ public: return try_push_( { detail::convert( ptr) }, lk); } - channel_op_status pop( value_type & va) { + queue_op_status pop( value_type & va) { std::unique_lock< mutex > lk( mtx_); not_empty_cond_.wait( lk, [this](){ return is_closed_() || ! is_empty_(); }); if ( is_closed_() && is_empty_() ) { - return channel_op_status::closed; + return queue_op_status::closed; } va = value_pop_( lk); - return channel_op_status::success; + return queue_op_status::success; } value_type value_pop() { @@ -378,33 +378,33 @@ public: return value_pop_( lk); } - channel_op_status try_pop( value_type & va) { + queue_op_status try_pop( value_type & va) { std::unique_lock< mutex > lk( mtx_); if ( is_closed_() && is_empty_() ) { // let other fibers run lk.unlock(); this_fiber::yield(); - return channel_op_status::closed; + return queue_op_status::closed; } if ( is_empty_() ) { // let other fibers run lk.unlock(); this_fiber::yield(); - return channel_op_status::empty; + return queue_op_status::empty; } va = value_pop_( lk); - return channel_op_status::success; + return queue_op_status::success; } template< typename Rep, typename Period > - channel_op_status pop_wait_for( value_type & va, + queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration) { return pop_wait_until( va, std::chrono::steady_clock::now() + timeout_duration); } template< typename Clock, typename Duration > - channel_op_status pop_wait_until( value_type & va, + queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time) { std::unique_lock< mutex > lk( mtx_); if ( ! not_empty_cond_.wait_until( lk, @@ -412,13 +412,13 @@ public: [this](){ return is_closed_() || ! is_empty_(); })) { - return channel_op_status::timeout; + return queue_op_status::timeout; } if ( is_closed_() && is_empty_() ) { - return channel_op_status::closed; + return queue_op_status::closed; } va = value_pop_( lk); - return channel_op_status::success; + return queue_op_status::success; } }; @@ -428,4 +428,4 @@ public: # include BOOST_ABI_SUFFIX #endif -#endif // BOOST_FIBERS_BOUNDED_CHANNEL_H +#endif // BOOST_FIBERS_BOUNDED_QUEUE_H diff --git a/include/boost/fiber/channel_op_status.hpp b/include/boost/fiber/queue_op_status.hpp similarity index 95% rename from include/boost/fiber/channel_op_status.hpp rename to include/boost/fiber/queue_op_status.hpp index 78fd9bd6..0dea5199 100644 --- a/include/boost/fiber/channel_op_status.hpp +++ b/include/boost/fiber/queue_op_status.hpp @@ -17,7 +17,7 @@ namespace boost { namespace fibers { -enum class channel_op_status { +enum class queue_op_status { success = 0, empty, full, diff --git a/include/boost/fiber/unbounded_channel.hpp b/include/boost/fiber/unbounded_queue.hpp similarity index 84% rename from include/boost/fiber/unbounded_channel.hpp rename to include/boost/fiber/unbounded_queue.hpp index 58c96608..5c9306d8 100644 --- a/include/boost/fiber/unbounded_channel.hpp +++ b/include/boost/fiber/unbounded_queue.hpp @@ -5,8 +5,8 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_FIBERS_UNBOUNDED_CHANNEL_H -#define BOOST_FIBERS_UNBOUNDED_CHANNEL_H +#ifndef BOOST_FIBERS_UNBOUNDED_QUEUE_H +#define BOOST_FIBERS_UNBOUNDED_QUEUE_H #include #include @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -38,7 +38,7 @@ namespace fibers { template< typename T, typename Allocator = std::allocator< T > > -class unbounded_channel { +class unbounded_queue { public: typedef T value_type; @@ -114,20 +114,20 @@ private: return ! head_; } - channel_op_status push_( ptr_t new_node, + queue_op_status push_( ptr_t new_node, std::unique_lock< mutex > & lk) noexcept { if ( is_closed_() ) { - return channel_op_status::closed; + return queue_op_status::closed; } return push_and_notify_( new_node, lk); } - channel_op_status push_and_notify_( ptr_t new_node, + queue_op_status push_and_notify_( ptr_t new_node, std::unique_lock< mutex > & lk) noexcept { push_tail_( new_node); lk.unlock(); not_empty_cond_.notify_one(); - return channel_op_status::success; + return queue_op_status::success; } void push_tail_( ptr_t new_node) noexcept { @@ -152,20 +152,20 @@ private: } public: - explicit unbounded_channel( Allocator const& alloc = Allocator() ) noexcept : + explicit unbounded_queue( Allocator const& alloc = Allocator() ) noexcept : alloc_{ alloc }, tail_{ & head_ } { } - unbounded_channel( unbounded_channel const&) = delete; - unbounded_channel & operator=( unbounded_channel const&) = delete; + unbounded_queue( unbounded_queue const&) = delete; + unbounded_queue & operator=( unbounded_queue const&) = delete; void close() noexcept { std::unique_lock< mutex > lk( mtx_); close_( lk); } - channel_op_status push( value_type const& va) { + queue_op_status push( value_type const& va) { typename allocator_traits_t::pointer ptr{ allocator_traits_t::allocate( alloc_, 1) }; try { @@ -178,7 +178,7 @@ public: return push_( { detail::convert( ptr) }, lk); } - channel_op_status push( value_type && va) { + queue_op_status push( value_type && va) { typename allocator_traits_t::pointer ptr{ allocator_traits_t::allocate( alloc_, 1) }; try { @@ -192,17 +192,17 @@ public: return push_( { detail::convert( ptr) }, lk); } - channel_op_status pop( value_type & va) { + queue_op_status pop( value_type & va) { std::unique_lock< mutex > lk( mtx_); not_empty_cond_.wait( lk, [this](){ return is_closed_() || ! is_empty_(); }); if ( is_closed_() && is_empty_() ) { - return channel_op_status::closed; + return queue_op_status::closed; } va = value_pop_( lk); - return channel_op_status::success; + return queue_op_status::success; } value_type value_pop() { @@ -219,45 +219,45 @@ public: return value_pop_( lk); } - channel_op_status try_pop( value_type & va) { + queue_op_status try_pop( value_type & va) { std::unique_lock< mutex > lk( mtx_); if ( is_closed_() && is_empty_() ) { // let other fibers run lk.unlock(); this_fiber::yield(); - return channel_op_status::closed; + return queue_op_status::closed; } if ( is_empty_() ) { // let other fibers run lk.unlock(); this_fiber::yield(); - return channel_op_status::empty; + return queue_op_status::empty; } va = value_pop_( lk); - return channel_op_status::success; + return queue_op_status::success; } template< typename Rep, typename Period > - channel_op_status pop_wait_for( value_type & va, + queue_op_status pop_wait_for( value_type & va, std::chrono::duration< Rep, Period > const& timeout_duration) { return pop_wait_until( va, std::chrono::steady_clock::now() + timeout_duration); } template< typename Clock, typename Duration > - channel_op_status pop_wait_until( value_type & va, + queue_op_status pop_wait_until( value_type & va, std::chrono::time_point< Clock, Duration > const& timeout_time) { std::unique_lock< mutex > lk( mtx_); if ( ! not_empty_cond_.wait_until( lk, timeout_time, [this](){ return is_closed_() || ! is_empty_(); })) { - return channel_op_status::timeout; + return queue_op_status::timeout; } if ( is_closed_() && is_empty_() ) { - return channel_op_status::closed; + return queue_op_status::closed; } va = value_pop_( lk); - return channel_op_status::success; + return queue_op_status::success; } }; @@ -267,4 +267,4 @@ public: # include BOOST_ABI_SUFFIX #endif -#endif // BOOST_FIBERS_UNBOUNDED_CHANNEL_H +#endif // BOOST_FIBERS_UNBOUNDED_QUEUE_H diff --git a/performance/fiber/skynet.cpp b/performance/fiber/skynet.cpp index c4c67853..d35d72cd 100644 --- a/performance/fiber/skynet.cpp +++ b/performance/fiber/skynet.cpp @@ -18,7 +18,7 @@ using clock_type = std::chrono::steady_clock; using duration_type = clock_type::duration; using time_point_type = clock_type::time_point; -using channel_type = boost::fibers::unbounded_channel< std::uint64_t >; +using queue_type = boost::fibers::unbounded_queue< std::uint64_t >; using allocator_type = boost::fibers::pooled_fixedsize_stack; void prepare( allocator_type & salloc) { @@ -27,11 +27,11 @@ void prepare( allocator_type & salloc) { }; // microbenchmark -void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { +void skynet( allocator_type & salloc, queue_type & c, std::size_t num, std::size_t size, std::size_t div) { if ( 1 == size) { c.push( num); } else { - channel_type rc; + queue_type rc; for ( std::size_t i = 0; i < div; ++i) { auto sub_num = num + i * size / div; boost::fibers::fiber{ boost::fibers::launch::dispatch, @@ -57,7 +57,7 @@ int main() { prepare( salloc); std::uint64_t result{ 0 }; duration_type duration{ duration_type::zero() }; - channel_type rc; + queue_type rc; time_point_type start{ clock_type::now() }; skynet( salloc, rc, 0, size, div); result = rc.value_pop(); diff --git a/performance/fiber/skynet_shared.cpp b/performance/fiber/skynet_shared.cpp index 87d45da7..d7e5b3f9 100644 --- a/performance/fiber/skynet_shared.cpp +++ b/performance/fiber/skynet_shared.cpp @@ -24,7 +24,7 @@ #include "bind/bind_processor.hpp" using allocator_type = boost::fibers::fixedsize_stack; -using channel_type = boost::fibers::unbounded_channel< std::uint64_t >; +using queue_type = boost::fibers::unbounded_queue< std::uint64_t >; using clock_type = std::chrono::steady_clock; using duration_type = clock_type::duration; using lock_type = std::unique_lock< std::mutex >; @@ -35,11 +35,11 @@ static std::mutex mtx{}; static boost::fibers::condition_variable_any cnd{}; // microbenchmark -void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { +void skynet( allocator_type & salloc, queue_type & c, std::size_t num, std::size_t size, std::size_t div) { if ( 1 == size) { c.push( num); } else { - channel_type rc; + queue_type rc; for ( std::size_t i = 0; i < div; ++i) { auto sub_num = num + i * size / div; boost::fibers::fiber{ boost::fibers::launch::dispatch, @@ -80,7 +80,7 @@ int main() { allocator_type salloc{ stack_size }; std::uint64_t result{ 0 }; duration_type duration{ duration_type::zero() }; - channel_type rc; + queue_type rc; b.wait(); time_point_type start{ clock_type::now() }; skynet( salloc, rc, 0, size, div); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index f4d65f28..290e9b1a 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -179,7 +179,7 @@ test-suite minimal : cxx11_thread_local cxx11_variadic_templates ] ] -[ run test_unbounded_channel_post.cpp : +[ run test_unbounded_queue_post.cpp : : : [ requires cxx11_auto_declarations cxx11_constexpr @@ -194,7 +194,7 @@ test-suite minimal : cxx11_thread_local cxx11_variadic_templates ] ] -[ run test_unbounded_channel_dispatch.cpp : +[ run test_unbounded_queue_dispatch.cpp : : : [ requires cxx11_auto_declarations cxx11_constexpr @@ -209,7 +209,7 @@ test-suite minimal : cxx11_thread_local cxx11_variadic_templates ] ] -[ run test_bounded_channel_post.cpp : +[ run test_bounded_queue_post.cpp : : : [ requires cxx11_auto_declarations cxx11_constexpr @@ -224,7 +224,7 @@ test-suite minimal : cxx11_thread_local cxx11_variadic_templates ] ] -[ run test_bounded_channel_dispatch.cpp : +[ run test_bounded_queue_dispatch.cpp : : : [ requires cxx11_auto_declarations cxx11_constexpr diff --git a/test/test_bounded_channel_dispatch.cpp b/test/test_bounded_queue_dispatch.cpp similarity index 66% rename from test/test_bounded_channel_dispatch.cpp rename to test/test_bounded_queue_dispatch.cpp index 3d831fc6..a8c489b5 100644 --- a/test/test_bounded_channel_dispatch.cpp +++ b/test/test_bounded_queue_dispatch.cpp @@ -48,7 +48,7 @@ struct moveable { void test_zero_wm_1() { bool thrown = false; try { - boost::fibers::bounded_channel< int > c( 0); + boost::fibers::bounded_queue< int > c( 0); } catch ( boost::fibers::fiber_error const&) { thrown = true; } @@ -58,7 +58,7 @@ void test_zero_wm_1() { void test_zero_wm_2() { bool thrown = false; try { - boost::fibers::bounded_channel< int > c( 0, 0); + boost::fibers::bounded_queue< int > c( 0, 0); } catch ( boost::fibers::fiber_error const&) { thrown = true; } @@ -68,7 +68,7 @@ void test_zero_wm_2() { void test_hwm_less_lwm() { bool thrown = false; try { - boost::fibers::bounded_channel< int > c( 2, 3); + boost::fibers::bounded_queue< int > c( 2, 3); } catch ( boost::fibers::fiber_error const&) { thrown = true; } @@ -78,7 +78,7 @@ void test_hwm_less_lwm() { void test_hwm_equal_lwm() { bool thrown = false; try { - boost::fibers::bounded_channel< int > c( 3, 3); + boost::fibers::bounded_queue< int > c( 3, 3); } catch ( boost::fibers::fiber_error const&) { thrown = true; } @@ -86,101 +86,101 @@ void test_hwm_equal_lwm() { } void test_push() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); BOOST_CHECK_EQUAL( c.upper_bound(), 10u ); BOOST_CHECK_EQUAL( c.lower_bound(), 9u ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); } void test_push_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.push( 1) ); } void test_try_push() { - boost::fibers::bounded_channel< int > c( 1); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + boost::fibers::bounded_queue< int > c( 1); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); } void test_try_push_closed() { - boost::fibers::bounded_channel< int > c( 1); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) ); + boost::fibers::bounded_queue< int > c( 1); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_push( 1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.try_push( 2) ); } void test_try_push_full() { - boost::fibers::bounded_channel< int > c( 1); + boost::fibers::bounded_queue< int > c( 1); BOOST_CHECK_EQUAL( c.lower_bound(), 0u ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) ); - BOOST_CHECK( boost::fibers::channel_op_status::full == c.try_push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::full == c.try_push( 2) ); } void test_push_wait_for() { - boost::fibers::bounded_channel< int > c( 2); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + boost::fibers::bounded_queue< int > c( 2); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); } void test_push_wait_for_closed() { - boost::fibers::bounded_channel< int > c( 1); + boost::fibers::bounded_queue< int > c( 1); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); } void test_push_wait_for_timeout() { - boost::fibers::bounded_channel< int > c( 1); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + boost::fibers::bounded_queue< int > c( 1); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); } void test_push_wait_until() { - boost::fibers::bounded_channel< int > c( 2); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + boost::fibers::bounded_queue< int > c( 2); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push_wait_until( 1, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_push_wait_until_closed() { - boost::fibers::bounded_channel< int > c( 1); + boost::fibers::bounded_queue< int > c( 1); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_until( 1, + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.push_wait_until( 1, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_push_wait_until_timeout() { - boost::fibers::bounded_channel< int > c( 1); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + boost::fibers::bounded_queue< int > c( 1); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push_wait_until( 1, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_until( 1, + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.push_wait_until( 1, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_pop() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop( v2) ); } void test_pop_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -188,17 +188,17 @@ void test_pop_success() { } void test_value_pop() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); v2 = c.value_pop(); BOOST_CHECK_EQUAL( v1, v2); } void test_value_pop_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); v2 = c.value_pop(); BOOST_CHECK_EQUAL( v1, v2); @@ -212,13 +212,13 @@ void test_value_pop_closed() { } void test_value_pop_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ v2 = c.value_pop(); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -226,31 +226,31 @@ void test_value_pop_success() { } void test_try_pop() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); } void test_try_pop_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.try_pop( v2) ); } void test_try_pop_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ - while ( boost::fibers::channel_op_status::success != c.try_pop( v2) ); + while ( boost::fibers::queue_op_status::success != c.try_pop( v2) ); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -258,31 +258,31 @@ void test_try_pop_success() { } void test_pop_wait_for() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_wait_for_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); } void test_pop_wait_for_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -290,44 +290,44 @@ void test_pop_wait_for_success() { } void test_pop_wait_for_timeout() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v = 0; boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v](){ - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); }); f.join(); } void test_pop_wait_until() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_wait_until_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_pop_wait_until_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -335,35 +335,35 @@ void test_pop_wait_until_success() { } void test_pop_wait_until_timeout() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v = 0; boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v](){ - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_until( v, + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.pop_wait_until( v, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); }); f.join(); } void test_wm_1() { - boost::fibers::bounded_channel< int > c( 3); + boost::fibers::bounded_queue< int > c( 3); std::vector< boost::fibers::fiber::id > ids; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&ids](){ ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 2) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 3) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 4) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 5) ); ids.push_back( boost::this_fiber::get_id() ); }); @@ -384,7 +384,7 @@ void test_wm_1() { BOOST_CHECK_EQUAL( 4, c.value_pop() ); ids.push_back( boost::this_fiber::get_id() ); - // would block because channel is empty + // would block because queue is empty BOOST_CHECK_EQUAL( 5, c.value_pop() ); ids.push_back( boost::this_fiber::get_id() ); @@ -397,37 +397,37 @@ void test_wm_1() { BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1 BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2 BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3 - BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (channel is full) + BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (queue is full) BOOST_CHECK_EQUAL( id2, ids[4]); // f2 resumes and pops 1, f1 gets ready to push 4, f2 yields - BOOST_CHECK_EQUAL( id1, ids[5]); // f1 resumes and pushes 4, blocks in push( 5) (channel full) + BOOST_CHECK_EQUAL( id1, ids[5]); // f1 resumes and pushes 4, blocks in push( 5) (queue full) BOOST_CHECK_EQUAL( id2, ids[6]); // f2 resumes and pops 2 BOOST_CHECK_EQUAL( id2, ids[7]); // f2 pops 3 BOOST_CHECK_EQUAL( id2, ids[8]); // f2 pops 4 - BOOST_CHECK_EQUAL( id2, ids[9]); // f2 blocks in pop() (channel is empty) + BOOST_CHECK_EQUAL( id2, ids[9]); // f2 blocks in pop() (queue is empty) BOOST_CHECK_EQUAL( id1, ids[10]); // f1 resumes and pushes 4, completes BOOST_CHECK_EQUAL( id2, ids[11]); // f2 resumes and pops 5, completes } void test_wm_2() { - boost::fibers::bounded_channel< int > c( 3); + boost::fibers::bounded_queue< int > c( 3); std::vector< boost::fibers::fiber::id > ids; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&ids](){ ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 2) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 3) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 4) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 5) ); ids.push_back( boost::this_fiber::get_id() ); }); @@ -463,9 +463,9 @@ void test_wm_2() { BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1 BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2 BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3 - BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (channel is full) + BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (queue is full) BOOST_CHECK_EQUAL( id2, ids[4]); // f2 resumes and pops 1, f1 gets ready to push 4, f2 yields - BOOST_CHECK_EQUAL( id1, ids[5]); // f1 resumes and pushes 4, blocks in push( 5) (channel full) + BOOST_CHECK_EQUAL( id1, ids[5]); // f1 resumes and pushes 4, blocks in push( 5) (queue full) BOOST_CHECK_EQUAL( id2, ids[6]); // f2 resumes and pops 2, f1 gets ready tp push 5, f2 yields BOOST_CHECK_EQUAL( id1, ids[7]); // f1 resumes and pushes 5, completes BOOST_CHECK_EQUAL( id2, ids[8]); // f2 resumes and pops 3 @@ -475,26 +475,26 @@ void test_wm_2() { } void test_wm_3() { - boost::fibers::bounded_channel< int > c( 3, 1); + boost::fibers::bounded_queue< int > c( 3, 1); BOOST_CHECK_EQUAL( c.upper_bound(), 3u ); BOOST_CHECK_EQUAL( c.lower_bound(), 1u ); std::vector< boost::fibers::fiber::id > ids; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&ids](){ ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 2) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 3) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 4) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 5) ); ids.push_back( boost::this_fiber::get_id() ); }); @@ -530,7 +530,7 @@ void test_wm_3() { BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1 BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2 BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3 - BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (channel is full) + BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (queue is full) BOOST_CHECK_EQUAL( id2, ids[4]); // f2 resumes and pops 1, f1 gets NOT ready to push 4, f2 yields BOOST_CHECK_EQUAL( id2, ids[5]); // f2 pops 2, f1 gets ready to push 4 (lwm == size == 1), f2 yields BOOST_CHECK_EQUAL( id1, ids[6]); // f1 resumes and pushes 4 + 5 @@ -542,21 +542,21 @@ void test_wm_3() { } void test_wm_4() { - boost::fibers::bounded_channel< int > c( 3, 1); + boost::fibers::bounded_queue< int > c( 3, 1); std::vector< boost::fibers::fiber::id > ids; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&ids](){ ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 2) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 3) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 4) ); ids.push_back( boost::this_fiber::get_id() ); }); @@ -580,7 +580,7 @@ void test_wm_4() { boost::this_fiber::yield(); ids.push_back( boost::this_fiber::get_id() ); - // would block because channel is empty + // would block because queue is empty BOOST_CHECK_EQUAL( 4, c.value_pop() ); ids.push_back( boost::this_fiber::get_id() ); @@ -593,7 +593,7 @@ void test_wm_4() { BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1 BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2 BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3 - BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) ( channel full) + BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) ( queue full) BOOST_CHECK_EQUAL( id2, ids[4]); // f2 resumes and pops 1, f1 gets NOT ready to push 4, f2 yields BOOST_CHECK_EQUAL( id2, ids[5]); // f2 pops 2, f1 gets ready to push 4 (lwm == size == 1), f2 yields BOOST_CHECK_EQUAL( id1, ids[6]); // f1 resumes and pushes 4, completes @@ -603,16 +603,16 @@ void test_wm_4() { } void test_moveable() { - boost::fibers::bounded_channel< moveable > c( 10); + boost::fibers::bounded_queue< moveable > c( 10); moveable m1( 3), m2; BOOST_CHECK( m1.state); BOOST_CHECK_EQUAL( 3, m1.value); BOOST_CHECK( ! m2.state); BOOST_CHECK_EQUAL( -1, m2.value); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( std::move( m1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( std::move( m1) ) ); BOOST_CHECK( ! m1.state); BOOST_CHECK( ! m2.state); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( m2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( m2) ); BOOST_CHECK( ! m1.state); BOOST_CHECK_EQUAL( -1, m1.value); BOOST_CHECK( m2.state); @@ -621,7 +621,7 @@ void test_moveable() { boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { boost::unit_test::test_suite * test = - BOOST_TEST_SUITE("Boost.Fiber: bounded_channel test suite"); + BOOST_TEST_SUITE("Boost.Fiber: bounded_queue test suite"); test->add( BOOST_TEST_CASE( & test_zero_wm_1) ); test->add( BOOST_TEST_CASE( & test_zero_wm_2) ); diff --git a/test/test_bounded_channel_post.cpp b/test/test_bounded_queue_post.cpp similarity index 66% rename from test/test_bounded_channel_post.cpp rename to test/test_bounded_queue_post.cpp index b9fde98f..56b816b9 100644 --- a/test/test_bounded_channel_post.cpp +++ b/test/test_bounded_queue_post.cpp @@ -48,7 +48,7 @@ struct moveable { void test_zero_wm_1() { bool thrown = false; try { - boost::fibers::bounded_channel< int > c( 0); + boost::fibers::bounded_queue< int > c( 0); } catch ( boost::fibers::fiber_error const&) { thrown = true; } @@ -58,7 +58,7 @@ void test_zero_wm_1() { void test_zero_wm_2() { bool thrown = false; try { - boost::fibers::bounded_channel< int > c( 0, 0); + boost::fibers::bounded_queue< int > c( 0, 0); } catch ( boost::fibers::fiber_error const&) { thrown = true; } @@ -68,7 +68,7 @@ void test_zero_wm_2() { void test_hwm_less_lwm() { bool thrown = false; try { - boost::fibers::bounded_channel< int > c( 2, 3); + boost::fibers::bounded_queue< int > c( 2, 3); } catch ( boost::fibers::fiber_error const&) { thrown = true; } @@ -78,7 +78,7 @@ void test_hwm_less_lwm() { void test_hwm_equal_lwm() { bool thrown = false; try { - boost::fibers::bounded_channel< int > c( 3, 3); + boost::fibers::bounded_queue< int > c( 3, 3); } catch ( boost::fibers::fiber_error const&) { thrown = true; } @@ -86,101 +86,101 @@ void test_hwm_equal_lwm() { } void test_push() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); BOOST_CHECK_EQUAL( c.upper_bound(), 10u ); BOOST_CHECK_EQUAL( c.lower_bound(), 9u ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); } void test_push_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.push( 1) ); } void test_try_push() { - boost::fibers::bounded_channel< int > c( 1); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + boost::fibers::bounded_queue< int > c( 1); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); } void test_try_push_closed() { - boost::fibers::bounded_channel< int > c( 1); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) ); + boost::fibers::bounded_queue< int > c( 1); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_push( 1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.try_push( 2) ); } void test_try_push_full() { - boost::fibers::bounded_channel< int > c( 1); + boost::fibers::bounded_queue< int > c( 1); BOOST_CHECK_EQUAL( c.lower_bound(), 0u ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) ); - BOOST_CHECK( boost::fibers::channel_op_status::full == c.try_push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::full == c.try_push( 2) ); } void test_push_wait_for() { - boost::fibers::bounded_channel< int > c( 2); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + boost::fibers::bounded_queue< int > c( 2); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); } void test_push_wait_for_closed() { - boost::fibers::bounded_channel< int > c( 1); + boost::fibers::bounded_queue< int > c( 1); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); } void test_push_wait_for_timeout() { - boost::fibers::bounded_channel< int > c( 1); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + boost::fibers::bounded_queue< int > c( 1); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); } void test_push_wait_until() { - boost::fibers::bounded_channel< int > c( 2); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + boost::fibers::bounded_queue< int > c( 2); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push_wait_until( 1, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_push_wait_until_closed() { - boost::fibers::bounded_channel< int > c( 1); + boost::fibers::bounded_queue< int > c( 1); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_until( 1, + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.push_wait_until( 1, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_push_wait_until_timeout() { - boost::fibers::bounded_channel< int > c( 1); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + boost::fibers::bounded_queue< int > c( 1); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push_wait_until( 1, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_until( 1, + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.push_wait_until( 1, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_pop() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop( v2) ); } void test_pop_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -188,17 +188,17 @@ void test_pop_success() { } void test_value_pop() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); v2 = c.value_pop(); BOOST_CHECK_EQUAL( v1, v2); } void test_value_pop_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); v2 = c.value_pop(); BOOST_CHECK_EQUAL( v1, v2); @@ -212,13 +212,13 @@ void test_value_pop_closed() { } void test_value_pop_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ v2 = c.value_pop(); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -226,31 +226,31 @@ void test_value_pop_success() { } void test_try_pop() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); } void test_try_pop_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.try_pop( v2) ); } void test_try_pop_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ - while ( boost::fibers::channel_op_status::success != c.try_pop( v2) ); + while ( boost::fibers::queue_op_status::success != c.try_pop( v2) ); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -258,31 +258,31 @@ void test_try_pop_success() { } void test_pop_wait_for() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_wait_for_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); } void test_pop_wait_for_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -290,44 +290,44 @@ void test_pop_wait_for_success() { } void test_pop_wait_for_timeout() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v = 0; boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v](){ - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); }); f.join(); } void test_pop_wait_until() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_wait_until_closed() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_pop_wait_until_success() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -335,35 +335,35 @@ void test_pop_wait_until_success() { } void test_pop_wait_until_timeout() { - boost::fibers::bounded_channel< int > c( 10); + boost::fibers::bounded_queue< int > c( 10); int v = 0; boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v](){ - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_until( v, + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.pop_wait_until( v, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); }); f.join(); } void test_wm_1() { - boost::fibers::bounded_channel< int > c( 3); + boost::fibers::bounded_queue< int > c( 3); std::vector< boost::fibers::fiber::id > ids; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&ids](){ ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 2) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 3) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 4) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 5) ); ids.push_back( boost::this_fiber::get_id() ); }); @@ -384,7 +384,7 @@ void test_wm_1() { BOOST_CHECK_EQUAL( 4, c.value_pop() ); ids.push_back( boost::this_fiber::get_id() ); - // would block because channel is empty + // would block because queue is empty BOOST_CHECK_EQUAL( 5, c.value_pop() ); ids.push_back( boost::this_fiber::get_id() ); @@ -397,37 +397,37 @@ void test_wm_1() { BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1 BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2 BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3 - BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (channel is full) + BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (queue is full) BOOST_CHECK_EQUAL( id2, ids[4]); // f2 resumes and pops 1, f1 gets ready to push 4, f2 yields - BOOST_CHECK_EQUAL( id1, ids[5]); // f1 resumes and pushes 4, blocks in push( 5) (channel full) + BOOST_CHECK_EQUAL( id1, ids[5]); // f1 resumes and pushes 4, blocks in push( 5) (queue full) BOOST_CHECK_EQUAL( id2, ids[6]); // f2 resumes and pops 2 BOOST_CHECK_EQUAL( id2, ids[7]); // f2 pops 3 BOOST_CHECK_EQUAL( id2, ids[8]); // f2 pops 4 - BOOST_CHECK_EQUAL( id2, ids[9]); // f2 blocks in pop() (channel is empty) + BOOST_CHECK_EQUAL( id2, ids[9]); // f2 blocks in pop() (queue is empty) BOOST_CHECK_EQUAL( id1, ids[10]); // f1 resumes and pushes 4, completes BOOST_CHECK_EQUAL( id2, ids[11]); // f2 resumes and pops 5, completes } void test_wm_2() { - boost::fibers::bounded_channel< int > c( 3); + boost::fibers::bounded_queue< int > c( 3); std::vector< boost::fibers::fiber::id > ids; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&ids](){ ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 2) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 3) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 4) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 5) ); ids.push_back( boost::this_fiber::get_id() ); }); @@ -463,9 +463,9 @@ void test_wm_2() { BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1 BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2 BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3 - BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (channel is full) + BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (queue is full) BOOST_CHECK_EQUAL( id2, ids[4]); // f2 resumes and pops 1, f1 gets ready to push 4, f2 yields - BOOST_CHECK_EQUAL( id1, ids[5]); // f1 resumes and pushes 4, blocks in push( 5) (channel full) + BOOST_CHECK_EQUAL( id1, ids[5]); // f1 resumes and pushes 4, blocks in push( 5) (queue full) BOOST_CHECK_EQUAL( id2, ids[6]); // f2 resumes and pops 2, f1 gets ready tp push 5, f2 yields BOOST_CHECK_EQUAL( id1, ids[7]); // f1 resumes and pushes 5, completes BOOST_CHECK_EQUAL( id2, ids[8]); // f2 resumes and pops 3 @@ -475,26 +475,26 @@ void test_wm_2() { } void test_wm_3() { - boost::fibers::bounded_channel< int > c( 3, 1); + boost::fibers::bounded_queue< int > c( 3, 1); BOOST_CHECK_EQUAL( c.upper_bound(), 3u ); BOOST_CHECK_EQUAL( c.lower_bound(), 1u ); std::vector< boost::fibers::fiber::id > ids; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&ids](){ ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 2) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 3) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 4) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 5) ); ids.push_back( boost::this_fiber::get_id() ); }); @@ -530,7 +530,7 @@ void test_wm_3() { BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1 BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2 BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3 - BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (channel is full) + BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) (queue is full) BOOST_CHECK_EQUAL( id2, ids[4]); // f2 resumes and pops 1, f1 gets NOT ready to push 4, f2 yields BOOST_CHECK_EQUAL( id2, ids[5]); // f2 pops 2, f1 gets ready to push 4 (lwm == size == 1), f2 yields BOOST_CHECK_EQUAL( id1, ids[6]); // f1 resumes and pushes 4 + 5 @@ -542,21 +542,21 @@ void test_wm_3() { } void test_wm_4() { - boost::fibers::bounded_channel< int > c( 3, 1); + boost::fibers::bounded_queue< int > c( 3, 1); std::vector< boost::fibers::fiber::id > ids; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&ids](){ ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 2) ); ids.push_back( boost::this_fiber::get_id() ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 3) ); ids.push_back( boost::this_fiber::get_id() ); - // would be blocked because channel is full - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + // would be blocked because queue is full + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 4) ); ids.push_back( boost::this_fiber::get_id() ); }); @@ -580,7 +580,7 @@ void test_wm_4() { boost::this_fiber::yield(); ids.push_back( boost::this_fiber::get_id() ); - // would block because channel is empty + // would block because queue is empty BOOST_CHECK_EQUAL( 4, c.value_pop() ); ids.push_back( boost::this_fiber::get_id() ); @@ -593,7 +593,7 @@ void test_wm_4() { BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1 BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2 BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3 - BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) ( channel full) + BOOST_CHECK_EQUAL( id1, ids[3]); // f1 blocks in push( 4) ( queue full) BOOST_CHECK_EQUAL( id2, ids[4]); // f2 resumes and pops 1, f1 gets NOT ready to push 4, f2 yields BOOST_CHECK_EQUAL( id2, ids[5]); // f2 pops 2, f1 gets ready to push 4 (lwm == size == 1), f2 yields BOOST_CHECK_EQUAL( id1, ids[6]); // f1 resumes and pushes 4, completes @@ -603,16 +603,16 @@ void test_wm_4() { } void test_moveable() { - boost::fibers::bounded_channel< moveable > c( 10); + boost::fibers::bounded_queue< moveable > c( 10); moveable m1( 3), m2; BOOST_CHECK( m1.state); BOOST_CHECK_EQUAL( 3, m1.value); BOOST_CHECK( ! m2.state); BOOST_CHECK_EQUAL( -1, m2.value); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( std::move( m1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( std::move( m1) ) ); BOOST_CHECK( ! m1.state); BOOST_CHECK( ! m2.state); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( m2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( m2) ); BOOST_CHECK( ! m1.state); BOOST_CHECK_EQUAL( -1, m1.value); BOOST_CHECK( m2.state); @@ -621,7 +621,7 @@ void test_moveable() { boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { boost::unit_test::test_suite * test = - BOOST_TEST_SUITE("Boost.Fiber: bounded_channel test suite"); + BOOST_TEST_SUITE("Boost.Fiber: bounded_queue test suite"); test->add( BOOST_TEST_CASE( & test_zero_wm_1) ); test->add( BOOST_TEST_CASE( & test_zero_wm_2) ); diff --git a/test/test_unbounded_channel_dispatch.cpp b/test/test_unbounded_queue_dispatch.cpp similarity index 59% rename from test/test_unbounded_channel_dispatch.cpp rename to test/test_unbounded_queue_dispatch.cpp index 908cecbd..935c9e66 100644 --- a/test/test_unbounded_channel_dispatch.cpp +++ b/test/test_unbounded_queue_dispatch.cpp @@ -45,42 +45,42 @@ struct moveable { }; void test_push() { - boost::fibers::unbounded_channel< int > c; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + boost::fibers::unbounded_queue< int > c; + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); } void test_push_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.push( 1) ); } void test_pop() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop( v2) ); } void test_pop_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -88,17 +88,17 @@ void test_pop_success() { } void test_value_pop() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); v2 = c.value_pop(); BOOST_CHECK_EQUAL( v1, v2); } void test_value_pop_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); v2 = c.value_pop(); BOOST_CHECK_EQUAL( v1, v2); @@ -112,13 +112,13 @@ void test_value_pop_closed() { } void test_value_pop_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ v2 = c.value_pop(); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -126,31 +126,31 @@ void test_value_pop_success() { } void test_try_pop() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); } void test_try_pop_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.try_pop( v2) ); } void test_try_pop_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ - while ( boost::fibers::channel_op_status::success != c.try_pop( v2) ); + while ( boost::fibers::queue_op_status::success != c.try_pop( v2) ); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -158,31 +158,31 @@ void test_try_pop_success() { } void test_pop_wait_for() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_wait_for_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); } void test_pop_wait_for_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -190,44 +190,44 @@ void test_pop_wait_for_success() { } void test_pop_wait_for_timeout() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v = 0; boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v](){ - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); }); f.join(); } void test_pop_wait_until() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_wait_until_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_pop_wait_until_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); }); boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -235,26 +235,26 @@ void test_pop_wait_until_success() { } void test_pop_wait_until_timeout() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v = 0; boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v](){ - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_until( v, + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.pop_wait_until( v, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); }); f.join(); } void test_moveable() { - boost::fibers::unbounded_channel< moveable > c; + boost::fibers::unbounded_queue< moveable > c; moveable m1( 3), m2; BOOST_CHECK( m1.state); BOOST_CHECK_EQUAL( 3, m1.value); BOOST_CHECK( ! m2.state); BOOST_CHECK_EQUAL( -1, m2.value); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( std::move( m1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( std::move( m1) ) ); BOOST_CHECK( ! m1.state); BOOST_CHECK( ! m2.state); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( m2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( m2) ); BOOST_CHECK( ! m1.state); BOOST_CHECK_EQUAL( -1, m1.value); BOOST_CHECK( m2.state); @@ -263,7 +263,7 @@ void test_moveable() { boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { boost::unit_test::test_suite * test = - BOOST_TEST_SUITE("Boost.Fiber: unbounded_channel test suite"); + BOOST_TEST_SUITE("Boost.Fiber: unbounded_queue test suite"); test->add( BOOST_TEST_CASE( & test_push) ); test->add( BOOST_TEST_CASE( & test_push_closed) ); diff --git a/test/test_unbounded_channel_post.cpp b/test/test_unbounded_queue_post.cpp similarity index 59% rename from test/test_unbounded_channel_post.cpp rename to test/test_unbounded_queue_post.cpp index 86782e5d..59eeb733 100644 --- a/test/test_unbounded_channel_post.cpp +++ b/test/test_unbounded_queue_post.cpp @@ -45,42 +45,42 @@ struct moveable { }; void test_push() { - boost::fibers::unbounded_channel< int > c; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + boost::fibers::unbounded_queue< int > c; + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( 1) ); } void test_push_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push( 1) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.push( 1) ); } void test_pop() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop( v2) ); } void test_pop_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( v2) ); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -88,17 +88,17 @@ void test_pop_success() { } void test_value_pop() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); v2 = c.value_pop(); BOOST_CHECK_EQUAL( v1, v2); } void test_value_pop_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); v2 = c.value_pop(); BOOST_CHECK_EQUAL( v1, v2); @@ -112,13 +112,13 @@ void test_value_pop_closed() { } void test_value_pop_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ v2 = c.value_pop(); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -126,31 +126,31 @@ void test_value_pop_success() { } void test_try_pop() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); } void test_try_pop_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.try_pop( v2) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_pop( v2) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.try_pop( v2) ); } void test_try_pop_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ - while ( boost::fibers::channel_op_status::success != c.try_pop( v2) ); + while ( boost::fibers::queue_op_status::success != c.try_pop( v2) ); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -158,31 +158,31 @@ void test_try_pop_success() { } void test_pop_wait_for() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_wait_for_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); } void test_pop_wait_for_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -190,44 +190,44 @@ void test_pop_wait_for_success() { } void test_pop_wait_for_timeout() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v = 0; boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v](){ - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); }); f.join(); } void test_pop_wait_until() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); } void test_pop_wait_until_closed() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); c.close(); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); BOOST_CHECK_EQUAL( v1, v2); - BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::closed == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); } void test_pop_wait_until_success() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v1 = 2, v2 = 0; boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop_wait_until( v2, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); }); boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( v1) ); }); f1.join(); f2.join(); @@ -235,26 +235,26 @@ void test_pop_wait_until_success() { } void test_pop_wait_until_timeout() { - boost::fibers::unbounded_channel< int > c; + boost::fibers::unbounded_queue< int > c; int v = 0; boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v](){ - BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_until( v, + BOOST_CHECK( boost::fibers::queue_op_status::timeout == c.pop_wait_until( v, std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); }); f.join(); } void test_moveable() { - boost::fibers::unbounded_channel< moveable > c; + boost::fibers::unbounded_queue< moveable > c; moveable m1( 3), m2; BOOST_CHECK( m1.state); BOOST_CHECK_EQUAL( 3, m1.value); BOOST_CHECK( ! m2.state); BOOST_CHECK_EQUAL( -1, m2.value); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( std::move( m1) ) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.push( std::move( m1) ) ); BOOST_CHECK( ! m1.state); BOOST_CHECK( ! m2.state); - BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( m2) ); + BOOST_CHECK( boost::fibers::queue_op_status::success == c.pop( m2) ); BOOST_CHECK( ! m1.state); BOOST_CHECK_EQUAL( -1, m1.value); BOOST_CHECK( m2.state); @@ -263,7 +263,7 @@ void test_moveable() { boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { boost::unit_test::test_suite * test = - BOOST_TEST_SUITE("Boost.Fiber: unbounded_channel test suite"); + BOOST_TEST_SUITE("Boost.Fiber: unbounded_queue test suite"); test->add( BOOST_TEST_CASE( & test_push) ); test->add( BOOST_TEST_CASE( & test_push_closed) );