Boost logo

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