Skip to content

Instantly share code, notes, and snippets.

@vinniefalco
Created September 20, 2017 16:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vinniefalco/04d6299c94c5a71f2808caeb742000de to your computer and use it in GitHub Desktop.
Save vinniefalco/04d6299c94c5a71f2808caeb742000de to your computer and use it in GitHub Desktop.
//
// Copyright (c) 2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// 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 <beast/core/async_result.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <list>
#include <thread>
/** Start an asynchronous operation in an synchronous context
*/
class sync_io_service
{
boost::asio::io_service ios_;
boost::optional<boost::asio::io_service::work> work_;
std::list<std::thread> workers_;
template<class SyncOp, class Handler>
class task
{
SyncOp op_;
boost::asio::io_service& ios_;
Handler h_;
public:
template<class DeducedHandler>
task(
DeducedHandler&& handler,
boost::asio::io_service& ios,
SyncOp const& op)
: op_(op)
, ios_(ios)
, h_(std::forward<DeducedHandler>(handler))
{
}
void
operator()
{
beast::error_code ec;
try
{
ec = op_();
}
catch(...)
{
// Could use a different error code
ec = beast::errc::io_error;
}
ios_.post(
beast::bind_handler(std::move(h_), ec));
}
};
public:
/** Constructor
@param n_threads The number of worker threads to launch.
*/
explicit
sync_io_service(std::size_t n_threads)
: work_(ios_)
{
while(n_threads--)
workers_.emplace([&]{ ios_.run(); });
}
/** Destructor
This function will not return until all pending work
has been completed.
*/
~sync_io_service()
{
work_ = boost::none;
for(auto& w : workers_)
w.join();
}
/** Launch an operation
This function launches a synchronous operation in an
implementation-provided thread. The call always returns
immediately. The asynchronous operation will continue
until one of the following conditions is met:
@li The synchronous function exits with an error code.
@li The synchronous function exits via exception. In
this case the error code delivered to the completion
handler will be `beast::errc::io_error`.
@param sync_op The synchronous operation to perform. Copies
will be made of this object as needed. The equivalent
function signature must be:
@code
error_code sync_op();
@endcode
The error returned from the synchronous operation function
will be delivered to the completion handler.
@param io_service The `io_service` upon which the handler
will be dispatched.
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required.
The equivalent function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class SyncOp, class Handler>
beast::async_return_type<Handler, void(error_code)>
async_run(
SyncOp const& sync_op,
boost::asio::io_service& ios,
Handler&& handler)
{
beast::async_completion<
Handler,
void(error_code)> init{handler};
ios_.post(
task<
SyncOp,
beast::handler_type<Handler, void(error_code)>
>{
std::forward<Handler>(handler),
ios,
sync_op});
return init.result.get();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment