|
Boost Users : |
Subject: Re: [Boost-users] boost::asio blocking socket read with timeout with multiple threads
From: Richard Hodges (hodges.r_at_[hidden])
Date: 2018-03-19 16:03:48
On Mon, 2018-03-19 at 14:21 +0000, Thomas Quarendon via Boost-users
wrote:
> > On 19 March 2018 at 10:33 tom_at_[hidden] wrote:
> >
> >
> > Attempting to understand the implementation it feels like this
> > could be made to work.
>
> Interestingly I notice that the basic_socket_streambuf class (in 1.67
> at least), in accordance with the N4656 draft specification, DOES
> have support for timeouts. It implements the overflow and underflow
> calls using lower level asio calls, somewhat shadowing the
> implementation of socket_ops::sync_recv, but, crucially, not
> attempting an initial blocking read, then passing a timeout to the
> "poll_read" call:
>
> // Wait for socket to become ready.
> if (detail::socket_ops::poll_read(
> socket().native_handle(), 0, timeout(), ec_) < 0)
>
>
> This would seem to suggest that fundamentally, reading with a timeout
> can be made to work, as it works fine here.
If this doesn't work out for you, here's another way - it relies on the
fact that you can create an asio socket object from a socket handle,
and that socket object can be owned by a different io_context.
This allows us to reserve a thread for a separate utility context:
std::string read_line_with_timeout(boost::asio::ip::tcp::socket &sock,
boost::asio::streambuf &buf)
{
namespace asio = boost::asio;
// these statics could of course be encapsulated into a service
object
static asio::io_context executor;
static asio::io_context::work work(executor);
static std::thread mythread{[&] { executor.run(); }};
auto temp_socket = asio::generic::stream_protocol::socket(executor,
sock.loca
l_endpoint().protocol(),
dup(sock.
native_handle()));
auto timer = asio::deadline_timer(executor,
boost::posix_time::milliseconds(3000));
std::condition_variable cv;
std::mutex m;
int done_count = 0;
boost::system::error_code err;
auto get_lock = [&] { return std::unique_lock<std::mutex>(m); };
auto aborted = [](boost::system::error_code const &ec) { return ec
== boost::asio::error::operation_aborted; };
auto common_handler = [&](auto ec)
{
if (not aborted(ec))
{
auto lock = get_lock();
if (done_count++ == 0) {
err = ec;
boost::system::error_code sink;
temp_socket.cancel(sink);
timer.cancel(sink);
}
lock.unlock();
cv.notify_one();
}
};
async_read_until(temp_socket, buf, '\n', [&](auto ec, auto&&...) {
common_handler(ec); });
timer.async_wait([&](auto ec)
{
common_handler(ec ? ec :
asio::error::timed_out);
});
auto lock = get_lock();
cv.wait(lock, [&] { return done_count == 2; });
if (err) throw boost::system::system_error(err);
std::istream is(&buf);
std::string result;
std::getline(is, result);
return result;
}
> So, for non SSL usage, replicating the same kind of logic that
> basic_socket_streambuf does would seem like it would work. However,
> that won't work for SSL. You'd basically have to create your own
> socket class that did this, and then wrap that in the ssl_stream.
>
> IMHO at least, having an equivalent of the "expiry" functionality
> provided by basic_socket_streambuf but at the tcp::socket class level
> would seem desirable. It seems odd that this functionality is
> considered useful by the N4656 draft specification at the
> basic_socket_streambuf level, but not at the tcp::socket level. And
> looking at what basic_socket_streambuf does, it wouldn't seem like it
> would be that complex.
>
> Thanks.
> _______________________________________________
> Boost-users mailing list
> Boost-users_at_[hidden]
> https://lists.boost.org/mailman/listinfo.cgi/boost-users
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net