• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //          Copyright Nat Goodspeed 2015.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 #include <boost/fiber/all.hpp>
7 #include <memory>                   // std::shared_ptr
8 #include <thread>
9 #include <chrono>
10 #include <iostream>
11 #include <sstream>
12 #include <exception>
13 #include <cassert>
14 
15 /*****************************************************************************
16 *   example async API
17 *****************************************************************************/
18 // introduce class-scope typedef
19 struct AsyncAPIBase {
20     // error callback accepts an int error code; 0 == success
21     typedef int errorcode;
22 };
23 
24 //[Response
25 // every async operation receives a subclass instance of this abstract base
26 // class through which to communicate its result
27 struct Response {
28     typedef std::shared_ptr< Response > ptr;
29 
30     // called if the operation succeeds
31     virtual void success( std::string const& data) = 0;
32 
33     // called if the operation fails
34     virtual void error( AsyncAPIBase::errorcode ec) = 0;
35 };
36 //]
37 
38 // the actual async API
39 class AsyncAPI: public AsyncAPIBase {
40 public:
41     // constructor acquires some resource that can be read
42     AsyncAPI( std::string const& data);
43 
44 //[method_init_read
45     // derive Response subclass, instantiate, pass Response::ptr
46     void init_read( Response::ptr);
47 //]
48 
49     // ... other operations ...
50     void inject_error( errorcode ec);
51 
52 private:
53     std::string data_;
54     errorcode   injected_;
55 };
56 
57 /*****************************************************************************
58 *   fake AsyncAPI implementation... pay no attention to the little man behind
59 *   the curtain...
60 *****************************************************************************/
AsyncAPI(std::string const & data)61 AsyncAPI::AsyncAPI( std::string const& data) :
62     data_( data),
63     injected_( 0) {
64 }
65 
inject_error(errorcode ec)66 void AsyncAPI::inject_error( errorcode ec) {
67     injected_ = ec;
68 }
69 
init_read(Response::ptr response)70 void AsyncAPI::init_read( Response::ptr response) {
71     // make a local copy of injected_
72     errorcode injected( injected_);
73     // reset it synchronously with caller
74     injected_ = 0;
75     // local copy of data_ so we can capture in lambda
76     std::string data( data_);
77     // Simulate an asynchronous I/O operation by launching a detached thread
78     // that sleeps a bit before calling either completion method.
79     std::thread( [injected, response, data](){
80                      std::this_thread::sleep_for( std::chrono::milliseconds(100) );
81                      if ( ! injected) {
82                          // no error, call success()
83                          response->success( data);
84                      } else {
85                          // injected error, call error()
86                          response->error( injected);
87                      }
88                  }).detach();
89 }
90 
91 /*****************************************************************************
92 *   adapters
93 *****************************************************************************/
94 // helper function
95 std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode);
96 
97 //[PromiseResponse
98 class PromiseResponse: public Response {
99 public:
100     // called if the operation succeeds
success(std::string const & data)101     virtual void success( std::string const& data) {
102         promise_.set_value( data);
103     }
104 
105     // called if the operation fails
error(AsyncAPIBase::errorcode ec)106     virtual void error( AsyncAPIBase::errorcode ec) {
107         promise_.set_exception(
108                 std::make_exception_ptr(
109                     make_exception("read", ec) ) );
110     }
111 
get_future()112     boost::fibers::future< std::string > get_future() {
113         return promise_.get_future();
114     }
115 
116 private:
117     boost::fibers::promise< std::string >   promise_;
118 };
119 //]
120 
121 //[method_read
read(AsyncAPI & api)122 std::string read( AsyncAPI & api) {
123     // Because init_read() requires a shared_ptr, we must allocate our
124     // ResponsePromise on the heap, even though we know its lifespan.
125     auto promisep( std::make_shared< PromiseResponse >() );
126     boost::fibers::future< std::string > future( promisep->get_future() );
127     // Both 'promisep' and 'future' will survive until our lambda has been
128     // called.
129     api.init_read( promisep);
130     return future.get();
131 }
132 //]
133 
134 /*****************************************************************************
135 *   helpers
136 *****************************************************************************/
make_exception(std::string const & desc,AsyncAPI::errorcode ec)137 std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode ec) {
138     std::ostringstream buffer;
139     buffer << "Error in AsyncAPI::" << desc << "(): " << ec;
140     return std::runtime_error( buffer.str() );
141 }
142 
143 /*****************************************************************************
144 *   driving logic
145 *****************************************************************************/
main(int argc,char * argv[])146 int main(int argc, char *argv[]) {
147     // prime AsyncAPI with some data
148     AsyncAPI api("abcd");
149 
150     // successful read(): retrieve it
151     std::string data( read( api) );
152     assert(data == "abcd");
153 
154     // read() with error
155     std::string thrown;
156     api.inject_error(1);
157     try {
158         data = read( api);
159     } catch ( std::exception const& e) {
160         thrown = e.what();
161     }
162     assert(thrown == make_exception("read", 1).what() );
163 
164     std::cout << "done." << std::endl;
165 
166     return EXIT_SUCCESS;
167 }
168