123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- // 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;
- }
|