summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/fiber/examples
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
commit483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch)
treee5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/boost/libs/fiber/examples
parentInitial commit. (diff)
downloadceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz
ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/fiber/examples')
-rw-r--r--src/boost/libs/fiber/examples/Jamfile.v253
-rw-r--r--src/boost/libs/fiber/examples/adapt_callbacks.cpp316
-rw-r--r--src/boost/libs/fiber/examples/adapt_method_calls.cpp167
-rw-r--r--src/boost/libs/fiber/examples/adapt_nonblocking.cpp208
-rw-r--r--src/boost/libs/fiber/examples/asio/autoecho.cpp261
-rw-r--r--src/boost/libs/fiber/examples/asio/detail/yield.hpp328
-rw-r--r--src/boost/libs/fiber/examples/asio/exchange.cpp56
-rw-r--r--src/boost/libs/fiber/examples/asio/ps/publisher.cpp52
-rw-r--r--src/boost/libs/fiber/examples/asio/ps/server.cpp381
-rw-r--r--src/boost/libs/fiber/examples/asio/ps/subscriber.cpp53
-rw-r--r--src/boost/libs/fiber/examples/asio/round_robin.hpp194
-rw-r--r--src/boost/libs/fiber/examples/asio/yield.hpp63
-rw-r--r--src/boost/libs/fiber/examples/barrier.cpp98
-rw-r--r--src/boost/libs/fiber/examples/cuda/Makefile29
-rw-r--r--src/boost/libs/fiber/examples/cuda/multiple_streams.cu112
-rw-r--r--src/boost/libs/fiber/examples/cuda/single_stream.cu96
-rw-r--r--src/boost/libs/fiber/examples/future.cpp50
-rw-r--r--src/boost/libs/fiber/examples/hip/Makefile29
-rw-r--r--src/boost/libs/fiber/examples/hip/multiple_streams.cpp111
-rw-r--r--src/boost/libs/fiber/examples/hip/single_stream.cpp96
-rw-r--r--src/boost/libs/fiber/examples/join.cpp67
-rw-r--r--src/boost/libs/fiber/examples/numa/Jamfile.v231
-rw-r--r--src/boost/libs/fiber/examples/numa/topology.cpp37
-rw-r--r--src/boost/libs/fiber/examples/ping_pong.cpp48
-rw-r--r--src/boost/libs/fiber/examples/priority.cpp353
-rw-r--r--src/boost/libs/fiber/examples/range_for.cpp53
-rw-r--r--src/boost/libs/fiber/examples/segmented_stack.cpp74
-rw-r--r--src/boost/libs/fiber/examples/simple.cpp39
-rw-r--r--src/boost/libs/fiber/examples/wait_stuff.cpp984
-rw-r--r--src/boost/libs/fiber/examples/work_sharing.cpp130
-rw-r--r--src/boost/libs/fiber/examples/work_stealing.cpp126
31 files changed, 4695 insertions, 0 deletions
diff --git a/src/boost/libs/fiber/examples/Jamfile.v2 b/src/boost/libs/fiber/examples/Jamfile.v2
new file mode 100644
index 00000000..0ebe2daf
--- /dev/null
+++ b/src/boost/libs/fiber/examples/Jamfile.v2
@@ -0,0 +1,53 @@
+# Boost.Fiber Library Examples Jamfile
+
+# Copyright Oliver Kowalke 2013.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+# For more information, see http://www.boost.org/
+
+import common ;
+import feature ;
+import indirect ;
+import modules ;
+import os ;
+import toolset ;
+
+project boost/fiber/example
+ : requirements
+ <library>../build//boost_fiber
+ <library>/boost/context//boost_context
+ <library>/boost/filesystem//boost_filesystem
+ <library>/boost/thread//boost_thread
+ <target-os>solaris:<linkflags>"-llgrp"
+ <target-os>solaris:<linkflags>"-lsocket"
+ <target-os>windows:<define>_WIN32_WINNT=0x0601
+ <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack
+ <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
+ <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack
+ <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
+ <link>shared
+ <threading>multi
+ ;
+
+exe adapt_callbacks : adapt_callbacks.cpp ;
+exe adapt_method_calls : adapt_method_calls.cpp ;
+exe adapt_nonblocking : adapt_nonblocking.cpp ;
+exe barrier : barrier.cpp ;
+exe future : future.cpp ;
+exe join : join.cpp ;
+exe ping_pong : ping_pong.cpp ;
+exe range_for : range_for.cpp ;
+exe priority : priority.cpp ;
+exe segmented_stack : segmented_stack.cpp ;
+exe simple : simple.cpp ;
+exe wait_stuff : wait_stuff.cpp ;
+exe work_sharing : work_sharing.cpp ;
+exe work_stealing : work_stealing.cpp ;
+
+exe asio/autoecho : asio/autoecho.cpp ;
+exe asio/exchange : asio/exchange.cpp ;
+exe asio/ps/publisher : asio/ps/publisher.cpp ;
+exe asio/ps/server : asio/ps/server.cpp ;
+exe asio/ps/subscriber : asio/ps/subscriber.cpp ;
diff --git a/src/boost/libs/fiber/examples/adapt_callbacks.cpp b/src/boost/libs/fiber/examples/adapt_callbacks.cpp
new file mode 100644
index 00000000..1c6bb502
--- /dev/null
+++ b/src/boost/libs/fiber/examples/adapt_callbacks.cpp
@@ -0,0 +1,316 @@
+// Copyright Nat Goodspeed 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cassert>
+#include <chrono>
+#include <exception>
+#include <iostream>
+#include <sstream>
+#include <thread>
+#include <tuple> // std::tie()
+
+#include <boost/context/detail/apply.hpp>
+#include <boost/fiber/all.hpp>
+
+#if defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+/*****************************************************************************
+* helper code to help callback
+*****************************************************************************/
+template< typename Fn, typename ... Args >
+class helper {
+private:
+ typename std::decay< Fn >::type fn_;
+ std::tuple< typename std::decay< Args >::type ... > args_;
+
+public:
+ helper( Fn && fn, Args && ... args) :
+ fn_( std::forward< Fn >( fn) ),
+ args_( std::make_tuple( std::forward< Args >( args) ... ) ) {
+ }
+
+ helper( helper && other) = default;
+ helper & operator=( helper && other) = default;
+
+ helper( helper const&) = default;
+ helper & operator=( helper const&) = default;
+
+ void operator()() {
+ boost::context::detail::apply( fn_, args_);
+ }
+};
+
+template< typename Fn, typename ... Args >
+helper< Fn, Args ... > help( Fn && fn, Args && ... args) {
+ return helper< Fn, Args ... >( std::forward< Fn >( fn), std::forward< Args >( args) ... );
+}
+#endif
+
+/*****************************************************************************
+* example async API
+*****************************************************************************/
+//[AsyncAPI
+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
+ template< typename Fn >
+ void init_write( std::string const& data, Fn && callback);
+
+ // read callback needs to accept both errorcode and data
+ template< typename Fn >
+ void init_read( Fn && callback);
+
+ // ... other operations ...
+//<-
+ void inject_error( errorcode ec);
+
+private:
+ std::string data_;
+ errorcode injected_;
+//->
+};
+//]
+
+/*****************************************************************************
+* fake AsyncAPI implementation... pay no attention to the little man behind
+* the curtain...
+*****************************************************************************/
+AsyncAPI::AsyncAPI() :
+ injected_( 0) {
+}
+
+void AsyncAPI::inject_error( errorcode ec) {
+ injected_ = ec;
+}
+
+template< typename Fn >
+void AsyncAPI::init_write( std::string const& data, Fn && callback) {
+ // make a local copy of injected_
+ errorcode injected( injected_);
+ // reset it synchronously with caller
+ injected_ = 0;
+ // update data_ (this might be an echo service)
+ if ( ! injected) {
+ data_ = data;
+ }
+ // Simulate an asynchronous I/O operation by launching a detached thread
+ // that sleeps a bit before calling completion callback. Echo back to
+ // caller any previously-injected errorcode.
+#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+ std::thread( [injected,callback=std::forward< Fn >( callback)]() mutable {
+ std::this_thread::sleep_for( std::chrono::milliseconds(100) );
+ callback( injected);
+ }).detach();
+#else
+ std::thread(
+ std::move(
+ help( std::forward< Fn >( callback), injected) ) ).detach();
+#endif
+}
+
+template< typename Fn >
+void AsyncAPI::init_read( Fn && callback) {
+ // make a local copy of injected_
+ errorcode injected( injected_);
+ // reset it synchronously with caller
+ injected_ = 0;
+ // local copy of data_ so we can capture in lambda
+ std::string data( data_);
+ // Simulate an asynchronous I/O operation by launching a detached thread
+ // that sleeps a bit before calling completion callback. Echo back to
+ // caller any previously-injected errorcode.
+#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+ std::thread( [injected,callback=std::forward< Fn >( callback),data]() mutable {
+ std::this_thread::sleep_for( std::chrono::milliseconds(100) );
+ callback( injected, data);
+ }).detach();
+#else
+ std::thread(
+ std::move(
+ help( std::forward< Fn >( callback), injected, data) ) ).detach();
+#endif
+}
+
+/*****************************************************************************
+* adapters
+*****************************************************************************/
+// helper function used in a couple of the adapters
+std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode);
+
+//[callbacks_write_ec
+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.
+#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+ api.init_write(
+ data,
+ [promise=std::move( promise)]( AsyncAPI::errorcode ec) mutable {
+ promise.set_value( ec);
+ });
+
+#else // defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+ api.init_write(
+ data,
+ std::bind([](boost::fibers::promise< AsyncAPI::errorcode > & promise,
+ AsyncAPI::errorcode ec) {
+ promise.set_value( ec);
+ },
+ std::move( promise),
+ std::placeholders::_1) );
+#endif // BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES
+
+ return future.get();
+}
+//]
+
+//[callbacks_write
+void write( AsyncAPI & api, std::string const& data) {
+ AsyncAPI::errorcode ec = write_ec( api, data);
+ if ( ec) {
+ throw make_exception("write", ec);
+ }
+}
+//]
+
+//[callbacks_read_ec
+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.
+#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+ api.init_read([promise=std::move( promise)]( AsyncAPI::errorcode ec, std::string const& data) mutable {
+ promise.set_value( result_pair( ec, data) );
+ });
+#else // defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+ api.init_read(
+ std::bind([]( boost::fibers::promise< result_pair > & promise,
+ AsyncAPI::errorcode ec, std::string const& data) mutable {
+ promise.set_value( result_pair( ec, data) );
+ },
+ std::move( promise),
+ std::placeholders::_1,
+ std::placeholders::_2) );
+#endif // BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES
+ return future.get();
+}
+//]
+
+//[callbacks_read
+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.
+#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+ 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) ) );
+ }
+ });
+#else // defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)
+ api.init_read(
+ std::bind([]( boost::fibers::promise< std::string > & 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) ) );
+ }
+ },
+ std::move( promise),
+ std::placeholders::_1,
+ std::placeholders::_2) );
+#endif // BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES
+ return future.get();
+}
+//]
+
+/*****************************************************************************
+* helpers
+*****************************************************************************/
+std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode ec) {
+ std::ostringstream buffer;
+ buffer << "Error in AsyncAPI::" << desc << "(): " << ec;
+ return std::runtime_error( buffer.str() );
+}
+
+/*****************************************************************************
+* driving logic
+*****************************************************************************/
+int main( int argc, char *argv[]) {
+ AsyncAPI api;
+
+ // successful write(): prime AsyncAPI with some data
+ write( api, "abcd");
+ // successful read(): retrieve it
+ std::string data( read( api) );
+ assert( data == "abcd");
+
+ // successful write_ec()
+ AsyncAPI::errorcode ec( write_ec( api, "efgh") );
+ assert( ec == 0);
+
+ // write_ec() with error
+ api.inject_error(1);
+ ec = write_ec( api, "ijkl");
+ assert( ec == 1);
+
+ // write() with error
+ std::string thrown;
+ api.inject_error(2);
+ try {
+ write(api, "mnop");
+ } catch ( std::exception const& e) {
+ thrown = e.what();
+ }
+ assert( thrown == make_exception("write", 2).what() );
+
+ // successful read_ec()
+//[callbacks_read_ec_call
+ std::tie( ec, data) = read_ec( api);
+//]
+ assert( ! ec);
+ assert( data == "efgh"); // last successful write_ec()
+
+ // read_ec() with error
+ api.inject_error(3);
+ std::tie( ec, data) = read_ec( api);
+ assert( ec == 3);
+ // 'data' in unspecified state, don't test
+
+ // read() with error
+ thrown.clear();
+ api.inject_error(4);
+ try {
+ data = read(api);
+ } catch ( std::exception const& e) {
+ thrown = e.what();
+ }
+ assert( thrown == make_exception("read", 4).what() );
+
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/boost/libs/fiber/examples/adapt_method_calls.cpp b/src/boost/libs/fiber/examples/adapt_method_calls.cpp
new file mode 100644
index 00000000..7cfd78df
--- /dev/null
+++ b/src/boost/libs/fiber/examples/adapt_method_calls.cpp
@@ -0,0 +1,167 @@
+// Copyright Nat Goodspeed 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <boost/fiber/all.hpp>
+#include <memory> // std::shared_ptr
+#include <thread>
+#include <chrono>
+#include <iostream>
+#include <sstream>
+#include <exception>
+#include <cassert>
+
+/*****************************************************************************
+* example async API
+*****************************************************************************/
+// introduce class-scope typedef
+struct AsyncAPIBase {
+ // error callback accepts an int error code; 0 == success
+ typedef int errorcode;
+};
+
+//[Response
+// 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;
+};
+//]
+
+// the actual async API
+class AsyncAPI: public AsyncAPIBase {
+public:
+ // constructor acquires some resource that can be read
+ AsyncAPI( std::string const& data);
+
+//[method_init_read
+ // derive Response subclass, instantiate, pass Response::ptr
+ void init_read( Response::ptr);
+//]
+
+ // ... other operations ...
+ void inject_error( errorcode ec);
+
+private:
+ std::string data_;
+ errorcode injected_;
+};
+
+/*****************************************************************************
+* fake AsyncAPI implementation... pay no attention to the little man behind
+* the curtain...
+*****************************************************************************/
+AsyncAPI::AsyncAPI( std::string const& data) :
+ data_( data),
+ injected_( 0) {
+}
+
+void AsyncAPI::inject_error( errorcode ec) {
+ injected_ = ec;
+}
+
+void AsyncAPI::init_read( Response::ptr response) {
+ // make a local copy of injected_
+ errorcode injected( injected_);
+ // reset it synchronously with caller
+ injected_ = 0;
+ // local copy of data_ so we can capture in lambda
+ std::string data( data_);
+ // Simulate an asynchronous I/O operation by launching a detached thread
+ // that sleeps a bit before calling either completion method.
+ std::thread( [injected, response, data](){
+ std::this_thread::sleep_for( std::chrono::milliseconds(100) );
+ if ( ! injected) {
+ // no error, call success()
+ response->success( data);
+ } else {
+ // injected error, call error()
+ response->error( injected);
+ }
+ }).detach();
+}
+
+/*****************************************************************************
+* adapters
+*****************************************************************************/
+// helper function
+std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode);
+
+//[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_;
+};
+//]
+
+//[method_read
+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();
+}
+//]
+
+/*****************************************************************************
+* helpers
+*****************************************************************************/
+std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode ec) {
+ std::ostringstream buffer;
+ buffer << "Error in AsyncAPI::" << desc << "(): " << ec;
+ return std::runtime_error( buffer.str() );
+}
+
+/*****************************************************************************
+* driving logic
+*****************************************************************************/
+int main(int argc, char *argv[]) {
+ // prime AsyncAPI with some data
+ AsyncAPI api("abcd");
+
+ // successful read(): retrieve it
+ std::string data( read( api) );
+ assert(data == "abcd");
+
+ // read() with error
+ std::string thrown;
+ api.inject_error(1);
+ try {
+ data = read( api);
+ } catch ( std::exception const& e) {
+ thrown = e.what();
+ }
+ assert(thrown == make_exception("read", 1).what() );
+
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/boost/libs/fiber/examples/adapt_nonblocking.cpp b/src/boost/libs/fiber/examples/adapt_nonblocking.cpp
new file mode 100644
index 00000000..879e1789
--- /dev/null
+++ b/src/boost/libs/fiber/examples/adapt_nonblocking.cpp
@@ -0,0 +1,208 @@
+// Copyright Nat Goodspeed 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <boost/fiber/all.hpp>
+#include <iostream>
+#include <sstream>
+#include <exception>
+#include <string>
+#include <algorithm> // std::min()
+#include <errno.h> // EWOULDBLOCK
+#include <cassert>
+#include <cstdio>
+
+/*****************************************************************************
+* example nonblocking API
+*****************************************************************************/
+//[NonblockingAPI
+class NonblockingAPI {
+public:
+ NonblockingAPI();
+
+ // nonblocking operation: may return EWOULDBLOCK
+ int read( std::string & data, std::size_t desired);
+
+/*= ...*/
+//<-
+ // for simulating a real nonblocking API
+ void set_data( std::string const& data, std::size_t chunksize);
+ void inject_error( int ec);
+
+private:
+ std::string data_;
+ int injected_;
+ unsigned tries_;
+ std::size_t chunksize_;
+//->
+};
+//]
+
+/*****************************************************************************
+* fake NonblockingAPI implementation... pay no attention to the little man
+* behind the curtain...
+*****************************************************************************/
+NonblockingAPI::NonblockingAPI() :
+ injected_( 0),
+ tries_( 0),
+ chunksize_( 9999) {
+}
+
+void NonblockingAPI::set_data( std::string const& data, std::size_t chunksize) {
+ data_ = data;
+ chunksize_ = chunksize;
+ // This delimits the start of a new test. Reset state.
+ injected_ = 0;
+ tries_ = 0;
+}
+
+void NonblockingAPI::inject_error( int ec) {
+ injected_ = ec;
+}
+
+int NonblockingAPI::read( std::string & data, std::size_t desired) {
+ // in case of error
+ data.clear();
+
+ if ( injected_) {
+ // copy injected_ because we're about to reset it
+ auto injected( injected_);
+ injected_ = 0;
+ // after an error situation, restart success count
+ tries_ = 0;
+ return injected;
+ }
+
+ if ( ++tries_ < 5) {
+ // no injected error, but the resource isn't yet ready
+ return EWOULDBLOCK;
+ }
+
+ // tell caller there's nothing left
+ if ( data_.empty() ) {
+ return EOF;
+ }
+
+ // okay, finally have some data
+ // but return minimum of desired and chunksize_
+ std::size_t size( ( std::min)( desired, chunksize_) );
+ data = data_.substr( 0, size);
+ // strip off what we just returned
+ data_ = data_.substr( size);
+ // reset I/O retries count for next time
+ tries_ = 0;
+ // success
+ return 0;
+}
+
+/*****************************************************************************
+* adapters
+*****************************************************************************/
+//[nonblocking_read_chunk
+// 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;
+}
+//]
+
+//[nonblocking_read_desired
+// 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;
+}
+//]
+
+//[nonblocking_IncompleteRead
+// 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_;
+};
+//]
+
+//[nonblocking_read
+// 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);
+}
+//]
+
+int main( int argc, char *argv[]) {
+ NonblockingAPI api;
+ const std::string sample_data("abcdefghijklmnopqrstuvwxyz");
+
+ // Try just reading directly from NonblockingAPI
+ api.set_data( sample_data, 5);
+ std::string data;
+ int ec = api.read( data, 13);
+ // whoops, underlying resource not ready
+ assert(ec == EWOULDBLOCK);
+ assert(data.empty());
+
+ // successful read()
+ api.set_data( sample_data, 5);
+ data = read( api, 13);
+ assert(data == "abcdefghijklm");
+
+ // read() with error
+ api.set_data( sample_data, 5);
+ // don't accidentally pick either EOF or EWOULDBLOCK
+ assert(EOF != 1);
+ assert(EWOULDBLOCK != 1);
+ api.inject_error(1);
+ int thrown = 0;
+ try {
+ data = read( api, 13);
+ } catch ( IncompleteRead const& e) {
+ thrown = e.get_errorcode();
+ }
+ assert(thrown == 1);
+
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/boost/libs/fiber/examples/asio/autoecho.cpp b/src/boost/libs/fiber/examples/asio/autoecho.cpp
new file mode 100644
index 00000000..06b4027a
--- /dev/null
+++ b/src/boost/libs/fiber/examples/asio/autoecho.cpp
@@ -0,0 +1,261 @@
+// Copyright 2003-2013 Christopher M. Kohlhoff
+// Copyright Oliver Kowalke, Nat Goodspeed 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <chrono>
+#include <cstdlib>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <thread>
+
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <boost/fiber/all.hpp>
+#include "round_robin.hpp"
+#include "yield.hpp"
+
+using boost::asio::ip::tcp;
+
+const int max_length = 1024;
+
+typedef boost::shared_ptr< tcp::socket > socket_ptr;
+
+const char* const alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/*****************************************************************************
+* thread names
+*****************************************************************************/
+class ThreadNames {
+private:
+ std::map<std::thread::id, std::string> names_{};
+ const char* next_{ alpha };
+ std::mutex mtx_{};
+
+public:
+ ThreadNames() = default;
+
+ std::string lookup() {
+ std::unique_lock<std::mutex> lk( mtx_);
+ auto this_id( std::this_thread::get_id() );
+ auto found = names_.find( this_id );
+ if ( found != names_.end() ) {
+ return found->second;
+ }
+ BOOST_ASSERT( *next_);
+ std::string name(1, *next_++ );
+ names_[ this_id ] = name;
+ return name;
+ }
+};
+
+ThreadNames thread_names;
+
+/*****************************************************************************
+* fiber names
+*****************************************************************************/
+class FiberNames {
+private:
+ std::map<boost::fibers::fiber::id, std::string> names_{};
+ unsigned next_{ 0 };
+ boost::fibers::mutex mtx_{};
+
+public:
+ FiberNames() = default;
+
+ std::string lookup() {
+ std::unique_lock<boost::fibers::mutex> lk( mtx_);
+ auto this_id( boost::this_fiber::get_id() );
+ auto found = names_.find( this_id );
+ if ( found != names_.end() ) {
+ return found->second;
+ }
+ std::ostringstream out;
+ // Bake into the fiber's name the thread name on which we first
+ // lookup() its ID, to be able to spot when a fiber hops between
+ // threads.
+ out << thread_names.lookup() << next_++;
+ std::string name( out.str() );
+ names_[ this_id ] = name;
+ return name;
+ }
+};
+
+FiberNames fiber_names;
+
+std::string tag() {
+ std::ostringstream out;
+ out << "Thread " << thread_names.lookup() << ": "
+ << std::setw(4) << fiber_names.lookup() << std::setw(0);
+ return out.str();
+}
+
+/*****************************************************************************
+* message printing
+*****************************************************************************/
+void print_( std::ostream& out) {
+ out << '\n';
+}
+
+template < typename T, typename... Ts >
+void print_( std::ostream& out, T const& arg, Ts const&... args) {
+ out << arg;
+ print_(out, args...);
+}
+
+template < typename... T >
+void print( T const&... args ) {
+ std::ostringstream buffer;
+ print_( buffer, args...);
+ std::cout << buffer.str() << std::flush;
+}
+
+/*****************************************************************************
+* fiber function per server connection
+*****************************************************************************/
+void session( socket_ptr sock) {
+ try {
+ for (;;) {
+ char data[max_length];
+ boost::system::error_code ec;
+ std::size_t length = sock->async_read_some(
+ boost::asio::buffer( data),
+ boost::fibers::asio::yield[ec]);
+ if ( ec == boost::asio::error::eof) {
+ break; //connection closed cleanly by peer
+ } else if ( ec) {
+ throw boost::system::system_error( ec); //some other error
+ }
+ print( tag(), ": handled: ", std::string(data, length));
+ boost::asio::async_write(
+ * sock,
+ boost::asio::buffer( data, length),
+ boost::fibers::asio::yield[ec]);
+ if ( ec == boost::asio::error::eof) {
+ break; //connection closed cleanly by peer
+ } else if ( ec) {
+ throw boost::system::system_error( ec); //some other error
+ }
+ }
+ print( tag(), ": connection closed");
+ } catch ( std::exception const& ex) {
+ print( tag(), ": caught exception : ", ex.what());
+ }
+}
+
+/*****************************************************************************
+* listening server
+*****************************************************************************/
+void server( std::shared_ptr< boost::asio::io_service > const& io_svc, tcp::acceptor & a) {
+ print( tag(), ": echo-server started");
+ try {
+ for (;;) {
+ socket_ptr socket( new tcp::socket( * io_svc) );
+ boost::system::error_code ec;
+ a.async_accept(
+ * socket,
+ boost::fibers::asio::yield[ec]);
+ if ( ec) {
+ throw boost::system::system_error( ec); //some other error
+ } else {
+ boost::fibers::fiber( session, socket).detach();
+ }
+ }
+ } catch ( std::exception const& ex) {
+ print( tag(), ": caught exception : ", ex.what());
+ }
+ io_svc->stop();
+ print( tag(), ": echo-server stopped");
+}
+
+/*****************************************************************************
+* fiber function per client
+*****************************************************************************/
+void client( std::shared_ptr< boost::asio::io_service > const& io_svc, tcp::acceptor & a,
+ boost::fibers::barrier& barrier, unsigned iterations) {
+ print( tag(), ": echo-client started");
+ for (unsigned count = 0; count < iterations; ++count) {
+ tcp::resolver resolver( * io_svc);
+ tcp::resolver::query query( tcp::v4(), "127.0.0.1", "9999");
+ tcp::resolver::iterator iterator = resolver.resolve( query);
+ tcp::socket s( * io_svc);
+ boost::asio::connect( s, iterator);
+ for (unsigned msg = 0; msg < 1; ++msg) {
+ std::ostringstream msgbuf;
+ msgbuf << "from " << fiber_names.lookup() << " " << count << "." << msg;
+ std::string message(msgbuf.str());
+ print( tag(), ": Sending: ", message);
+ boost::system::error_code ec;
+ boost::asio::async_write(
+ s,
+ boost::asio::buffer( message),
+ boost::fibers::asio::yield[ec]);
+ if ( ec == boost::asio::error::eof) {
+ return; //connection closed cleanly by peer
+ } else if ( ec) {
+ throw boost::system::system_error( ec); //some other error
+ }
+ char reply[max_length];
+ size_t reply_length = s.async_read_some(
+ boost::asio::buffer( reply, max_length),
+ boost::fibers::asio::yield[ec]);
+ if ( ec == boost::asio::error::eof) {
+ return; //connection closed cleanly by peer
+ } else if ( ec) {
+ throw boost::system::system_error( ec); //some other error
+ }
+ print( tag(), ": Reply : ", std::string( reply, reply_length));
+ }
+ }
+ // done with all iterations, wait for rest of client fibers
+ if ( barrier.wait()) {
+ // exactly one barrier.wait() call returns true
+ // we're the lucky one
+ a.close();
+ print( tag(), ": acceptor stopped");
+ }
+ print( tag(), ": echo-client stopped");
+}
+
+/*****************************************************************************
+* main
+*****************************************************************************/
+int main( int argc, char* argv[]) {
+ try {
+//[asio_rr_setup
+ std::shared_ptr< boost::asio::io_service > io_svc = std::make_shared< boost::asio::io_service >();
+ boost::fibers::use_scheduling_algorithm< boost::fibers::asio::round_robin >( io_svc);
+//]
+ print( "Thread ", thread_names.lookup(), ": started");
+//[asio_rr_launch_fibers
+ // server
+ tcp::acceptor a( * io_svc, tcp::endpoint( tcp::v4(), 9999) );
+ boost::fibers::fiber( server, io_svc, std::ref( a) ).detach();
+ // client
+ const unsigned iterations = 2;
+ const unsigned clients = 3;
+ boost::fibers::barrier b( clients);
+ for ( unsigned i = 0; i < clients; ++i) {
+ boost::fibers::fiber(
+ client, io_svc, std::ref( a), std::ref( b), iterations).detach();
+ }
+//]
+//[asio_rr_run
+ io_svc->run();
+//]
+ print( tag(), ": io_service returned");
+ print( "Thread ", thread_names.lookup(), ": stopping");
+ std::cout << "done." << std::endl;
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ print("Exception: ", e.what(), "\n");
+ }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/asio/detail/yield.hpp b/src/boost/libs/fiber/examples/asio/detail/yield.hpp
new file mode 100644
index 00000000..01b680b5
--- /dev/null
+++ b/src/boost/libs/fiber/examples/asio/detail/yield.hpp
@@ -0,0 +1,328 @@
+// Copyright Oliver Kowalke, Nat Goodspeed 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
+#define BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
+
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/detail/config.hpp>
+#include <boost/asio/handler_type.hpp>
+#include <boost/assert.hpp>
+#include <boost/atomic.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/system/system_error.hpp>
+#include <boost/throw_exception.hpp>
+
+#include <boost/fiber/all.hpp>
+
+#include <mutex> // std::unique_lock
+
+#ifdef BOOST_HAS_ABI_HEADERS
+# include BOOST_ABI_PREFIX
+#endif
+
+namespace boost {
+namespace fibers {
+namespace asio {
+namespace detail {
+
+//[fibers_asio_yield_completion
+// Bundle a completion bool flag with a spinlock to protect it.
+struct yield_completion {
+ enum state_t {
+ init,
+ waiting,
+ complete
+ };
+
+ typedef fibers::detail::spinlock mutex_t;
+ typedef std::unique_lock< mutex_t > lock_t;
+ typedef boost::intrusive_ptr< yield_completion > ptr_t;
+
+ std::atomic< std::size_t > use_count_{ 0 };
+ mutex_t mtx_{};
+ state_t state_{ init };
+
+ void wait() {
+ // yield_handler_base::operator()() will set state_ `complete` and
+ // attempt to wake a suspended fiber. It would be Bad if that call
+ // happened between our detecting (complete != state_) and suspending.
+ lock_t lk{ mtx_ };
+ // If state_ is already set, we're done here: don't suspend.
+ if ( complete != state_) {
+ state_ = waiting;
+ // suspend(unique_lock<spinlock>) unlocks the lock in the act of
+ // resuming another fiber
+ fibers::context::active()->suspend( lk);
+ }
+ }
+
+ friend void intrusive_ptr_add_ref( yield_completion * yc) noexcept {
+ BOOST_ASSERT( nullptr != yc);
+ yc->use_count_.fetch_add( 1, std::memory_order_relaxed);
+ }
+
+ friend void intrusive_ptr_release( yield_completion * yc) noexcept {
+ BOOST_ASSERT( nullptr != yc);
+ if ( 1 == yc->use_count_.fetch_sub( 1, std::memory_order_release) ) {
+ std::atomic_thread_fence( std::memory_order_acquire);
+ delete yc;
+ }
+ }
+};
+//]
+
+//[fibers_asio_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 state_ flag, wait until it
+ // has observed (completed != state_).
+ yield_completion::lock_t lk{ ycomp_->mtx_ };
+ yield_completion::state_t state = ycomp_->state_;
+ // Notify a subsequent yield_completion::wait() call that it need not
+ // suspend.
+ ycomp_->state_ = yield_completion::complete;
+ // set the error_code bound by yield_t
+ * yt_.ec_ = ec;
+ // unlock the lock that protects state_
+ lk.unlock();
+ // If ctx_ is still active, e.g. because the async operation
+ // immediately called its callback (this method!) before the asio
+ // async function called async_result_base::get(), we must not set it
+ // ready.
+ if ( yield_completion::waiting == state) {
+ // wake the fiber
+ fibers::context::active()->schedule( 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::ptr_t ycomp_{};
+};
+//]
+
+//[fibers_asio_yield_handler_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 };
+};
+//]
+
+//[fibers_asio_yield_handler_void
+// 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();
+};
+//]
+
+// Specialize asio_handler_invoke hook to ensure that any exceptions thrown
+// from the handler are propagated back to the caller
+template< typename Fn, typename T >
+void asio_handler_invoke( Fn&& fn, yield_handler< T > *) {
+ fn();
+}
+
+//[fibers_asio_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) :
+ ycomp_{ new yield_completion{} } {
+ // 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_ } );
+ }
+ }
+
+private:
+ // If yield_t does not bind an error_code instance, store into here.
+ boost::system::error_code ec_{};
+ yield_completion::ptr_t ycomp_;
+};
+//]
+
+}}}}
+
+namespace boost {
+namespace asio {
+
+//[fibers_asio_async_result_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_{};
+};
+//]
+
+//[fibers_asio_async_result_void
+// 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 } {
+ }
+};
+//]
+
+// Handler type specialisation for fibers::asio::yield.
+// When 'yield' is passed as a completion handler which accepts no parameters,
+// use yield_handler<void>.
+template< typename ReturnType >
+struct handler_type< fibers::asio::yield_t, ReturnType() >
+{ typedef fibers::asio::detail::yield_handler< void > type; };
+
+// Handler type specialisation for fibers::asio::yield.
+// When 'yield' is passed as a completion handler which accepts a data
+// parameter, use yield_handler<parameter type> to return that parameter to
+// the caller.
+template< typename ReturnType, typename Arg1 >
+struct handler_type< fibers::asio::yield_t, ReturnType( Arg1) >
+{ typedef fibers::asio::detail::yield_handler< Arg1 > type; };
+
+//[asio_handler_type
+// 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; };
+//]
+
+// Handler type specialisation for fibers::asio::yield.
+// When 'yield' is passed as a completion handler which accepts a data
+// parameter and an error_code, use yield_handler<parameter type> to return
+// just the parameter to the caller. yield_handler will take care of the
+// error_code one way or another.
+template< typename ReturnType, typename Arg2 >
+struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code, Arg2) >
+{ typedef fibers::asio::detail::yield_handler< Arg2 > type; };
+
+}}
+
+#ifdef BOOST_HAS_ABI_HEADERS
+# include BOOST_ABI_SUFFIX
+#endif
+
+#endif // BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
diff --git a/src/boost/libs/fiber/examples/asio/exchange.cpp b/src/boost/libs/fiber/examples/asio/exchange.cpp
new file mode 100644
index 00000000..8d7482af
--- /dev/null
+++ b/src/boost/libs/fiber/examples/asio/exchange.cpp
@@ -0,0 +1,56 @@
+// Copyright Arnaud Kapp, Oliver Kowalke 2016
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <iostream>
+#include <memory>
+#include <thread>
+
+#include <boost/asio.hpp>
+
+#include <boost/fiber/all.hpp>
+#include "round_robin.hpp"
+
+std::shared_ptr< boost::fibers::unbuffered_channel< int > > c;
+
+void foo() {
+ auto io_ptr = std::make_shared< boost::asio::io_service >();
+ boost::fibers::use_scheduling_algorithm< boost::fibers::asio::round_robin >( io_ptr);
+ boost::fibers::fiber([io_ptr](){
+ for ( int i = 0; i < 10; ++i) {
+ std::cout << "push " << i << std::endl;
+ c->push( i);
+ }
+ c->close();
+ io_ptr->stop();
+ }).detach();
+ io_ptr->run();
+}
+
+void bar() {
+ auto io_ptr = std::make_shared< boost::asio::io_service >();
+ boost::fibers::use_scheduling_algorithm< boost::fibers::asio::round_robin >( io_ptr);
+ boost::fibers::fiber([io_ptr](){
+ try {
+ for (;;) {
+ int i = c->value_pop();
+ std::cout << "pop " << i << std::endl;
+ }
+ } catch ( std::exception const& e) {
+ std::cout << "exception: " << e.what() << std::endl;
+ }
+ io_ptr->stop();
+ }).detach();
+ io_ptr->run();
+}
+
+int main() {
+ c = std::make_shared< boost::fibers::unbuffered_channel< int > >();
+ std::thread t1( foo);
+ std::thread t2( bar);
+ t2.join();
+ t1.join();
+ std::cout << "done." << std::endl;
+ return 0;
+}
diff --git a/src/boost/libs/fiber/examples/asio/ps/publisher.cpp b/src/boost/libs/fiber/examples/asio/ps/publisher.cpp
new file mode 100644
index 00000000..76a63d77
--- /dev/null
+++ b/src/boost/libs/fiber/examples/asio/ps/publisher.cpp
@@ -0,0 +1,52 @@
+//
+// blocking_tcp_echo_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+#include <boost/asio.hpp>
+
+using boost::asio::ip::tcp;
+
+enum {
+ max_length = 1024
+};
+
+int main( int argc, char* argv[]) {
+ try {
+ if ( 3 != argc) {
+ std::cerr << "Usage: publisher <host> <queue>\n";
+ return EXIT_FAILURE;
+ }
+ boost::asio::io_service io_service;
+ tcp::resolver resolver( io_service);
+ tcp::resolver::query query( tcp::v4(), argv[1], "9997");
+ tcp::resolver::iterator iterator = resolver.resolve(query);
+ tcp::socket s( io_service);
+ boost::asio::connect( s, iterator);
+ char msg[max_length];
+ std::string queue( argv[2]);
+ std::memset( msg, '\0', max_length);
+ std::memcpy( msg, queue.c_str(), queue.size() );
+ boost::asio::write( s, boost::asio::buffer( msg, max_length) );
+ for (;;) {
+ std::cout << "publish: ";
+ char request[max_length];
+ std::cin.getline( request, max_length);
+ boost::asio::write( s, boost::asio::buffer( request, max_length) );
+ }
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/asio/ps/server.cpp b/src/boost/libs/fiber/examples/asio/ps/server.cpp
new file mode 100644
index 00000000..aeb58f23
--- /dev/null
+++ b/src/boost/libs/fiber/examples/asio/ps/server.cpp
@@ -0,0 +1,381 @@
+// Copyright Oliver Kowalke 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstddef>
+#include <cstdlib>
+#include <map>
+#include <memory>
+#include <set>
+#include <iostream>
+#include <string>
+
+#include <boost/asio.hpp>
+#include <boost/utility.hpp>
+
+#include <boost/fiber/all.hpp>
+#include "../round_robin.hpp"
+#include "../yield.hpp"
+
+using boost::asio::ip::tcp;
+
+const std::size_t max_length = 1024;
+
+class subscriber_session;
+typedef std::shared_ptr< subscriber_session > subscriber_session_ptr;
+
+// a queue has n subscribers (subscriptions)
+// this class holds a list of subcribers for one queue
+class subscriptions {
+public:
+ ~subscriptions();
+
+ // subscribe to this queue
+ void subscribe( subscriber_session_ptr const& s) {
+ subscribers_.insert( s);
+ }
+
+ // unsubscribe from this queue
+ void unsubscribe( subscriber_session_ptr const& s) {
+ subscribers_.erase(s);
+ }
+
+ // publish a message, e.g. push this message to all subscribers
+ void publish( std::string const& msg);
+
+private:
+ // list of subscribers
+ std::set< subscriber_session_ptr > subscribers_;
+};
+
+// 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 > > queues_cont;
+ typedef queues_cont::iterator queues_iter;
+
+ boost::fibers::mutex mtx_;
+ queues_cont queues_;
+
+ void register_queue_( std::string const& queue) {
+ if ( queues_.end() != queues_.find( queue) ) {
+ throw std::runtime_error("queue already exists");
+ }
+ queues_[queue] = std::make_shared< subscriptions >();
+ std::cout << "new queue '" << queue << "' registered" << std::endl;
+ }
+
+ void unregister_queue_( std::string const& queue) {
+ queues_.erase( queue);
+ std::cout << "queue '" << queue << "' unregistered" << std::endl;
+ }
+
+ 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 queue '" << queue << "'" << std::endl;
+ }
+
+ 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& 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 queue '" << queue << "'" << std::endl;
+ }
+
+public:
+ // add a queue to registry
+ void register_queue( std::string const& queue) {
+ std::unique_lock< boost::fibers::mutex > lk( mtx_);
+ register_queue_( queue);
+ }
+
+ // remove a queue from registry
+ void unregister_queue( std::string const& queue) {
+ std::unique_lock< boost::fibers::mutex > lk( mtx_);
+ unregister_queue_( queue);
+ }
+
+ // subscribe to a queue
+ void subscribe( std::string const& queue, subscriber_session_ptr s) {
+ std::unique_lock< boost::fibers::mutex > lk( mtx_);
+ subscribe_( queue, s);
+ }
+
+ // unsubscribe from a queue
+ void unsubscribe( std::string const& queue, subscriber_session_ptr s) {
+ std::unique_lock< boost::fibers::mutex > lk( mtx_);
+ unsubscribe_( queue, s);
+ }
+
+ // 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_( queue, msg);
+ }
+};
+
+// 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) :
+ socket_( * io_service),
+ reg_( reg) {
+ }
+
+ tcp::socket& socket() {
+ return socket_;
+ }
+
+ // this function is executed inside the fiber
+ void run() {
+ std::string queue;
+ try {
+ boost::system::error_code ec;
+ // 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'
+ boost::asio::async_read(
+ socket_,
+ boost::asio::buffer( data_),
+ boost::fibers::asio::yield[ec]);
+ if ( ec) {
+ throw std::runtime_error("no queue from subscriber");
+ }
+ // first message ist equal to the queue name the publisher
+ // publishes to
+ queue = data_;
+ // subscribe to new queue
+ reg_.subscribe( queue, shared_from_this() );
+ // read published messages
+ for (;;) {
+ // wait for a conditon-variable for new messages
+ // the fiber will be suspended until the condtion
+ // becomes true and the fiber is resumed
+ // published message is stored in buffer 'data_'
+ std::unique_lock< boost::fibers::mutex > lk( mtx_);
+ cond_.wait( lk);
+ std::string data( data_);
+ lk.unlock();
+ // message '<fini>' terminates subscription
+ if ( "<fini>" == data) {
+ break;
+ }
+ // async. write message to socket connected with
+ // subscriber
+ // async_write() returns if the complete message was writen
+ // the fiber is suspended in the meanwhile
+ boost::asio::async_write(
+ socket_,
+ boost::asio::buffer( data, data.size() ),
+ boost::fibers::asio::yield[ec]);
+ if ( ec == boost::asio::error::eof) {
+ break; //connection closed cleanly by peer
+ } else if ( ec) {
+ throw boost::system::system_error( ec); //some other error
+ }
+ std::cout << "subscriber::run(): '" << data << "' written" << std::endl;
+ }
+ } catch ( std::exception const& e) {
+ std::cerr << "subscriber [" << queue << "] failed: " << e.what() << std::endl;
+ }
+ // close socket
+ socket_.close();
+ // unregister queue
+ reg_.unsubscribe( queue, shared_from_this() );
+ }
+
+ // called from publisher_session (running in other fiber)
+ void publish( std::string const& msg) {
+ std::unique_lock< boost::fibers::mutex > lk( mtx_);
+ std::memset( data_, '\0', sizeof( data_));
+ std::memcpy( data_, msg.c_str(), (std::min)(max_length, msg.size()));
+ cond_.notify_one();
+ }
+
+private:
+ tcp::socket socket_;
+ registry & reg_;
+ boost::fibers::mutex mtx_;
+ boost::fibers::condition_variable cond_;
+ // fixed size message
+ char data_[max_length];
+};
+
+
+subscriptions::~subscriptions() {
+ for ( subscriber_session_ptr s : subscribers_) {
+ s->publish("<fini>");
+ }
+}
+
+void
+subscriptions::publish( std::string const& msg) {
+ for ( subscriber_session_ptr s : subscribers_) {
+ s->publish( msg);
+ }
+}
+
+// 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) :
+ socket_( * io_service),
+ reg_( reg) {
+ }
+
+ tcp::socket& socket() {
+ return socket_;
+ }
+
+ // this function is executed inside the fiber
+ void run() {
+ std::string queue;
+ try {
+ boost::system::error_code ec;
+ // fixed size message
+ char data[max_length];
+ // 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'
+ boost::asio::async_read(
+ socket_,
+ boost::asio::buffer( data),
+ boost::fibers::asio::yield[ec]);
+ if ( ec) {
+ throw std::runtime_error("no queue from publisher");
+ }
+ // first message ist equal to the queue name the publisher
+ // publishes to
+ queue = data;
+ // register the new queue
+ reg_.register_queue( queue);
+ // start publishing messages
+ for (;;) {
+ // read message from publisher asyncronous
+ // async_read() suspends this fiber until the complete emssage is read
+ // and stored in the given buffer 'data'
+ boost::asio::async_read(
+ socket_,
+ boost::asio::buffer( data),
+ boost::fibers::asio::yield[ec]);
+ if ( ec == boost::asio::error::eof) {
+ break; //connection closed cleanly by peer
+ } else if ( ec) {
+ throw boost::system::system_error( ec); //some other error
+ }
+ // publish message to all subscribers
+ reg_.publish( queue, std::string( data) );
+ }
+ } catch ( std::exception const& e) {
+ std::cerr << "publisher [" << queue << "] failed: " << e.what() << std::endl;
+ }
+ // close socket
+ socket_.close();
+ // unregister queue
+ reg_.unregister_queue( queue);
+ }
+
+private:
+ tcp::socket socket_;
+ registry & reg_;
+};
+
+typedef std::shared_ptr< publisher_session > publisher_session_ptr;
+
+// function accepts connections requests from clients acting as a publisher
+void accept_publisher( std::shared_ptr< boost::asio::io_service > const& io_service,
+ unsigned short port,
+ registry & reg) {
+ // create TCP-acceptor
+ tcp::acceptor acceptor( * io_service, tcp::endpoint( tcp::v4(), port) );
+ // loop for accepting connection requests
+ for (;;) {
+ boost::system::error_code ec;
+ // create new publisher-session
+ // this instance will be associated with one publisher
+ publisher_session_ptr new_publisher_session =
+ std::make_shared< publisher_session >( io_service, std::ref( reg) );
+ // async. accept of new connection request
+ // this function will suspend this execution context (fiber) until a
+ // connection was established, after returning from this function a new client (publisher)
+ // is connected
+ acceptor.async_accept(
+ new_publisher_session->socket(),
+ boost::fibers::asio::yield[ec]);
+ if ( ! ec) {
+ // run the new publisher in its own fiber (one fiber for one client)
+ boost::fibers::fiber(
+ std::bind( & publisher_session::run, new_publisher_session) ).detach();
+ }
+ }
+}
+
+// function accepts connections requests from clients acting as a subscriber
+void accept_subscriber( std::shared_ptr< boost::asio::io_service > const& io_service,
+ unsigned short port,
+ registry & reg) {
+ // create TCP-acceptor
+ tcp::acceptor acceptor( * io_service, tcp::endpoint( tcp::v4(), port) );
+ // loop for accepting connection requests
+ for (;;) {
+ boost::system::error_code ec;
+ // create new subscriber-session
+ // this instance will be associated with one subscriber
+ subscriber_session_ptr new_subscriber_session =
+ std::make_shared< subscriber_session >( io_service, std::ref( reg) );
+ // async. accept of new connection request
+ // this function will suspend this execution context (fiber) until a
+ // connection was established, after returning from this function a new client (subscriber)
+ // is connected
+ acceptor.async_accept(
+ new_subscriber_session->socket(),
+ boost::fibers::asio::yield[ec]);
+ if ( ! ec) {
+ // run the new subscriber in its own fiber (one fiber for one client)
+ boost::fibers::fiber(
+ std::bind( & subscriber_session::run, new_subscriber_session) ).detach();
+ }
+ }
+}
+
+
+int main( int argc, char* argv[]) {
+ try {
+ // create io_service for async. I/O
+ 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 queues and its subscription
+ registry reg;
+ // create an acceptor for publishers, run it as fiber
+ boost::fibers::fiber(
+ accept_publisher, std::ref( io_service), 9997, std::ref( reg) ).detach();
+ // create an acceptor for subscribers, run it as fiber
+ boost::fibers::fiber(
+ accept_subscriber, std::ref( io_service), 9998, std::ref( reg) ).detach();
+ // dispatch
+ io_service->run();
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/asio/ps/subscriber.cpp b/src/boost/libs/fiber/examples/asio/ps/subscriber.cpp
new file mode 100644
index 00000000..04e583e2
--- /dev/null
+++ b/src/boost/libs/fiber/examples/asio/ps/subscriber.cpp
@@ -0,0 +1,53 @@
+//
+// blocking_tcp_echo_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+#include <boost/asio.hpp>
+
+using boost::asio::ip::tcp;
+
+enum {
+ max_length = 1024
+};
+
+int main( int argc, char* argv[]) {
+ try {
+ if ( 3 != argc) {
+ std::cerr << "Usage: subscriber <host> <queue>\n";
+ return EXIT_FAILURE;
+ }
+ boost::asio::io_service io_service;
+ tcp::resolver resolver( io_service);
+ tcp::resolver::query query( tcp::v4(), argv[1], "9998");
+ tcp::resolver::iterator iterator = resolver.resolve( query);
+ tcp::socket s( io_service);
+ boost::asio::connect( s, iterator);
+ char msg[max_length];
+ std::string queue( argv[2]);
+ std::memset( msg, '\0', max_length);
+ std::memcpy( msg, queue.c_str(), queue.size() );
+ boost::asio::write( s, boost::asio::buffer( msg, max_length) );
+ for (;;) {
+ char reply[max_length];
+ size_t reply_length = s.read_some( boost::asio::buffer( reply, max_length) );
+ std::cout << "published: ";
+ std::cout.write( reply, reply_length);
+ std::cout << std::endl;
+ }
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/asio/round_robin.hpp b/src/boost/libs/fiber/examples/asio/round_robin.hpp
new file mode 100644
index 00000000..b06bb35c
--- /dev/null
+++ b/src/boost/libs/fiber/examples/asio/round_robin.hpp
@@ -0,0 +1,194 @@
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef BOOST_FIBERS_ASIO_ROUND_ROBIN_H
+#define BOOST_FIBERS_ASIO_ROUND_ROBIN_H
+
+#include <chrono>
+#include <cstddef>
+#include <memory>
+#include <mutex>
+#include <queue>
+
+#include <boost/asio.hpp>
+#include <boost/assert.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/config.hpp>
+
+#include <boost/fiber/condition_variable.hpp>
+#include <boost/fiber/context.hpp>
+#include <boost/fiber/mutex.hpp>
+#include <boost/fiber/operations.hpp>
+#include <boost/fiber/scheduler.hpp>
+
+#include "yield.hpp"
+
+#ifdef BOOST_HAS_ABI_HEADERS
+# include BOOST_ABI_PREFIX
+#endif
+
+namespace boost {
+namespace fibers {
+namespace asio {
+
+class round_robin : public algo::algorithm {
+private:
+//[asio_rr_suspend_timer
+ std::shared_ptr< boost::asio::io_service > io_svc_;
+ boost::asio::steady_timer suspend_timer_;
+//]
+ boost::fibers::scheduler::ready_queue_type rqueue_{};
+ boost::fibers::mutex mtx_{};
+ boost::fibers::condition_variable cnd_{};
+ std::size_t counter_{ 0 };
+
+public:
+//[asio_rr_service_top
+ struct service : public boost::asio::io_service::service {
+ static boost::asio::io_service::id id;
+
+ std::unique_ptr< boost::asio::io_service::work > work_;
+
+ service( boost::asio::io_service & io_svc) :
+ boost::asio::io_service::service( io_svc),
+ work_{ new boost::asio::io_service::work( io_svc) } {
+ }
+
+ virtual ~service() {}
+
+ service( service const&) = delete;
+ service & operator=( service const&) = delete;
+
+ void shutdown_service() override final {
+ work_.reset();
+ }
+ };
+//]
+
+//[asio_rr_ctor
+ round_robin( std::shared_ptr< boost::asio::io_service > const& io_svc) :
+ io_svc_( io_svc),
+ suspend_timer_( * io_svc_) {
+ // We use add_service() very deliberately. This will throw
+ // service_already_exists if you pass the same io_service instance to
+ // more than one round_robin instance.
+ boost::asio::add_service( * io_svc_, new service( * io_svc_) );
+ io_svc_->post([this]() mutable {
+//]
+//[asio_rr_service_lambda
+ while ( ! io_svc_->stopped() ) {
+ if ( has_ready_fibers() ) {
+ // run all pending handlers in round_robin
+ while ( io_svc_->poll() );
+ // block this fiber till all pending (ready) fibers are processed
+ // == round_robin::suspend_until() has been called
+ std::unique_lock< boost::fibers::mutex > lk( mtx_);
+ cnd_.wait( lk);
+ } else {
+ // run one handler inside io_service
+ // if no handler available, block this thread
+ if ( ! io_svc_->run_one() ) {
+ break;
+ }
+ }
+ }
+//]
+ });
+ }
+
+ void awakened( context * ctx) noexcept {
+ BOOST_ASSERT( nullptr != ctx);
+ BOOST_ASSERT( ! ctx->ready_is_linked() );
+ ctx->ready_link( rqueue_); /*< fiber, enqueue on ready queue >*/
+ if ( ! ctx->is_context( boost::fibers::type::dispatcher_context) ) {
+ ++counter_;
+ }
+ }
+
+ context * pick_next() noexcept {
+ context * ctx( nullptr);
+ if ( ! rqueue_.empty() ) { /*<
+ pop an item from the ready queue
+ >*/
+ ctx = & rqueue_.front();
+ rqueue_.pop_front();
+ BOOST_ASSERT( nullptr != ctx);
+ BOOST_ASSERT( context::active() != ctx);
+ if ( ! ctx->is_context( boost::fibers::type::dispatcher_context) ) {
+ --counter_;
+ }
+ }
+ return ctx;
+ }
+
+ bool has_ready_fibers() const noexcept {
+ return 0 < counter_;
+ }
+
+//[asio_rr_suspend_until
+ void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept {
+ // Set a timer so at least one handler will eventually fire, causing
+ // run_one() to eventually return.
+ if ( (std::chrono::steady_clock::time_point::max)() != abs_time) {
+ // Each expires_at(time_point) call cancels any previous pending
+ // call. We could inadvertently spin like this:
+ // dispatcher calls suspend_until() with earliest wake time
+ // suspend_until() sets suspend_timer_
+ // lambda loop calls run_one()
+ // some other asio handler runs before timer expires
+ // run_one() returns to lambda loop
+ // lambda loop yields to dispatcher
+ // dispatcher finds no ready fibers
+ // dispatcher calls suspend_until() with SAME wake time
+ // suspend_until() sets suspend_timer_ to same time, canceling
+ // previous async_wait()
+ // lambda loop calls run_one()
+ // asio calls suspend_timer_ handler with operation_aborted
+ // run_one() returns to lambda loop... etc. etc.
+ // So only actually set the timer when we're passed a DIFFERENT
+ // abs_time value.
+ suspend_timer_.expires_at( abs_time);
+ suspend_timer_.async_wait([](boost::system::error_code const&){
+ this_fiber::yield();
+ });
+ }
+ cnd_.notify_one();
+ }
+//]
+
+//[asio_rr_notify
+ void notify() noexcept {
+ // Something has happened that should wake one or more fibers BEFORE
+ // suspend_timer_ expires. Reset the timer to cause it to fire
+ // immediately, causing the run_one() call to return. In theory we
+ // could use cancel() because we don't care whether suspend_timer_'s
+ // handler is called with operation_aborted or success. However --
+ // cancel() doesn't change the expiration time, and we use
+ // suspend_timer_'s expiration time to decide whether it's already
+ // set. If suspend_until() set some specific wake time, then notify()
+ // canceled it, then suspend_until() was called again with the same
+ // wake time, it would match suspend_timer_'s expiration time and we'd
+ // refrain from setting the timer. So instead of simply calling
+ // cancel(), reset the timer, which cancels the pending sleep AND sets
+ // a new expiration time. This will cause us to spin the loop twice --
+ // once for the operation_aborted handler, once for timer expiration
+ // -- but that shouldn't be a big problem.
+ suspend_timer_.async_wait([](boost::system::error_code const&){
+ this_fiber::yield();
+ });
+ suspend_timer_.expires_at( std::chrono::steady_clock::now() );
+ }
+//]
+};
+
+boost::asio::io_service::id round_robin::service::id;
+
+}}}
+
+#ifdef BOOST_HAS_ABI_HEADERS
+# include BOOST_ABI_SUFFIX
+#endif
+
+#endif // BOOST_FIBERS_ASIO_ROUND_ROBIN_H
diff --git a/src/boost/libs/fiber/examples/asio/yield.hpp b/src/boost/libs/fiber/examples/asio/yield.hpp
new file mode 100644
index 00000000..d76467f9
--- /dev/null
+++ b/src/boost/libs/fiber/examples/asio/yield.hpp
@@ -0,0 +1,63 @@
+// Copyright 2003-2013 Christopher M. Kohlhoff
+// Copyright Oliver Kowalke, Nat Goodspeed 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+
+#ifndef BOOST_FIBERS_ASIO_YIELD_HPP
+#define BOOST_FIBERS_ASIO_YIELD_HPP
+
+#include <boost/config.hpp>
+
+#ifdef BOOST_HAS_ABI_HEADERS
+# include BOOST_ABI_PREFIX
+#endif
+
+namespace boost {
+namespace fibers {
+namespace asio {
+
+//[fibers_asio_yield_t
+class yield_t {
+public:
+ yield_t() = default;
+
+ /**
+ * @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;
+ tmp.ec_ = & ec;
+ return tmp;
+ }
+
+//private:
+ // ptr to bound error_code instance if any
+ boost::system::error_code * ec_{ nullptr };
+};
+//]
+
+//[fibers_asio_yield
+// canonical instance
+thread_local yield_t yield{};
+//]
+
+}}}
+
+#ifdef BOOST_HAS_ABI_HEADERS
+# include BOOST_ABI_SUFFIX
+#endif
+
+#include "detail/yield.hpp"
+
+#endif // BOOST_FIBERS_ASIO_YIELD_HPP
diff --git a/src/boost/libs/fiber/examples/barrier.cpp b/src/boost/libs/fiber/examples/barrier.cpp
new file mode 100644
index 00000000..dedec6c6
--- /dev/null
+++ b/src/boost/libs/fiber/examples/barrier.cpp
@@ -0,0 +1,98 @@
+
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstdlib>
+#include <functional>
+#include <iostream>
+#include <stdexcept>
+
+#include <boost/assert.hpp>
+
+#include <boost/fiber/all.hpp>
+
+int value1 = 0;
+int value2 = 0;
+
+inline
+void fn1( boost::fibers::barrier & b)
+{
+ boost::fibers::fiber::id id = boost::this_fiber::get_id();
+ std::cout << "fiber " << id << ": fn1 entered" << std::endl;
+
+ ++value1;
+ std::cout << "fiber " << id << ": incremented value1: " << value1 << std::endl;
+ boost::this_fiber::yield();
+
+ std::cout << "fiber " << id << ": waits for barrier" << std::endl;
+ b.wait();
+ std::cout << "fiber " << id << ": passed barrier" << std::endl;
+
+ ++value1;
+ std::cout << "fiber " << id << ": incremented value1: " << value1 << std::endl;
+ boost::this_fiber::yield();
+
+ ++value1;
+ std::cout << "fiber " << id << ": incremented value1: " << value1 << std::endl;
+ boost::this_fiber::yield();
+
+ ++value1;
+ std::cout << "fiber " << id << ": incremented value1: " << value1 << std::endl;
+ boost::this_fiber::yield();
+
+ std::cout << "fiber " << id << ": fn1 returns" << std::endl;
+}
+
+inline
+void fn2( boost::fibers::barrier & b)
+{
+ boost::fibers::fiber::id id = boost::this_fiber::get_id();
+ std::cout << "fiber " << id << ": fn2 entered" << std::endl;
+
+ ++value2;
+ std::cout << "fiber " << id << ": incremented value2: " << value2 << std::endl;
+ boost::this_fiber::yield();
+
+ ++value2;
+ std::cout << "fiber " << id << ": incremented value2: " << value2 << std::endl;
+ boost::this_fiber::yield();
+
+ ++value2;
+ std::cout << "fiber " << id << ": incremented value2: " << value2 << std::endl;
+ boost::this_fiber::yield();
+
+ std::cout << "fiber " << id << ": waits for barrier" << std::endl;
+ b.wait();
+ std::cout << "fiber " << id << ": passed barrier" << std::endl;
+
+ ++value2;
+ std::cout << "fiber " << id << ": incremented value2: " << value2 << std::endl;
+ boost::this_fiber::yield();
+
+ std::cout << "fiber " << id << ": fn2 returns" << std::endl;
+}
+
+int main()
+{
+ try
+ {
+ boost::fibers::barrier fb( 2);
+
+ boost::fibers::fiber f1( & fn1, std::ref( fb) );
+ boost::fibers::fiber f2( & fn2, std::ref( fb) );
+
+ f1.join();
+ f2.join();
+
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+ }
+ catch ( std::exception const& e)
+ { std::cerr << "exception: " << e.what() << std::endl; }
+ catch (...)
+ { std::cerr << "unhandled exception" << std::endl; }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/cuda/Makefile b/src/boost/libs/fiber/examples/cuda/Makefile
new file mode 100644
index 00000000..60a3098e
--- /dev/null
+++ b/src/boost/libs/fiber/examples/cuda/Makefile
@@ -0,0 +1,29 @@
+CUDA_PATH := /opt/cuda
+
+NVCC := $(CUDA_PATH)/bin/nvcc
+
+CPPFLAGS := -O2 -std=c++11
+LDFLAGS := -g -L/usr/local/lib
+INCLUDES := -I/usr/local/include -I$(CUDA_PATH)/include
+LIBRARIES := -lboost_fiber -lboost_context -lboost_system -lboost_filesystem
+
+all: build
+
+build: single_stream multiple_streams
+
+single_stream.o:single_stream.cu
+ $(NVCC) $(INCLUDES) $(CPPFLAGS) -o $@ -c $<
+
+single_stream: single_stream.o
+ $(NVCC) $(LDFLAGS) -o $@ $+ $(LIBRARIES)
+
+multiple_streams.o:multiple_streams.cu
+ $(NVCC) $(INCLUDES) $(CPPFLAGS) -o $@ -c $<
+
+multiple_streams: multiple_streams.o
+ $(NVCC) $(LDFLAGS) -o $@ $+ $(LIBRARIES)
+
+clean:
+ rm -f single_stream single_stream.o multiple_streams multiple_streams.o
+
+clobber: clean
diff --git a/src/boost/libs/fiber/examples/cuda/multiple_streams.cu b/src/boost/libs/fiber/examples/cuda/multiple_streams.cu
new file mode 100644
index 00000000..0c665e29
--- /dev/null
+++ b/src/boost/libs/fiber/examples/cuda/multiple_streams.cu
@@ -0,0 +1,112 @@
+
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <tuple>
+
+#include <cuda.h>
+
+#include <boost/assert.hpp>
+#include <boost/bind.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+#include <boost/fiber/all.hpp>
+#include <boost/fiber/cuda/waitfor.hpp>
+
+__global__
+void vector_add( int * a, int * b, int * c, int size) {
+ int idx = threadIdx.x + blockIdx.x * blockDim.x;
+ if ( idx < size) {
+ c[idx] = a[idx] + b[idx];
+ }
+}
+
+int main() {
+ try {
+ bool done = false;
+ boost::fibers::fiber f1( [&done]{
+ std::cout << "f1: entered" << std::endl;
+ try {
+ cudaStream_t stream0, stream1;
+ cudaStreamCreate( & stream0);
+ cudaStreamCreate( & stream1);
+ int size = 1024 * 1024;
+ int full_size = 20 * size;
+ int * host_a, * host_b, * host_c;
+ cudaHostAlloc( & host_a, full_size * sizeof( int), cudaHostAllocDefault);
+ cudaHostAlloc( & host_b, full_size * sizeof( int), cudaHostAllocDefault);
+ cudaHostAlloc( & host_c, full_size * sizeof( int), cudaHostAllocDefault);
+ int * dev_a0, * dev_b0, * dev_c0;
+ int * dev_a1, * dev_b1, * dev_c1;
+ cudaMalloc( & dev_a0, size * sizeof( int) );
+ cudaMalloc( & dev_b0, size * sizeof( int) );
+ cudaMalloc( & dev_c0, size * sizeof( int) );
+ cudaMalloc( & dev_a1, size * sizeof( int) );
+ cudaMalloc( & dev_b1, size * sizeof( int) );
+ cudaMalloc( & dev_c1, size * sizeof( int) );
+ std::minstd_rand generator;
+ std::uniform_int_distribution<> distribution(1, 6);
+ for ( int i = 0; i < full_size; ++i) {
+ host_a[i] = distribution( generator);
+ host_b[i] = distribution( generator);
+ }
+ for ( int i = 0; i < full_size; i += 2 * size) {
+ cudaMemcpyAsync( dev_a0, host_a + i, size * sizeof( int), cudaMemcpyHostToDevice, stream0);
+ cudaMemcpyAsync( dev_a1, host_a + i + size, size * sizeof( int), cudaMemcpyHostToDevice, stream1);
+ cudaMemcpyAsync( dev_b0, host_b + i, size * sizeof( int), cudaMemcpyHostToDevice, stream0);
+ cudaMemcpyAsync( dev_b1, host_b + i + size, size * sizeof( int), cudaMemcpyHostToDevice, stream1);
+ vector_add<<< size / 256, 256, 0, stream0 >>>( dev_a0, dev_b0, dev_c0, size);
+ vector_add<<< size / 256, 256, 0, stream1 >>>( dev_a1, dev_b1, dev_c1, size);
+ cudaMemcpyAsync( host_c + i, dev_c0, size * sizeof( int), cudaMemcpyDeviceToHost, stream0);
+ cudaMemcpyAsync( host_c + i + size, dev_c1, size * sizeof( int), cudaMemcpyDeviceToHost, stream1);
+ }
+ auto results = boost::fibers::cuda::waitfor_all( stream0, stream1);
+ for ( auto & result : results) {
+ BOOST_ASSERT( stream0 == std::get< 0 >( result) || stream1 == std::get< 0 >( result) );
+ BOOST_ASSERT( cudaSuccess == std::get< 1 >( result) );
+ }
+ std::cout << "f1: GPU computation finished" << std::endl;
+ cudaFreeHost( host_a);
+ cudaFreeHost( host_b);
+ cudaFreeHost( host_c);
+ cudaFree( dev_a0);
+ cudaFree( dev_b0);
+ cudaFree( dev_c0);
+ cudaFree( dev_a1);
+ cudaFree( dev_b1);
+ cudaFree( dev_c1);
+ cudaStreamDestroy( stream0);
+ cudaStreamDestroy( stream1);
+ done = true;
+ } catch ( std::exception const& ex) {
+ std::cerr << "exception: " << ex.what() << std::endl;
+ }
+ std::cout << "f1: leaving" << std::endl;
+ });
+ boost::fibers::fiber f2([&done]{
+ std::cout << "f2: entered" << std::endl;
+ while ( ! done) {
+ std::cout << "f2: sleeping" << std::endl;
+ boost::this_fiber::sleep_for( std::chrono::milliseconds( 1 ) );
+ }
+ std::cout << "f2: leaving" << std::endl;
+ });
+ f1.join();
+ f2.join();
+ std::cout << "done." << std::endl;
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "exception: " << e.what() << std::endl;
+ } catch (...) {
+ std::cerr << "unhandled exception" << std::endl;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/cuda/single_stream.cu b/src/boost/libs/fiber/examples/cuda/single_stream.cu
new file mode 100644
index 00000000..79f398a1
--- /dev/null
+++ b/src/boost/libs/fiber/examples/cuda/single_stream.cu
@@ -0,0 +1,96 @@
+
+// Copyright Oliver Kowalke 2017.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <tuple>
+
+#include <cuda.h>
+
+#include <boost/assert.hpp>
+#include <boost/bind.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+#include <boost/fiber/all.hpp>
+#include <boost/fiber/cuda/waitfor.hpp>
+
+__global__
+void vector_add( int * a, int * b, int * c, int size) {
+ int idx = threadIdx.x + blockIdx.x * blockDim.x;
+ if ( idx < size) {
+ c[idx] = a[idx] + b[idx];
+ }
+}
+
+int main() {
+ try {
+ bool done = false;
+ boost::fibers::fiber f1([&done]{
+ std::cout << "f1: entered" << std::endl;
+ try {
+ cudaStream_t stream;
+ cudaStreamCreate( & stream);
+ int size = 1024 * 1024;
+ int full_size = 20 * size;
+ int * host_a, * host_b, * host_c;
+ cudaHostAlloc( & host_a, full_size * sizeof( int), cudaHostAllocDefault);
+ cudaHostAlloc( & host_b, full_size * sizeof( int), cudaHostAllocDefault);
+ cudaHostAlloc( & host_c, full_size * sizeof( int), cudaHostAllocDefault);
+ int * dev_a, * dev_b, * dev_c;
+ cudaMalloc( & dev_a, size * sizeof( int) );
+ cudaMalloc( & dev_b, size * sizeof( int) );
+ cudaMalloc( & dev_c, size * sizeof( int) );
+ std::minstd_rand generator;
+ std::uniform_int_distribution<> distribution(1, 6);
+ for ( int i = 0; i < full_size; ++i) {
+ host_a[i] = distribution( generator);
+ host_b[i] = distribution( generator);
+ }
+ for ( int i = 0; i < full_size; i += size) {
+ cudaMemcpyAsync( dev_a, host_a + i, size * sizeof( int), cudaMemcpyHostToDevice, stream);
+ cudaMemcpyAsync( dev_b, host_b + i, size * sizeof( int), cudaMemcpyHostToDevice, stream);
+ vector_add<<< size / 256, 256, 0, stream >>>( dev_a, dev_b, dev_c, size);
+ cudaMemcpyAsync( host_c + i, dev_c, size * sizeof( int), cudaMemcpyDeviceToHost, stream);
+ }
+ auto result = boost::fibers::cuda::waitfor_all( stream);
+ BOOST_ASSERT( stream == std::get< 0 >( result) );
+ BOOST_ASSERT( cudaSuccess == std::get< 1 >( result) );
+ std::cout << "f1: GPU computation finished" << std::endl;
+ cudaFreeHost( host_a);
+ cudaFreeHost( host_b);
+ cudaFreeHost( host_c);
+ cudaFree( dev_a);
+ cudaFree( dev_b);
+ cudaFree( dev_c);
+ cudaStreamDestroy( stream);
+ done = true;
+ } catch ( std::exception const& ex) {
+ std::cerr << "exception: " << ex.what() << std::endl;
+ }
+ std::cout << "f1: leaving" << std::endl;
+ });
+ boost::fibers::fiber f2([&done]{
+ std::cout << "f2: entered" << std::endl;
+ while ( ! done) {
+ std::cout << "f2: sleeping" << std::endl;
+ boost::this_fiber::sleep_for( std::chrono::milliseconds( 1 ) );
+ }
+ std::cout << "f2: leaving" << std::endl;
+ });
+ f1.join();
+ f2.join();
+ std::cout << "done." << std::endl;
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "exception: " << e.what() << std::endl;
+ } catch (...) {
+ std::cerr << "unhandled exception" << std::endl;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/future.cpp b/src/boost/libs/fiber/examples/future.cpp
new file mode 100644
index 00000000..cc7c6b0f
--- /dev/null
+++ b/src/boost/libs/fiber/examples/future.cpp
@@ -0,0 +1,50 @@
+
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <iostream>
+#include <string>
+
+#include <boost/fiber/all.hpp>
+
+inline
+int fn( std::string const& str, int n)
+{
+ for ( int i = 0; i < n; ++i)
+ {
+ std::cout << i << ": " << str << std::endl;
+ boost::this_fiber::yield();
+ }
+
+ return n;
+}
+
+void start()
+{
+ boost::fibers::future< int > fi(
+ boost::fibers::async(
+ std::bind( fn, "abc", 5) ) );
+ fi.wait();
+ std::cout << "fn() returned " << fi.get() << std::endl;
+}
+
+int main()
+{
+ try
+ {
+ boost::fibers::fiber( start).join();
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+ }
+ catch ( std::exception const& e)
+ { std::cerr << "exception: " << e.what() << std::endl; }
+ catch (...)
+ { std::cerr << "unhandled exception" << std::endl; }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/hip/Makefile b/src/boost/libs/fiber/examples/hip/Makefile
new file mode 100644
index 00000000..d40e8c1a
--- /dev/null
+++ b/src/boost/libs/fiber/examples/hip/Makefile
@@ -0,0 +1,29 @@
+HIP_PATH := /opt/rocm/hip
+
+HIPCC := $(HIP_PATH)/bin/hipcc
+
+CPPFLAGS := -O2 -std=c++11
+LDFLAGS := -L/usr/local/lib
+INCLUDES := -I/usr/local/include -I$(HIP_PATH)/include
+LIBRARIES := -lboost_fiber -lboost_context -lboost_system -lboost_filesystem
+
+all: build
+
+build: single_stream multiple_streams
+
+single_stream.o:single_stream.cpp
+ $(HIPCC) $(INCLUDES) $(CPPFLAGS) -o $@ -c $<
+
+single_stream: single_stream.o
+ $(HIPCC) $(LDFLAGS) -o $@ $+ $(LIBRARIES)
+
+multiple_streams.o:multiple_streams.cpp
+ $(HIPCC) $(INCLUDES) $(CPPFLAGS) -o $@ -c $<
+
+multiple_streams: multiple_streams.o
+ $(HIPCC) $(LDFLAGS) -o $@ $+ $(LIBRARIES)
+
+clean:
+ rm -f single_stream single_stream.o multiple_streams multiple_streams.o
+
+clobber: clean
diff --git a/src/boost/libs/fiber/examples/hip/multiple_streams.cpp b/src/boost/libs/fiber/examples/hip/multiple_streams.cpp
new file mode 100644
index 00000000..75a7449c
--- /dev/null
+++ b/src/boost/libs/fiber/examples/hip/multiple_streams.cpp
@@ -0,0 +1,111 @@
+
+// Copyright Oliver Kowalke 2017.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <tuple>
+
+#include <hip/hip_runtime.h>
+
+#include <boost/assert.hpp>
+#include <boost/bind.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+#include <boost/fiber/all.hpp>
+#include <boost/fiber/hip/waitfor.hpp>
+
+__global__
+void vector_add(hipLaunchParm lp, int * a, int * b, int * c, int size) {
+ int idx = threadIdx.x + blockIdx.x * blockDim.x;
+ if ( idx < size) {
+ c[idx] = a[idx] + b[idx];
+ }
+}
+
+int main() {
+ try {
+ bool done = false;
+ boost::fibers::fiber f1( [&done]{
+ std::cout << "f1: entered" << std::endl;
+ try {
+ hipStream_t stream0, stream1;
+ hipStreamCreate( & stream0);
+ hipStreamCreate( & stream1);
+ int size = 1024 * 1024;
+ int full_size = 20 * size;
+ int * host_a, * host_b, * host_c;
+ hipHostMalloc( & host_a, full_size * sizeof( int), hipHostMallocDefault);
+ hipHostMalloc( & host_b, full_size * sizeof( int), hipHostMallocDefault);
+ hipHostMalloc( & host_c, full_size * sizeof( int), hipHostMallocDefault);
+ int * dev_a0, * dev_b0, * dev_c0;
+ int * dev_a1, * dev_b1, * dev_c1;
+ hipMalloc( & dev_a0, size * sizeof( int) );
+ hipMalloc( & dev_b0, size * sizeof( int) );
+ hipMalloc( & dev_c0, size * sizeof( int) );
+ hipMalloc( & dev_a1, size * sizeof( int) );
+ hipMalloc( & dev_b1, size * sizeof( int) );
+ hipMalloc( & dev_c1, size * sizeof( int) );
+ std::minstd_rand generator;
+ std::uniform_int_distribution<> distribution(1, 6);
+ for ( int i = 0; i < full_size; ++i) {
+ host_a[i] = distribution( generator);
+ host_b[i] = distribution( generator);
+ }
+ for ( int i = 0; i < full_size; i += 2 * size) {
+ hipMemcpyAsync( dev_a0, host_a + i, size * sizeof( int), hipMemcpyHostToDevice, stream0);
+ hipMemcpyAsync( dev_a1, host_a + i + size, size * sizeof( int), hipMemcpyHostToDevice, stream1);
+ hipMemcpyAsync( dev_b0, host_b + i, size * sizeof( int), hipMemcpyHostToDevice, stream0);
+ hipMemcpyAsync( dev_b1, host_b + i + size, size * sizeof( int), hipMemcpyHostToDevice, stream1);
+ hipLaunchKernel( vector_add, dim3(size / 256), dim3(256), 0, stream0, dev_a0, dev_b0, dev_c0, size);
+ hipLaunchKernel( vector_add, dim3(size / 256), dim3(256), 0, stream1, dev_a1, dev_b1, dev_c1, size);
+ hipMemcpyAsync( host_c + i, dev_c0, size * sizeof( int), hipMemcpyDeviceToHost, stream0);
+ hipMemcpyAsync( host_c + i + size, dev_c1, size * sizeof( int), hipMemcpyDeviceToHost, stream1);
+ }
+ auto results = boost::fibers::hip::waitfor_all( stream0, stream1);
+ for ( auto & result : results) {
+ BOOST_ASSERT( stream0 == std::get< 0 >( result) || stream1 == std::get< 0 >( result) );
+ BOOST_ASSERT( hipSuccess == std::get< 1 >( result) );
+ }
+ std::cout << "f1: GPU computation finished" << std::endl;
+ hipHostFree( host_a);
+ hipHostFree( host_b);
+ hipHostFree( host_c);
+ hipFree( dev_a0);
+ hipFree( dev_b0);
+ hipFree( dev_c0);
+ hipFree( dev_a1);
+ hipFree( dev_b1);
+ hipFree( dev_c1);
+ hipStreamDestroy( stream0);
+ hipStreamDestroy( stream1);
+ done = true;
+ } catch ( std::exception const& ex) {
+ std::cerr << "exception: " << ex.what() << std::endl;
+ }
+ std::cout << "f1: leaving" << std::endl;
+ });
+ boost::fibers::fiber f2([&done]{
+ std::cout << "f2: entered" << std::endl;
+ while ( ! done) {
+ std::cout << "f2: sleeping" << std::endl;
+ boost::this_fiber::sleep_for( std::chrono::milliseconds( 1 ) );
+ }
+ std::cout << "f2: leaving" << std::endl;
+ });
+ f1.join();
+ f2.join();
+ std::cout << "done." << std::endl;
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "exception: " << e.what() << std::endl;
+ } catch (...) {
+ std::cerr << "unhandled exception" << std::endl;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/hip/single_stream.cpp b/src/boost/libs/fiber/examples/hip/single_stream.cpp
new file mode 100644
index 00000000..1959528e
--- /dev/null
+++ b/src/boost/libs/fiber/examples/hip/single_stream.cpp
@@ -0,0 +1,96 @@
+
+// Copyright Oliver Kowalke 2017.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <tuple>
+
+#include <hip/hip_runtime.h>
+
+#include <boost/assert.hpp>
+#include <boost/bind.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+#include <boost/fiber/all.hpp>
+#include <boost/fiber/hip/waitfor.hpp>
+
+__global__
+void vector_add(hipLaunchParm lp, int * a, int * b, int * c, int size) {
+ int idx = threadIdx.x + blockIdx.x * blockDim.x;
+ if ( idx < size) {
+ c[idx] = a[idx] + b[idx];
+ }
+}
+
+int main() {
+ try {
+ bool done = false;
+ boost::fibers::fiber f1([&done]{
+ std::cout << "f1: entered" << std::endl;
+ try {
+ hipStream_t stream;
+ hipStreamCreate( & stream);
+ int size = 1024 * 1024;
+ int full_size = 20 * size;
+ int * host_a, * host_b, * host_c;
+ hipHostMalloc( & host_a, full_size * sizeof( int), hipHostMallocDefault);
+ hipHostMalloc( & host_b, full_size * sizeof( int), hipHostMallocDefault);
+ hipHostMalloc( & host_c, full_size * sizeof( int), hipHostMallocDefault);
+ int * dev_a, * dev_b, * dev_c;
+ hipMalloc( & dev_a, size * sizeof( int) );
+ hipMalloc( & dev_b, size * sizeof( int) );
+ hipMalloc( & dev_c, size * sizeof( int) );
+ std::minstd_rand generator;
+ std::uniform_int_distribution<> distribution(1, 6);
+ for ( int i = 0; i < full_size; ++i) {
+ host_a[i] = distribution( generator);
+ host_b[i] = distribution( generator);
+ }
+ for ( int i = 0; i < full_size; i += size) {
+ hipMemcpyAsync( dev_a, host_a + i, size * sizeof( int), hipMemcpyHostToDevice, stream);
+ hipMemcpyAsync( dev_b, host_b + i, size * sizeof( int), hipMemcpyHostToDevice, stream);
+ hipLaunchKernel( vector_add, dim3(size / 256), dim3(256), 0, stream, dev_a, dev_b, dev_c, size);
+ hipMemcpyAsync( host_c + i, dev_c, size * sizeof( int), hipMemcpyDeviceToHost, stream);
+ }
+ auto result = boost::fibers::hip::waitfor_all( stream);
+ BOOST_ASSERT( stream == std::get< 0 >( result) );
+ BOOST_ASSERT( hipSuccess == std::get< 1 >( result) );
+ std::cout << "f1: GPU computation finished" << std::endl;
+ hipHostFree( host_a);
+ hipHostFree( host_b);
+ hipHostFree( host_c);
+ hipFree( dev_a);
+ hipFree( dev_b);
+ hipFree( dev_c);
+ hipStreamDestroy( stream);
+ done = true;
+ } catch ( std::exception const& ex) {
+ std::cerr << "exception: " << ex.what() << std::endl;
+ }
+ std::cout << "f1: leaving" << std::endl;
+ });
+ boost::fibers::fiber f2([&done]{
+ std::cout << "f2: entered" << std::endl;
+ while ( ! done) {
+ std::cout << "f2: sleeping" << std::endl;
+ boost::this_fiber::sleep_for( std::chrono::milliseconds( 1 ) );
+ }
+ std::cout << "f2: leaving" << std::endl;
+ });
+ f1.join();
+ f2.join();
+ std::cout << "done." << std::endl;
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "exception: " << e.what() << std::endl;
+ } catch (...) {
+ std::cerr << "unhandled exception" << std::endl;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/join.cpp b/src/boost/libs/fiber/examples/join.cpp
new file mode 100644
index 00000000..7ece0500
--- /dev/null
+++ b/src/boost/libs/fiber/examples/join.cpp
@@ -0,0 +1,67 @@
+
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstdlib>
+#include <functional>
+#include <stdexcept>
+#include <iostream>
+#include <string>
+
+#include <boost/fiber/all.hpp>
+
+int value1 = 0;
+int value2 = 0;
+
+void fn1()
+{
+ boost::fibers::fiber::id this_id = boost::this_fiber::get_id();
+ for ( int i = 0; i < 5; ++i)
+ {
+ ++value1;
+ std::cout << "fiber " << this_id << " fn1: increment value1: " << value1 << std::endl;
+ boost::this_fiber::yield();
+ }
+ std::cout << "fiber " << this_id << " fn1: returns" << std::endl;
+}
+
+void fn2( boost::fibers::fiber & f)
+{
+ boost::fibers::fiber::id this_id = boost::this_fiber::get_id();
+ for ( int i = 0; i < 5; ++i)
+ {
+ ++value2;
+ std::cout << "fiber " << this_id << " fn2: increment value2: " << value2 << std::endl;
+ if ( i == 1)
+ {
+ boost::fibers::fiber::id id = f.get_id();
+ std::cout << "fiber " << this_id << " fn2: joins fiber " << id << std::endl;
+ f.join();
+ std::cout << "fiber " << this_id << " fn2: joined fiber " << id << std::endl;
+ }
+ boost::this_fiber::yield();
+ }
+ std::cout << "fiber " << this_id << " fn2: returns" << std::endl;
+}
+
+int main()
+{
+ try
+ {
+ boost::fibers::fiber f1( fn1);
+ boost::fibers::fiber f2( fn2, std::ref( f1) );
+
+ f2.join();
+
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+ }
+ catch ( std::exception const& e)
+ { std::cerr << "exception: " << e.what() << std::endl; }
+ catch (...)
+ { std::cerr << "unhandled exception" << std::endl; }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/numa/Jamfile.v2 b/src/boost/libs/fiber/examples/numa/Jamfile.v2
new file mode 100644
index 00000000..15420cb5
--- /dev/null
+++ b/src/boost/libs/fiber/examples/numa/Jamfile.v2
@@ -0,0 +1,31 @@
+# Boost.Fiber Library Examples Jamfile
+
+# Copyright Oliver Kowalke 2017.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+# For more information, see http://www.boost.org/
+
+import common ;
+import feature ;
+import indirect ;
+import modules ;
+import os ;
+import toolset ;
+
+project boost/fiber/example/numa
+ : requirements
+ <library>../../build//boost_fiber
+ <library>../../build//boost_fiber_numa
+ <target-os>solaris:<linkflags>"-llgrp"
+ <target-os>windows:<define>_WIN32_WINNT=0x0601
+ <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack
+ <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
+ <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack
+ <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
+ <link>static
+ <threading>multi
+ ;
+
+exe topology : topology.cpp ;
diff --git a/src/boost/libs/fiber/examples/numa/topology.cpp b/src/boost/libs/fiber/examples/numa/topology.cpp
new file mode 100644
index 00000000..9b85e3f4
--- /dev/null
+++ b/src/boost/libs/fiber/examples/numa/topology.cpp
@@ -0,0 +1,37 @@
+
+// Copyright Oliver Kowalke 2017.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstdlib>
+#include <cstring>
+#include <exception>
+#include <iostream>
+#include <vector>
+
+#include <boost/assert.hpp>
+#include <boost/fiber/numa/topology.hpp>
+
+int main( int argc, char * argv[]) {
+ try {
+ std::vector< boost::fibers::numa::node > topo = boost::fibers::numa::topology();
+ for ( auto n : topo) {
+ std::cout << "node: " << n.id << " | ";
+ std::cout << "cpus: ";
+ for ( auto cpu_id : n.logical_cpus) {
+ std::cout << cpu_id << " ";
+ }
+ std::cout << "| distance: ";
+ for ( auto d : n.distance) {
+ std::cout << d << " ";
+ }
+ std::cout << std::endl;
+ }
+ std::cout << "done" << std::endl;
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& ex) {
+ std::cerr << "exception: " << ex.what() << std::endl;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/ping_pong.cpp b/src/boost/libs/fiber/examples/ping_pong.cpp
new file mode 100644
index 00000000..aa56a411
--- /dev/null
+++ b/src/boost/libs/fiber/examples/ping_pong.cpp
@@ -0,0 +1,48 @@
+
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstdlib>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+
+#include <boost/fiber/all.hpp>
+
+int main() {
+ using channel_t = boost::fibers::buffered_channel< std::string >;
+ try {
+ channel_t chan1{ 2 }, chan2{ 2 };
+
+ boost::fibers::fiber fping([&chan1,&chan2]{
+ chan1.push( "ping");
+ std::cout << chan2.value_pop() << "\n";
+ chan1.push( "ping");
+ std::cout << chan2.value_pop() << "\n";
+ chan1.push( "ping");
+ std::cout << chan2.value_pop() << "\n";
+ });
+ boost::fibers::fiber fpong([&chan1,&chan2]{
+ std::cout << chan1.value_pop() << "\n";
+ chan2.push( "pong");
+ std::cout << chan1.value_pop() << "\n";
+ chan2.push( "pong");
+ std::cout << chan1.value_pop() << "\n";
+ chan2.push( "pong");
+ });
+
+ fping.join();
+ fpong.join();
+
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+ }
+ catch ( std::exception const& e)
+ { std::cerr << "exception: " << e.what() << std::endl; }
+ catch (...)
+ { std::cerr << "unhandled exception" << std::endl; }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/priority.cpp b/src/boost/libs/fiber/examples/priority.cpp
new file mode 100644
index 00000000..5f9c51e8
--- /dev/null
+++ b/src/boost/libs/fiber/examples/priority.cpp
@@ -0,0 +1,353 @@
+// Copyright Nat Goodspeed 2014.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <chrono>
+#include <condition_variable>
+#include <iostream>
+#include <mutex>
+#include <algorithm> // std::find_if()
+
+#include <boost/fiber/all.hpp>
+#include <boost/fiber/scheduler.hpp>
+
+class Verbose {
+public:
+ Verbose( std::string const& d, std::string const& s="stop") :
+ desc( d),
+ stop( s) {
+ std::cout << desc << " start" << std::endl;
+ }
+
+ ~Verbose() {
+ std::cout << desc << ' ' << stop << std::endl;
+ }
+
+ Verbose( Verbose const&) = delete;
+ Verbose & operator=( Verbose const&) = delete;
+
+private:
+ std::string desc;
+ std::string stop;
+};
+
+//[priority_props
+class priority_props : public boost::fibers::fiber_properties {
+public:
+ priority_props( boost::fibers::context * ctx):
+ fiber_properties( ctx), /*< Your subclass constructor must accept a
+ [^[class_link context]*] and pass it to
+ the `fiber_properties` constructor. >*/
+ priority_( 0) {
+ }
+
+ int get_priority() const {
+ return priority_; /*< Provide read access methods at your own discretion. >*/
+ }
+
+ // Call this method to alter priority, because we must notify
+ // priority_scheduler of any change.
+ void set_priority( int p) { /*<
+ 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. >*/
+ // 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; /*< A property that does not affect the scheduler does
+ not need access methods. >*/
+private:
+ int priority_;
+};
+//]
+
+//[priority_scheduler
+class priority_scheduler :
+ public boost::fibers::algo::algorithm_with_properties< priority_props > {
+private:
+ typedef boost::fibers::scheduler::ready_queue_type/*< See [link ready_queue_t]. >*/ rqueue_t;
+
+ rqueue_t rqueue_;
+ std::mutex mtx_{};
+ std::condition_variable cnd_{};
+ bool flag_{ false };
+
+public:
+ priority_scheduler() :
+ rqueue_() {
+ }
+
+ // For a subclass of algorithm_with_properties<>, it's important to
+ // override the correct awakened() overload.
+ /*<< You must override the [member_link algorithm_with_properties..awakened]
+ method. This is how your scheduler receives notification of a
+ fiber that has become ready to run. >>*/
+ virtual void awakened( boost::fibers::context * ctx, priority_props & props) noexcept {
+ int ctx_priority = props.get_priority(); /*< `props` is the instance of
+ priority_props associated
+ with the passed fiber `ctx`. >*/
+ // 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);
+//<-
+
+ std::cout << "awakened(" << props.name << "): ";
+ describe_ready_queue();
+//->
+ }
+
+ /*<< You must override the [member_link algorithm_with_properties..pick_next]
+ method. This is how your scheduler actually advises the fiber manager
+ of the next fiber to run. >>*/
+ virtual 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();
+//<-
+ std::cout << "pick_next() resuming " << properties( ctx).name << ": ";
+ describe_ready_queue();
+//->
+ return ctx;
+ }
+
+ /*<< You must override [member_link algorithm_with_properties..has_ready_fibers]
+ to inform the fiber manager of the state of your ready queue. >>*/
+ virtual bool has_ready_fibers() const noexcept {
+ return ! rqueue_.empty();
+ }
+
+ /*<< Overriding [member_link 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. >>*/
+ virtual 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.
+//<-
+ std::cout << "property_change(" << props.name << '(' << props.get_priority()
+ << ")): ";
+//->
+
+ // '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()) { /*<
+ 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. >*/
+//<-
+ // hopefully user will distinguish this case by noticing that
+ // the fiber with which we were called does not appear in the
+ // ready queue at all
+ describe_ready_queue();
+//->
+ 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 describe_ready_queue() {
+ if ( rqueue_.empty() ) {
+ std::cout << "[empty]";
+ } else {
+ const char * delim = "";
+ for ( boost::fibers::context & ctx : rqueue_) {
+ priority_props & props( properties( & ctx) );
+ std::cout << delim << props.name << '(' << props.get_priority() << ')';
+ delim = ", ";
+ }
+ }
+ std::cout << std::endl;
+ }
+//->
+
+ 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();
+ }
+};
+//]
+
+//[launch
+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;
+}
+//]
+
+void yield_fn() {
+ std::string name( boost::this_fiber::properties< priority_props >().name);
+ Verbose v( std::string("fiber ") + name);
+ for ( int i = 0; i < 3; ++i) {
+ std::cout << "fiber " << name << " yielding" << std::endl;
+ boost::this_fiber::yield();
+ }
+}
+
+void barrier_fn( boost::fibers::barrier & barrier) {
+ std::string name( boost::this_fiber::properties< priority_props >().name);
+ Verbose v( std::string("fiber ") + name);
+ std::cout << "fiber " << name << " waiting on barrier" << std::endl;
+ barrier.wait();
+ std::cout << "fiber " << name << " yielding" << std::endl;
+ boost::this_fiber::yield();
+}
+
+//[change_fn
+void change_fn( boost::fibers::fiber & other,
+ int other_priority,
+ boost::fibers::barrier& barrier) {
+ std::string name( boost::this_fiber::properties< priority_props >().name);
+ Verbose v( std::string("fiber ") + name);
+
+//<-
+ std::cout << "fiber " << name << " waiting on barrier" << std::endl;
+//->
+ barrier.wait();
+ // We assume a couple things about 'other':
+ // - that it was also waiting on the same barrier
+ // - that it has lower priority than this fiber.
+ // If both are true, 'other' is now ready to run but is sitting in
+ // priority_scheduler's ready queue. Change its priority.
+ priority_props & other_props(
+ other.properties< priority_props >() );
+//<-
+ std::cout << "fiber " << name << " changing priority of " << other_props.name
+ << " to " << other_priority << std::endl;
+//->
+ other_props.set_priority( other_priority);
+}
+//]
+
+//[main
+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 >();
+/*= ...*/
+/*=}*/
+//]
+ Verbose v("main()");
+
+ // for clarity
+ std::cout << "main() setting name" << std::endl;
+//[main_name
+ boost::this_fiber::properties< priority_props >().name = "main";
+//]
+ std::cout << "main() running tests" << std::endl;
+
+ {
+ Verbose v("high-priority first", "stop\n");
+ // verify that high-priority fiber always gets scheduled first
+ boost::fibers::fiber low( launch( yield_fn, "low", 1) );
+ boost::fibers::fiber med( launch( yield_fn, "medium", 2) );
+ boost::fibers::fiber hi( launch( yield_fn, "high", 3) );
+ std::cout << "main: high.join()" << std::endl;
+ hi.join();
+ std::cout << "main: medium.join()" << std::endl;
+ med.join();
+ std::cout << "main: low.join()" << std::endl;
+ low.join();
+ }
+
+ {
+ Verbose v("same priority round-robin", "stop\n");
+ // fibers of same priority are scheduled in round-robin order
+ boost::fibers::fiber a( launch( yield_fn, "a", 0) );
+ boost::fibers::fiber b( launch( yield_fn, "b", 0) );
+ boost::fibers::fiber c( launch( yield_fn, "c", 0) );
+ std::cout << "main: a.join()" << std::endl;
+ a.join();
+ std::cout << "main: b.join()" << std::endl;
+ b.join();
+ std::cout << "main: c.join()" << std::endl;
+ c.join();
+ }
+
+ {
+ Verbose v("barrier wakes up all", "stop\n");
+ // using a barrier wakes up all waiting fibers at the same time
+ boost::fibers::barrier barrier( 3);
+ boost::fibers::fiber low( launch( [&barrier](){ barrier_fn( barrier); }, "low", 1) );
+ boost::fibers::fiber med( launch( [&barrier](){ barrier_fn( barrier); }, "medium", 2) );
+ boost::fibers::fiber hi( launch( [&barrier](){ barrier_fn( barrier); }, "high", 3) );
+ std::cout << "main: low.join()" << std::endl;
+ low.join();
+ std::cout << "main: medium.join()" << std::endl;
+ med.join();
+ std::cout << "main: high.join()" << std::endl;
+ hi.join();
+ }
+
+ {
+ Verbose v("change priority", "stop\n");
+ // change priority of a fiber in priority_scheduler's ready queue
+ boost::fibers::barrier barrier( 3);
+ boost::fibers::fiber c( launch( [&barrier](){ barrier_fn( barrier); }, "c", 1) );
+ boost::fibers::fiber a( launch( [&c,&barrier]() { change_fn( c, 3, barrier); }, "a", 3) );
+ boost::fibers::fiber b( launch( [&barrier](){ barrier_fn( barrier); }, "b", 2) );
+ std::cout << "main: a.join()" << std::endl;
+ std::cout << "main: a.join()" << std::endl;
+ a.join();
+ std::cout << "main: b.join()" << std::endl;
+ b.join();
+ std::cout << "main: c.join()" << std::endl;
+ c.join();
+ }
+
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/boost/libs/fiber/examples/range_for.cpp b/src/boost/libs/fiber/examples/range_for.cpp
new file mode 100644
index 00000000..8a1dbba1
--- /dev/null
+++ b/src/boost/libs/fiber/examples/range_for.cpp
@@ -0,0 +1,53 @@
+
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstdlib>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+
+#include <boost/fiber/all.hpp>
+
+typedef boost::fibers::unbuffered_channel< unsigned int > channel_t;
+
+void foo( channel_t & chan) {
+ chan.push( 1);
+ chan.push( 1);
+ chan.push( 2);
+ chan.push( 3);
+ chan.push( 5);
+ chan.push( 8);
+ chan.push( 12);
+ chan.close();
+}
+
+void bar( channel_t & chan) {
+ for ( unsigned int value : chan) {
+ std::cout << value << " ";
+ }
+ std::cout << std::endl;
+}
+
+int main() {
+ try {
+ channel_t chan;
+
+ boost::fibers::fiber f1( & foo, std::ref( chan) );
+ boost::fibers::fiber f2( & bar, std::ref( chan) );
+
+ f1.join();
+ f2.join();
+
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "exception: " << e.what() << std::endl;
+ } catch (...) {
+ std::cerr << "unhandled exception" << std::endl;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/segmented_stack.cpp b/src/boost/libs/fiber/examples/segmented_stack.cpp
new file mode 100644
index 00000000..ce52ee10
--- /dev/null
+++ b/src/boost/libs/fiber/examples/segmented_stack.cpp
@@ -0,0 +1,74 @@
+
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <iostream>
+#include <thread>
+
+#include <boost/assert.hpp>
+#include <boost/fiber/all.hpp>
+
+int count = 384;
+
+#ifdef BOOST_MSVC //MS VisualStudio
+__declspec(noinline) void access( char *buf);
+#else // GCC
+void access( char *buf) __attribute__ ((noinline));
+#endif
+void access( char *buf)
+{
+ buf[0] = '\0';
+}
+
+void bar( int i)
+{
+ char buf[4 * 1024];
+
+ if ( i > 0)
+ {
+ access( buf);
+ std::cout << i << ". iteration" << std::endl;
+ bar( i - 1);
+ }
+}
+
+void foo()
+{
+ bar( count);
+ boost::this_fiber::yield();
+}
+
+void thread_fn()
+{
+ {
+ boost::fibers::fiber f(
+#if defined(BOOST_USE_SEGMENTED_STACKS)
+ std::allocator_arg,
+ boost::fibers::segmented_stack(
+ boost::fibers::segmented_stack::traits_type::default_size() ),
+#endif
+ foo);
+ f.join();
+ }
+}
+
+int main( int argc, char * argv[])
+{
+#if defined(BOOST_USE_SEGMENTED_STACKS)
+ std::cout << "using segmented_stack stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, ";
+ std::cout << "initial stack size = " << boost::fibers::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl;
+ std::cout << "application should not fail" << std::endl;
+#else
+ std::cout << "using standard stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, ";
+ std::cout << "initial stack size = " << boost::fibers::fixedsize_stack::traits_type::default_size() / 1024 << "kB" << std::endl;
+ std::cout << "application might fail" << std::endl;
+#endif
+
+ std::thread( thread_fn).join();
+
+ std::cout << "done." << std::endl;
+
+ return 0;
+}
diff --git a/src/boost/libs/fiber/examples/simple.cpp b/src/boost/libs/fiber/examples/simple.cpp
new file mode 100644
index 00000000..07b7b6fd
--- /dev/null
+++ b/src/boost/libs/fiber/examples/simple.cpp
@@ -0,0 +1,39 @@
+
+// Copyright Oliver Kowalke 2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread>
+
+#include <boost/intrusive_ptr.hpp>
+
+#include <boost/fiber/all.hpp>
+
+inline
+void fn( std::string const& str, int n) {
+ for ( int i = 0; i < n; ++i) {
+ std::cout << i << ": " << str << std::endl;
+ boost::this_fiber::yield();
+ }
+}
+
+int main() {
+ try {
+ boost::fibers::fiber f1( fn, "abc", 5);
+ std::cerr << "f1 : " << f1.get_id() << std::endl;
+ f1.join();
+ std::cout << "done." << std::endl;
+
+ return EXIT_SUCCESS;
+ } catch ( std::exception const& e) {
+ std::cerr << "exception: " << e.what() << std::endl;
+ } catch (...) {
+ std::cerr << "unhandled exception" << std::endl;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/src/boost/libs/fiber/examples/wait_stuff.cpp b/src/boost/libs/fiber/examples/wait_stuff.cpp
new file mode 100644
index 00000000..00f64788
--- /dev/null
+++ b/src/boost/libs/fiber/examples/wait_stuff.cpp
@@ -0,0 +1,984 @@
+// Copyright Nat Goodspeed 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <boost/fiber/all.hpp>
+#include <boost/variant/variant.hpp>
+#include <boost/variant/get.hpp>
+
+// These are wait_something() functions rather than when_something()
+// functions. A big part of the point of the Fiber library is to model
+// sequencing using the processor's instruction pointer rather than chains of
+// callbacks. The future-oriented when_all() / when_any() functions are still
+// based on chains of callbacks. With Fiber, we can do better.
+
+/*****************************************************************************
+* Verbose
+*****************************************************************************/
+class Verbose {
+public:
+ Verbose( std::string const& d):
+ desc( d) {
+ std::cout << desc << " start" << std::endl;
+ }
+
+ ~Verbose() {
+ std::cout << desc << " stop" << std::endl;
+ }
+
+ Verbose( Verbose const&) = delete;
+ Verbose & operator=( Verbose const&) = delete;
+
+private:
+ const std::string desc;
+};
+
+/*****************************************************************************
+* Runner and Example
+*****************************************************************************/
+// collect and ultimately run every Example
+class Runner {
+ typedef std::vector< std::pair< std::string, std::function< void() > > > function_list;
+
+public:
+ void add( std::string const& desc, std::function< void() > const& func) {
+ functions_.push_back( function_list::value_type( desc, func) );
+ }
+
+ void run() {
+ for ( function_list::value_type const& pair : functions_) {
+ Verbose v( pair.first);
+ pair.second();
+ }
+ }
+
+private:
+ function_list functions_;
+};
+
+Runner runner;
+
+// Example allows us to embed Runner::add() calls at module scope
+struct Example {
+ Example( Runner & runner, std::string const& desc, std::function< void() > const& func) {
+ runner.add( desc, func);
+ }
+};
+
+/*****************************************************************************
+* example task functions
+*****************************************************************************/
+//[wait_sleeper
+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;
+}
+//]
+
+inline
+std::string sleeper( std::string const& item, int ms, bool thrw = false) {
+ return sleeper_impl( item, ms, thrw);
+}
+
+inline
+double sleeper( double item, int ms, bool thrw = false) {
+ return sleeper_impl( item, ms, thrw);
+}
+
+inline
+int sleeper(int item, int ms, bool thrw = false) {
+ return sleeper_impl( item, ms, thrw);
+}
+
+/*****************************************************************************
+* Done
+*****************************************************************************/
+//[wait_done
+// 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();
+ }
+};
+//]
+
+/*****************************************************************************
+* when_any, simple completion
+*****************************************************************************/
+//[wait_first_simple_impl
+// 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) ... );
+}
+//]
+
+// interface function: instantiate Done, launch tasks, wait for Done
+//[wait_first_simple
+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();
+}
+//]
+
+// example usage
+Example wfs( runner, "wait_first_simple()", [](){
+//[wait_first_simple_ex
+ wait_first_simple(
+ [](){ sleeper("wfs_long", 150); },
+ [](){ sleeper("wfs_medium", 100); },
+ [](){ sleeper("wfs_short", 50); });
+//]
+});
+
+/*****************************************************************************
+* when_any, return value
+*****************************************************************************/
+// 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::buffered_channel< T > > chan,
+ Fn && function) {
+ boost::fibers::fiber( [chan, function](){
+ // Ignore channel_op_status returned by push():
+ // might be closed; we simply don't care.
+ chan->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::buffered_channel< T > > chan,
+ Fn0 && function0,
+ Fn1 && function1,
+ Fns && ... functions) {
+ // process the first function using the single-function overload
+ wait_first_value_impl< T >( chan,
+ std::forward< Fn0 >( function0) );
+ // then recur to process the rest
+ wait_first_value_impl< T >( chan,
+ std::forward< Fn1 >( function1),
+ std::forward< Fns >( functions) ... );
+}
+
+//[wait_first_value
+// 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::buffered_channel< return_t > channel_t;
+ auto chanp( std::make_shared< channel_t >( 64) );
+ // launch all the relevant fibers
+ wait_first_value_impl< return_t >( chanp,
+ std::forward< Fn >( function),
+ std::forward< Fns >( functions) ... );
+ // retrieve the first value
+ return_t value( chanp->value_pop() );
+ // close the channel: no subsequent push() has to succeed
+ chanp->close();
+ return value;
+}
+//]
+
+// example usage
+Example wfv( runner, "wait_first_value()", [](){
+//[wait_first_value_ex
+ 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");
+//]
+});
+
+/*****************************************************************************
+* when_any, produce first outcome, whether result or exception
+*****************************************************************************/
+// When there's only one function, call this overload.
+//[wait_first_outcome_impl
+template< typename T, typename CHANP, typename Fn >
+void wait_first_outcome_impl( CHANP chan, 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(
+ []( CHANP & chan,
+ 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.
+ chan->push( task.get_future() );
+ },
+ chan,
+ std::forward< Fn >( function)
+ )).detach();
+}
+//]
+
+// When there are two or more functions, call this overload
+template< typename T, typename CHANP, typename Fn0, typename Fn1, typename ... Fns >
+void wait_first_outcome_impl( CHANP chan,
+ Fn0 && function0,
+ Fn1 && function1,
+ Fns && ... functions) {
+ // process the first function using the single-function overload
+ wait_first_outcome_impl< T >( chan,
+ std::forward< Fn0 >( function0) );
+ // then recur to process the rest
+ wait_first_outcome_impl< T >( chan,
+ std::forward< Fn1 >( function1),
+ std::forward< Fns >( functions) ... );
+}
+
+// Assume that all passed functions have the same return type. The return type
+// of wait_first_outcome() is the return type of the first passed function. It is
+// simply invalid to pass NO functions.
+//[wait_first_outcome
+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::buffered_channel< future_t > channel_t;
+ auto chanp(std::make_shared< channel_t >( 64) );
+ // launch all the relevant fibers
+ wait_first_outcome_impl< return_t >( chanp,
+ std::forward< Fn >( function),
+ std::forward< Fns >( functions) ... );
+ // retrieve the first future
+ future_t future( chanp->value_pop() );
+ // close the channel: no subsequent push() has to succeed
+ chanp->close();
+ // either return value or throw exception
+ return future.get();
+}
+//]
+
+// example usage
+Example wfo( runner, "wait_first_outcome()", [](){
+//[wait_first_outcome_ex
+ 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");
+//]
+});
+
+/*****************************************************************************
+* when_any, collect exceptions until success; throw exception_list if no
+* success
+*****************************************************************************/
+// define an exception to aggregate exception_ptrs; prefer
+// std::exception_list (N4407 et al.) once that becomes available
+//[exception_list
+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_;
+};
+//]
+
+// Assume that all passed functions have the same return type. The return type
+// of wait_first_success() is the return type of the first passed function. It is
+// simply invalid to pass NO functions.
+//[wait_first_success
+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::buffered_channel< future_t > channel_t;
+ auto chanp( std::make_shared< channel_t >( 64) );
+ // launch all the relevant fibers
+ wait_first_outcome_impl< return_t >( chanp,
+ 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( chanp->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
+ chanp->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;
+}
+//]
+
+// example usage
+Example wfss( runner, "wait_first_success()", [](){
+//[wait_first_success_ex
+ 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");
+//]
+
+ std::string thrown;
+ std::size_t count = 0;
+ try {
+ result = wait_first_success(
+ [](){ return sleeper("wfsf_first", 50, true); },
+ [](){ return sleeper("wfsf_second", 100, true); },
+ [](){ return sleeper("wfsf_third", 150, true); });
+ } catch ( exception_list const& e) {
+ thrown = e.what();
+ count = e.size();
+ } catch ( std::exception const& e) {
+ thrown = e.what();
+ }
+ std::cout << "wait_first_success(fail) threw '" << thrown << "': "
+ << count << " errors" << std::endl;
+ assert(thrown == "wait_first_success() produced only errors");
+ assert(count == 3);
+});
+
+/*****************************************************************************
+* when_any, heterogeneous
+*****************************************************************************/
+//[wait_first_value_het
+// 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 buffered_channel<boost::variant<T1, T2, ...>>; see remarks above.
+ typedef boost::variant< typename std::result_of< Fns() >::type ... > return_t;
+ typedef boost::fibers::buffered_channel< return_t > channel_t;
+ auto chanp( std::make_shared< channel_t >( 64) );
+ // launch all the relevant fibers
+ wait_first_value_impl< return_t >( chanp,
+ std::forward< Fns >( functions) ... );
+ // retrieve the first value
+ return_t value( chanp->value_pop() );
+ // close the channel: no subsequent push() has to succeed
+ chanp->close();
+ return value;
+}
+//]
+
+// example usage
+Example wfvh( runner, "wait_first_value_het()", [](){
+//[wait_first_value_het_ex
+ 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);
+//]
+});
+
+/*****************************************************************************
+* when_all, simple completion
+*****************************************************************************/
+// Degenerate case: when there are no functions to wait for, return
+// immediately.
+void wait_all_simple_impl( std::shared_ptr< boost::fibers::barrier >) {
+}
+
+// When there's at least one function to wait for, launch it and recur to
+// process the rest.
+//[wait_all_simple_impl
+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) ... );
+}
+//]
+
+// interface function: instantiate barrier, launch tasks, wait for barrier
+//[wait_all_simple
+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();
+}
+//]
+
+// example usage
+Example was( runner, "wait_all_simple()", [](){
+//[wait_all_simple_ex
+ wait_all_simple(
+ [](){ sleeper("was_long", 150); },
+ [](){ sleeper("was_medium", 100); },
+ [](){ sleeper("was_short", 50); });
+//]
+});
+
+/*****************************************************************************
+* when_all, return values
+*****************************************************************************/
+//[wait_nchannel
+// 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::buffered_channel< T > > chan,
+ std::size_t lm):
+ chan_( chan),
+ limit_( lm) {
+ assert(chan_);
+ if ( 0 == limit_) {
+ chan_->close();
+ }
+ }
+
+ boost::fibers::channel_op_status push( T && va) {
+ boost::fibers::channel_op_status ok =
+ chan_->push( std::forward< T >( va) );
+ if ( ok == boost::fibers::channel_op_status::success &&
+ --limit_ == 0) {
+ // after the 'limit_'th successful push, close the channel
+ chan_->close();
+ }
+ return ok;
+ }
+
+private:
+ std::shared_ptr< boost::fibers::buffered_channel< T > > chan_;
+ std::size_t limit_;
+};
+//]
+
+// 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 > > chan,
+ Fn && function) {
+ boost::fibers::fiber( [chan, function](){
+ chan->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 > > chan,
+ Fn0 && function0,
+ Fn1 && function1,
+ Fns && ... functions) {
+ // process the first function using the single-function overload
+ wait_all_values_impl< T >( chan, std::forward< Fn0 >( function0) );
+ // then recur to process the rest
+ wait_all_values_impl< T >( chan,
+ std::forward< Fn1 >( function1),
+ std::forward< Fns >( functions) ... );
+}
+
+//[wait_all_values_source
+// Return a shared_ptr<buffered_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::buffered_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::buffered_channel< return_t > channel_t;
+ // make the channel
+ auto chanp( std::make_shared< channel_t >( 64) );
+ // and make an nchannel facade to close it after 'count' items
+ auto ncp( std::make_shared< nchannel< return_t > >( chanp, 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 chanp;
+}
+//]
+
+// When all passed functions have completed, return vector<T> containing
+// collected results. Assume that all passed functions have the same return
+// type. It is simply invalid to pass NO functions.
+//[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::buffered_channel< return_t > > chan =
+ 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 == chan->pop(value) ) {
+ results.push_back( value);
+ }
+ // return vector to caller
+ return results;
+}
+//]
+
+Example wav( runner, "wait_all_values()", [](){
+//[wait_all_values_source_ex
+ std::shared_ptr< boost::fibers::buffered_channel< std::string > > chan =
+ 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 == chan->pop(value) ) {
+ std::cout << "wait_all_values_source() => '" << value
+ << "'" << std::endl;
+ }
+//]
+
+//[wait_all_values_ex
+ std::vector< std::string > values =
+ wait_all_values(
+ [](){ return sleeper("wav_late", 150); },
+ [](){ return sleeper("wav_middle", 100); },
+ [](){ return sleeper("wav_early", 50); });
+//]
+ std::cout << "wait_all_values() =>";
+ for ( std::string const& v : values) {
+ std::cout << " '" << v << "'";
+ }
+ std::cout << std::endl;
+});
+
+/*****************************************************************************
+* when_all, throw first exception
+*****************************************************************************/
+//[wait_all_until_error_source
+// Return a shared_ptr<buffered_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::buffered_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::buffered_channel< future_t > channel_t;
+ // make the channel
+ auto chanp( std::make_shared< channel_t >( 64) );
+ // and make an nchannel facade to close it after 'count' items
+ auto ncp( std::make_shared< nchannel< future_t > >( chanp, 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 chanp;
+}
+//]
+
+// When all passed functions have completed, return vector<T> containing
+// collected results, or throw the first exception thrown by any of the passed
+// functions. Assume that all passed functions have the same return type. It
+// is simply invalid to pass NO functions.
+//[wait_all_until_error
+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::buffered_channel< future_t > > chan(
+ 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 == chan->pop( future) ) {
+ results.push_back( future.get() );
+ }
+ // return vector to caller
+ return results;
+}
+//]
+
+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::buffered_channel< future_t > > chan =
+ 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 == chan->pop( future) ) {
+ std::string value( future.get() );
+ std::cout << "wait_all_until_error_source(success) => '" << value
+ << "'" << std::endl;
+ }
+//]
+
+ chan = wait_all_until_error_source(
+ [](){ return sleeper("wauesf_third", 150); },
+ [](){ return sleeper("wauesf_second", 100, true); },
+ [](){ return sleeper("wauesf_first", 50); });
+//[wait_all_until_error_ex
+ std::string thrown;
+//<-
+ try {
+ while ( boost::fibers::channel_op_status::success == chan->pop( future) ) {
+ std::string value( future.get() );
+ std::cout << "wait_all_until_error_source(fail) => '" << value
+ << "'" << std::endl;
+ }
+ } catch ( std::exception const& e) {
+ thrown = e.what();
+ }
+ std::cout << "wait_all_until_error_source(fail) threw '" << thrown
+ << "'" << std::endl;
+
+ thrown.clear();
+//->
+ 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); });
+//<-
+ std::cout << "wait_all_until_error(fail) =>";
+ for ( std::string const& v : values) {
+ std::cout << " '" << v << "'";
+ }
+ std::cout << std::endl;
+//->
+ } catch ( std::exception const& e) {
+ thrown = e.what();
+ }
+ std::cout << "wait_all_until_error(fail) threw '" << thrown
+ << "'" << std::endl;
+//]
+});
+
+/*****************************************************************************
+* when_all, collect exceptions
+*****************************************************************************/
+// When all passed functions have succeeded, return vector<T> containing
+// collected results, or throw exception_list containing all exceptions thrown
+// by any of the passed functions. Assume that all passed functions have the
+// same return type. It is simply invalid to pass NO functions.
+//[wait_all_collect_errors
+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::buffered_channel< future_t > > chan(
+ 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 == chan->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;
+}
+//]
+
+Example wace( runner, "wait_all_collect_errors()", [](){
+ std::vector< std::string > values = wait_all_collect_errors(
+ [](){ return sleeper("waces_late", 150); },
+ [](){ return sleeper("waces_middle", 100); },
+ [](){ return sleeper("waces_early", 50); });
+ std::cout << "wait_all_collect_errors(success) =>";
+ for ( std::string const& v : values) {
+ std::cout << " '" << v << "'";
+ }
+ std::cout << std::endl;
+
+ std::string thrown;
+ std::size_t errors = 0;
+ try {
+ values = wait_all_collect_errors(
+ [](){ return sleeper("wacef_late", 150, true); },
+ [](){ return sleeper("wacef_middle", 100, true); },
+ [](){ return sleeper("wacef_early", 50); });
+ std::cout << "wait_all_collect_errors(fail) =>";
+ for ( std::string const& v : values) {
+ std::cout << " '" << v << "'";
+ }
+ std::cout << std::endl;
+ } catch ( exception_list const& e) {
+ thrown = e.what();
+ errors = e.size();
+ } catch ( std::exception const& e) {
+ thrown = e.what();
+ }
+ std::cout << "wait_all_collect_errors(fail) threw '" << thrown
+ << "': " << errors << " errors" << std::endl;
+});
+
+/*****************************************************************************
+* when_all, heterogeneous
+*****************************************************************************/
+//[wait_all_members_get
+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() ... };
+}
+//]
+
+//[wait_all_members
+// 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) ) ... );
+}
+//]
+
+// used by following example
+//[wait_Data
+struct Data {
+ std::string str;
+ double inexact;
+ int exact;
+
+ friend std::ostream& operator<<( std::ostream& out, Data const& data)/*=;
+ ...*/
+//<-
+ {
+ return out << "Data{str='" << data.str << "', inexact=" << data.inexact
+ << ", exact=" << data.exact << "}";
+ }
+//->
+};
+//]
+
+// example usage
+Example wam( runner, "wait_all_members()", [](){
+//[wait_all_members_data_ex
+ 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;
+//]
+
+ std::string thrown;
+ try {
+ data = wait_all_members< Data >(
+ [](){ return sleeper("wamf_left", 100, true); },
+ [](){ return sleeper(3.14, 150); },
+ [](){ return sleeper(17, 50, true); });
+ std::cout << "wait_all_members<Data>(fail) => " << data << std::endl;
+ } catch ( std::exception const& e) {
+ thrown = e.what();
+ }
+ std::cout << "wait_all_members<Data>(fail) threw '" << thrown
+ << '"' << std::endl;
+
+//[wait_all_members_vector_ex
+ // 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;
+//]
+});
+
+
+/*****************************************************************************
+* main()
+*****************************************************************************/
+int main( int argc, char *argv[]) {
+ runner.run();
+ std::cout << "done." << std::endl;
+ return EXIT_SUCCESS;
+}
diff --git a/src/boost/libs/fiber/examples/work_sharing.cpp b/src/boost/libs/fiber/examples/work_sharing.cpp
new file mode 100644
index 00000000..f5e31e51
--- /dev/null
+++ b/src/boost/libs/fiber/examples/work_sharing.cpp
@@ -0,0 +1,130 @@
+// Copyright Nat Goodspeed + Oliver Kowalke 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <chrono>
+#include <condition_variable>
+#include <cstddef>
+#include <deque>
+#include <iomanip>
+#include <iostream>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <boost/assert.hpp>
+
+#include <boost/fiber/all.hpp>
+
+#include <boost/fiber/detail/thread_barrier.hpp>
+
+static std::size_t fiber_count{ 0 };
+static std::mutex mtx_count{};
+static boost::fibers::condition_variable_any cnd_count{};
+typedef std::unique_lock< std::mutex > lock_type;
+
+/*****************************************************************************
+* example fiber function
+*****************************************************************************/
+//[fiber_fn_ws
+void whatevah( char me) {
+ try {
+ std::thread::id my_thread = std::this_thread::get_id(); /*< get ID of initial thread >*/
+ {
+ 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) { /*< loop ten times >*/
+ boost::this_fiber::yield(); /*< yield to other fibers >*/
+ std::thread::id new_thread = std::this_thread::get_id(); /*< get ID of current thread >*/
+ if ( new_thread != my_thread) { /*< test if fiber was migrated to another thread >*/
+ my_thread = new_thread;
+ std::ostringstream buffer;
+ buffer << "fiber " << me << " switched to thread " << my_thread << '\n';
+ std::cout << buffer.str() << std::flush;
+ }
+ }
+ } catch ( ... ) {
+ }
+ lock_type lk( mtx_count);
+ if ( 0 == --fiber_count) { /*< Decrement fiber counter for each completed fiber. >*/
+ lk.unlock();
+ cnd_count.notify_all(); /*< Notify all fibers waiting on `cnd_count`. >*/
+ }
+}
+//]
+
+/*****************************************************************************
+* example thread function
+*****************************************************************************/
+//[thread_fn_ws
+void thread( boost::fibers::detail::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< boost::fibers::algo::shared_work >(); /*<
+ Install the scheduling algorithm `boost::fibers::algo::shared_work` in order to
+ join the work sharing.
+ >*/
+ b->wait(); /*< sync with other threads: allow them to start processing >*/
+ lock_type lk( mtx_count);
+ cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); /*<
+ 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.
+ >*/
+ BOOST_ASSERT( 0 == fiber_count);
+}
+//]
+
+/*****************************************************************************
+* main()
+*****************************************************************************/
+int main( int argc, char *argv[]) {
+ std::cout << "main thread started " << std::this_thread::get_id() << std::endl;
+//[main_ws
+ boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_work >(); /*<
+ Install the scheduling algorithm `boost::fibers::algo::shared_work` in the main thread
+ too, so each new fiber gets launched into the shared pool.
+ >*/
+
+ for ( char c : std::string("abcdefghijklmnopqrstuvwxyz")) { /*<
+ 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.
+ >*/
+ boost::fibers::fiber([c](){ whatevah( c); }).detach();
+ ++fiber_count; /*< Increment fiber counter for each new fiber. >*/
+ }
+ boost::fibers::detail::thread_barrier b( 4);
+ std::thread threads[] = { /*<
+ Launch a couple of threads that join the work sharing.
+ >*/
+ std::thread( thread, & b),
+ std::thread( thread, & b),
+ std::thread( thread, & b)
+ };
+ b.wait(); /*< sync with other threads: allow them to start processing >*/
+ {
+ lock_type/*< `lock_type` is typedef'ed as __unique_lock__< [@http://en.cppreference.com/w/cpp/thread/mutex `std::mutex`] > >*/ lk( mtx_count);
+ cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); /*<
+ 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.
+ >*/
+ } /*<
+ Releasing lock of mtx_count is required before joining the threads, otherwise
+ the other threads would be blocked inside condition_variable::wait() and
+ would never return (deadlock).
+ >*/
+ BOOST_ASSERT( 0 == fiber_count);
+ for ( std::thread & t : threads) { /*< wait for threads to terminate >*/
+ t.join();
+ }
+//]
+ std::cout << "done." << std::endl;
+ return EXIT_SUCCESS;
+}
diff --git a/src/boost/libs/fiber/examples/work_stealing.cpp b/src/boost/libs/fiber/examples/work_stealing.cpp
new file mode 100644
index 00000000..42c99491
--- /dev/null
+++ b/src/boost/libs/fiber/examples/work_stealing.cpp
@@ -0,0 +1,126 @@
+// Copyright Nat Goodspeed + Oliver Kowalke 2015.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <chrono>
+#include <condition_variable>
+#include <cstddef>
+#include <deque>
+#include <iomanip>
+#include <iostream>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <boost/assert.hpp>
+
+#include <boost/fiber/all.hpp>
+
+#include <boost/fiber/detail/thread_barrier.hpp>
+
+static std::size_t fiber_count{ 0 };
+static std::mutex mtx_count{};
+static boost::fibers::condition_variable_any cnd_count{};
+typedef std::unique_lock< std::mutex > lock_type;
+
+/*****************************************************************************
+* example fiber function
+*****************************************************************************/
+//[fiber_fn_ws
+void whatevah( char me) {
+ try {
+ std::thread::id my_thread = std::this_thread::get_id(); /*< get ID of initial thread >*/
+ {
+ 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) { /*< loop ten times >*/
+ boost::this_fiber::yield(); /*< yield to other fibers >*/
+ std::thread::id new_thread = std::this_thread::get_id(); /*< get ID of current thread >*/
+ if ( new_thread != my_thread) { /*< test if fiber was migrated to another thread >*/
+ my_thread = new_thread;
+ std::ostringstream buffer;
+ buffer << "fiber " << me << " switched to thread " << my_thread << '\n';
+ std::cout << buffer.str() << std::flush;
+ }
+ }
+ } catch ( ... ) {
+ }
+ lock_type lk( mtx_count);
+ if ( 0 == --fiber_count) { /*< Decrement fiber counter for each completed fiber. >*/
+ lk.unlock();
+ cnd_count.notify_all(); /*< Notify all fibers waiting on `cnd_count`. >*/
+ }
+}
+//]
+
+/*****************************************************************************
+* example thread function
+*****************************************************************************/
+//[thread_fn_ws
+void thread() {
+ std::ostringstream buffer;
+ buffer << "thread started " << std::this_thread::get_id() << std::endl;
+ std::cout << buffer.str() << std::flush;
+ boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( 4); /*<
+ Install the scheduling algorithm `boost::fibers::algo::work_stealing` in order to
+ join the work sharing.
+ >*/
+ lock_type lk( mtx_count);
+ cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); /*<
+ 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.
+ >*/
+ BOOST_ASSERT( 0 == fiber_count);
+}
+//]
+
+/*****************************************************************************
+* main()
+*****************************************************************************/
+int main( int argc, char *argv[]) {
+ std::cout << "main thread started " << std::this_thread::get_id() << std::endl;
+//[main_ws
+ for ( char c : std::string("abcdefghijklmnopqrstuvwxyz")) { /*<
+ 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.
+ >*/
+ boost::fibers::fiber([c](){ whatevah( c); }).detach();
+ ++fiber_count; /*< Increment fiber counter for each new fiber. >*/
+ }
+ std::thread threads[] = { /*<
+ Launch a couple of threads that join the work sharing.
+ >*/
+ std::thread( thread),
+ std::thread( thread),
+ std::thread( thread)
+ };
+ boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( 4); /*<
+ Install the scheduling algorithm `boost::fibers::algo::work_stealing` in the main thread
+ too, so each new fiber gets launched into the shared pool.
+ >*/
+ {
+ lock_type/*< `lock_type` is typedef'ed as __unique_lock__< [@http://en.cppreference.com/w/cpp/thread/mutex `std::mutex`] > >*/ lk( mtx_count);
+ cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); /*<
+ 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.
+ >*/
+ } /*<
+ Releasing lock of mtx_count is required before joining the threads, otherwise
+ the other threads would be blocked inside condition_variable::wait() and
+ would never return (deadlock).
+ >*/
+ BOOST_ASSERT( 0 == fiber_count);
+ for ( std::thread & t : threads) { /*< wait for threads to terminate >*/
+ t.join();
+ }
+//]
+ std::cout << "done." << std::endl;
+ return EXIT_SUCCESS;
+}