It seems like most of the attachments were somehow lost, so I'll try
sending them again. There should  be a CppClient.cpp, CppServer.cpp,
TAsync.h, TAsioAsync.h and TAsioAsync.cpp

On Thu, Jul 9, 2009 at 2:41 PM, Erik Bernhardsson<[email protected]> wrote:
> Hi everyone,
>
> There's been some discussions about asynchronous C++ clients and
> servers (THRIFT-1). Inspired by the Twisted server (THRIFT-148) by
> Esteve Fernandez, as well as his C++ ideas (THRIFT-311), we (Erik
> Bernhardsson, Mattias de Zalenski) decided to implement our own
> version here at Spotify.
>
> Based on TFramedTransport and Boost.ASIO, this version is event-driven
> and runs fully asynchronously in a single thread. There is no overhead
> in terms of additional threads. This code is not thread-safe yet, in
> the sense that multiple threads cannot simultaneously invoke calls on
> the same client, though we're definitely interested in discussing how
> to implement this.
>
> We have implemented an additional C++ stub generator (invoked by --gen
> cpp:async) which adds two extra arguments to all client and server
> methods. These are a callback and an errback, which are both Boost
> closures. On the client side, these are passed when making a call, and
> will be invoked when the call returns. On the server side, the server
> is free to invoke these at any point in the future in order to respond
> to the client. Calls and responses can be sent in any order. See code
> example below.
>
> The stub generator is independent of the ASIO code and can easily be
> plugged into other reactor loops/frameworks. It shares most of the
> code with the standard cpp generator (and does generate the
> synchronous stubs as well). We have attached a diff between them.
>
> This is still very much an early version, so feel welcome to submit
> your comments. There are a few design decisions we still aren't 100%
> sure of. Additionally, we haven't worked out how to handle low-level
> failures such as disconnects. We are also working on a stress test and
> some unit tests.
>
> We have compiled everything by adding the stub generator to
> compiler/cpp/src/ and the client/server code to lib/cpp/src/async/. In
> order for this to work, the corresponding Makefiles have to be
> modified and everything has to be compiled with -lboost_system.
>
> Code example for the server follows below. We have modified the add
> method so that it sleeps for num1+num2 seconds before returning, so
> that there is an easy way of generating responses in a different order
> than requests. We only include the most relevant parts below, but have
> attached the full source code. Client code follows after the server
> code.
>
> class CalculatorAsyncHandler : public CalculatorAsyncIf {
>  public:
>  CalculatorAsyncHandler() {}
>
>  virtual void ping(boost::function<void (void)> callback,
> boost::function<void (Calculator_ping_result)> errback) {
>    printf("ping()\n");
>    callback();
>  }
>
>  virtual void add(const int32_t num1, const int32_t num2,
> boost::function<void (int32_t)> callback, boost::function<void
> (Calculator_add_result)> errback) {
>    printf("add(%d,%d)\n", num1, num2);
>    boost::shared_ptr<boost::asio::deadline_timer> timer(new
> boost::asio::deadline_timer(io_service,
> boost::posix_time::seconds(num1 + num2)));
>    timer->async_wait(boost::bind(&CalculatorAsyncHandler::wait_done,
> this, num1 + num2, callback, timer));
>  }
>  virtual void wait_done(const int32_t sum, boost::function<void
> (int32_t)> callback, boost::shared_ptr<boost::asio::deadline_timer>) {
>    callback(sum);
>    // timer will fall out of scope now and will be deleted
>  }
>
>  virtual void calculate(const int32_t logid, const Work& w,
> boost::function<void (int32_t)> callback, boost::function<void
> (Calculator_calculate_result)> errback) {
> (...)
>    case DIVIDE:
>      if (w.num2 == 0) {
>        InvalidOperation io;
>        io.what = w.op;
>        io.why = "Cannot divide by 0";
>        errback(calculate_ouch(io));
>        return;
>      }
>      val = w.num1 / w.num2;
>      break;
>    default:
>      errback(calculate_failure(std::string("Invalid Operation")));
>      return;
>    }
>  (...)
> }
> (... other methods, omitted for brevity)
>
> };
>
> int main(int argc, char **argv) {
>  boost::shared_ptr<protocol::TProtocolFactory> protocolFactory(new
> protocol::TBinaryProtocolFactory());
>  boost::shared_ptr<CalculatorAsyncHandler> handler(new
> CalculatorAsyncHandler());
>  boost::shared_ptr<TProcessor> processor(new
> CalculatorAsyncProcessor(handler));
>
>  boost::shared_ptr<apache::thrift::async::TAsioServer> server(
>                                                               new
> apache::thrift::async::TAsioServer(
>
>                               io_service,
>
>                               9090,
>
>                               protocolFactory,
>
>                               protocolFactory,
>
>                               processor));
>
>  server->start(); // Nonblocking
>  io_service.run(); // Blocking
>
>  return 0;
> }
>
> Code for the client:
>
> void pingback() {
>  printf("ping()\n");
> }
>
> void pingerr(tutorial::Calculator_ping_result result) {
>  printf("Exception caught\n");
> }
>
> void addback(int32_t a, int32_t b, int32_t sum) {
>  printf("%d+%d=%d\n", a, b, sum);
> }
>
> void adderr(tutorial::Calculator_add_result result) {
>  printf("Exception caught\n");
> }
>
> void connected(boost::shared_ptr<tutorial::CalculatorAsyncClient> client) {
>  client->ping(pingback, pingerr);
>
>  client->add(2, 3, boost::bind(&addback, 2, 3, _1), &adderr);  //
> will return after 5s
>  client->add(1, 2, boost::bind(&addback, 1, 2, _1), &adderr);  //
> will return after 3s
>  client->add(1, 1, boost::bind(&addback, 1, 1, _1), &adderr);  //
> will return after 2s
> }
>
> int main(int argc, char* argv[])
> {
>  try
>  {
>    boost::asio::io_service io_service;
>
>    boost::shared_ptr<protocol::TProtocolFactory> protocolFactory(new
> protocol::TBinaryProtocolFactory());
>
>    boost::shared_ptr<async::TAsioClient> client (
>                                                  new async::TAsioClient(
>
>  io_service,
>
>  protocolFactory,
>
>  protocolFactory));
>
>    client->connect("localhost", 9090, connected); // the type of the
> client (tutorial::CalculatorAsyncClient) is inferred from the
> signature of connected
>
>    io_service.run();
>  }
>  catch (std::exception& e)
>  {
>    std::cout << "Exception: " << e.what() << "\n";
>  }
>
>  return 0;
> }
>
> Regards,
> Erik Bernhardsson,
> Mattias de Zalenski
> Spotify - http://www.spotify.com/
>

Reply via email to