Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost::asio with boost::unique_future

According to http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/overview/cpp2011/futures.html, we can use boost::asio with std::future. But I couldn't find any information about working with boost::unique_future, which has more functions, such as then(). How can I use?

like image 828
ikh Avatar asked Mar 15 '14 13:03

ikh


People also ask

What is Boost :: ASIO :: io_service?

Asio defines boost::asio::io_service , a single class for an I/O service object. Every program based on Boost. Asio uses an object of type boost::asio::io_service . This can also be a global variable. While there is only one class for an I/O service object, several classes for I/O objects exist.

Is boost :: ASIO :: io_service thread safe?

It is safe to post handlers from within a handler for a single instance of an io_service according to the documentation.

Does boost ASIO use threads?

If the run() method is called on an object of type boost::asio::io_service, the associated handlers are invoked on the same thread. By using multiple threads, an application can call multiple run() methods simultaneously.

How does boost ASIO work?

At its core, Boost Asio provides a task execution framework that you can use to perform operations of any kind. You create your tasks as function objects and post them to a task queue maintained by Boost Asio. You enlist one or more threads to pick these tasks (function objects) and invoke them.


1 Answers

Boost.Asio only provides first-class support for asynchronous operations to return a C++11 std::future or an actual value in stackful coroutines. Nevertheless, the requirements on asynchronous operations documents how to customize the return type for other types, such as Boost.Thread's boost::unique_future. It requires:

  • A specialization of the handler_type template. This template is used to determine the actual handler to use based on the asynchronous operation's signature.
  • A specialization of the async_result template. This template is used both to determine the return type and to extract the return value from the handler.

Below is a minimal complete example demonstrating deadline_timer::async_wait() returning boost:unique_future with a basic calculation being performed over a series of continuations composed with .then(). To keep the example simple, I have opted to only specialize handler_type for the asynchronous operation signatures used in the example. For a complete reference, I highly suggest reviewing use_future.hpp and impl/use_future.hpp.

#include <exception> // current_exception, make_exception_ptr
#include <memory> // make_shared, shared_ptr
#include <thread> // thread
#include <utility> // move

#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION

#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/future.hpp>

/// @brief Class used to indicate an asynchronous operation should return
///        a boost::unique_future.
class use_unique_future_t {};

/// @brief A special value, similiar to std::nothrow.
constexpr use_unique_future_t use_unique_future;

namespace detail {

/// @brief Completion handler to adapt a boost::promise as a completion
///        handler.
template <typename T>
class unique_promise_handler;

/// @brief Completion handler to adapt a void boost::promise as a completion
///        handler.
template <>
class unique_promise_handler<void>
{
public:
  /// @brief Construct from use_unique_future special value.
  explicit unique_promise_handler(use_unique_future_t)
    : promise_(std::make_shared<boost::promise<void> >())
  {}

  void operator()(const boost::system::error_code& error)
  {
    // On error, convert the error code into an exception and set it on
    // the promise.
    if (error)
      promise_->set_exception(
          std::make_exception_ptr(boost::system::system_error(error)));
    // Otherwise, set the value.
    else
      promise_->set_value();
  }

//private:
  std::shared_ptr<boost::promise<void> > promise_;
};

// Ensure any exceptions thrown from the handler are propagated back to the
// caller via the future.
template <typename Function, typename T>
void asio_handler_invoke(
    Function function,
    unique_promise_handler<T>* handler)
{
  // Guarantee the promise lives for the duration of the function call.
  std::shared_ptr<boost::promise<T> > promise(handler->promise_);
  try
  {
    function();
  }
  catch (...)
  {
    promise->set_exception(std::current_exception());
  }
}

} // namespace detail

namespace boost {
namespace asio {

/// @brief Handler type specialization for use_unique_future.
template <typename ReturnType>
struct handler_type<
    use_unique_future_t,
    ReturnType(boost::system::error_code)>
{
  typedef ::detail::unique_promise_handler<void> type;
};

/// @brief Handler traits specialization for unique_promise_handler.
template <typename T>
class async_result< ::detail::unique_promise_handler<T> >
{
public:
  // The initiating function will return a boost::unique_future.
  typedef boost::unique_future<T> type;

  // Constructor creates a new promise for the async operation, and obtains the
  // corresponding future.
  explicit async_result(::detail::unique_promise_handler<T>& handler)
  {
    value_ = handler.promise_->get_future();
  }

  // Obtain the future to be returned from the initiating function.
  type get() { return std::move(value_); }

private:
  type value_;
};

} // namespace asio
} // namespace boost

int main()
{
  boost::asio::io_service io_service;
  boost::asio::io_service::work work(io_service);

  // Run io_service in its own thread to demonstrate future usage.
  std::thread thread([&io_service](){ io_service.run(); });

  // Arm 3 second timer.
  boost::asio::deadline_timer timer(
      io_service, boost::posix_time::seconds(3));

  // Asynchronously wait on the timer, then perform basic calculations
  // within the future's continuations.
  boost::unique_future<int> result =
      timer.async_wait(use_unique_future)
        .then([](boost::unique_future<void> future){
           std::cout << "calculation 1" << std::endl;
           return 21;
        })
        .then([](boost::unique_future<int> future){
          std::cout << "calculation 2" << std::endl;
          return 2 * future.get();
        })
      ;

  std::cout << "Waiting for result" << std::endl;
  // Wait for the timer to trigger and for its continuations to calculate
  // the result.
  std::cout << result.get() << std::endl;

  // Cleanup.
  io_service.stop();
  thread.join();
}

Output:

Waiting for result
calculation 1
calculation 2
42
like image 137
Tanner Sansbury Avatar answered Oct 12 '22 18:10

Tanner Sansbury