summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/fiber/examples/adapt_callbacks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/fiber/examples/adapt_callbacks.cpp')
-rw-r--r--src/boost/libs/fiber/examples/adapt_callbacks.cpp316
1 files changed, 316 insertions, 0 deletions
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;
+}