1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 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;
}
|