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-23 09:14:35


forgive me. I read "a synchronous call" as "asynchronous call".

Of course the correct way to "cancel" a sync call in linux is to raise a
signal, which should cause the socket's read to return with EINTR.

But before I realised my mistake, I wrote this little test to prove that
async calls are canclled :) Maybe someone will find it useful...

#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <boost/asio.hpp>

using namespace std::literals;
namespace asio = boost::asio;

using protocol = asio::ip::tcp;

void server(protocol::acceptor& acceptor, int child_pid)
{
    acceptor.listen();
    auto& executor = acceptor.get_io_context();
    auto sock = protocol::socket(executor);
    auto timer = asio::system_timer(executor);

    acceptor.accept(sock);

    auto read_handler = [](auto ec, auto...)
    {
        if (ec)
            std::cerr << "read handler error: " << ec.message();
        else
            std::cerr << "strange - we expected an error";
    };

    auto timer_handler = [&](auto ec)
    {
        if (not ec) {
            sock.cancel();
        }
    };

    sock.async_read_some(asio::null_buffers(), read_handler);
    timer.expires_after(1s);
    timer.async_wait(timer_handler);

    executor.run();

    auto data = "foo"s;
    sock.write_some(asio::buffer(data));

    int status = 0;
    waitpid(child_pid, & status, 0);
}

void client(asio::io_context& executor, protocol::endpoint server_endpoint)
{
    protocol::socket sock(executor);
    sock.connect(server_endpoint);

    auto on_read = [](auto, auto) {};
    sock.async_read_some(asio::null_buffers(), on_read);

    executor.run();
}

int main() {

    auto executor = asio::io_context();
    auto acceptor = protocol::acceptor(executor);
    acceptor.open(protocol::v4());
    acceptor.bind(protocol::endpoint(protocol::v4(), 0));
    auto server_endpoint = acceptor.local_endpoint();
    executor.notify_fork(asio::io_context::fork_prepare);
    int child_pid = fork();
    if (child_pid < 0)
    {
        std::cerr << "fork failed" << std::endl;
        std::exit(100);
    }
    else if (child_pid > 0)
    {
        executor.notify_fork(asio::io_context::fork_parent);
        server(acceptor,child_pid);
    }
    else
    {
        executor.notify_fork(asio::io_context::fork_child);
        client(executor, server_endpoint);
    }

    return 0;
}

expected output:

> *read handler error: Operation canceled*

On 23 March 2018 at 08:44, Thomas Quarendon via Boost-users <
boost-users_at_[hidden]> wrote:

> > This seems unlikely. I have been using asio in production code on Linux
> for
> > 4 years. Can you post a mcve so I can test?
> Yes, the code I started this thread with:
> https://gist.github.com/tomq42/331b8d48110c5025e0fce93e689bd5a3
>
> I don't think this is a surprise. As far as I understand it, "cancel"
> isn't expected to cancel a *synchronous* read from the socket. It's more of
> a surprise to me that calling close on the socket doesn't have the effect
> of causing the read to return with an error. Both of these things work on
> Windows, and I started out on Windows, so the code was all fine there.
> _______________________________________________
> 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