Boost logo

Boost :

From: Dominique Devienne (ddevienne_at_[hidden])
Date: 2024-01-18 18:18:53


Hi,

I'm trying to use ASIO to wait until a socket is ready to read, or timeout.
This is a blocking wait. But the socket is not "owned" by ASIO, but is from
the libpq PostgreSQL connection. So ASIO should NOT consume any of
the input on the socket, just notify me when that socket is "read-ready".

I've cobbled together the following test code:

```
    const int socket2 = PQsocket(conn2.handle());
    BOOST_CHECK(socket2 > 0);

    int timr_err_code = 0;
    int sock_err_code = 0;
    bool timr_aborted = false;
    bool sock_aborted = false;
    std::string timr_err_msg;
    std::string sock_err_msg;
    bool timed_out = true;

    auto c1 = "C# 1"sv;
    conn2.listen(c1);
    conn1->notify(c1, "wake up!"sv);

    {
        boost::asio::io_context io_ctx;
        boost::asio::ip::tcp::socket io_sock(io_ctx);
        io_sock.assign(boost::asio::ip::tcp::v4(), socket2);

        boost::asio::steady_timer io_timer(io_ctx);
        io_timer.expires_from_now(std::chrono::milliseconds(100));

        io_sock.async_wait(
            io_sock.wait_read,
            [&](const boost::system::error_code& errc) {
                if (errc) {
                    sock_err_code = errc.value();
                    sock_err_msg = errc.message();
                    sock_aborted = (errc ==
boost::asio::error::operation_aborted);
                }
                timed_out = false;
                //io_timer.cancel();
                io_ctx.stop();
            }
        );

        io_timer.async_wait(
            [&](const boost::system::error_code& errc) {
                if (errc) {
                    timr_err_code = errc.value();
                    timr_err_msg = errc.message();
                    timr_aborted = (errc ==
boost::asio::error::operation_aborted);
                }
                io_sock.cancel();
                io_ctx.stop();
            }
        );

        io_ctx.run(); // blocks until either async op above runs
    }

    // there's a notif to read, so io_sock.async_wait should have completed
    BOOST_CHECK_EQUAL(timr_err_code, 0);
    BOOST_CHECK_EQUAL(sock_err_code, 0);
    BOOST_CHECK_EQUAL(timr_err_msg, "");
    BOOST_CHECK_EQUAL(sock_err_msg, "");
    BOOST_CHECK_EQUAL(timr_aborted, false);
    BOOST_CHECK_EQUAL(sock_aborted, false);
    BOOST_CHECK_EQUAL(timed_out, false);

    auto notif = conn2.notification();
    BOOST_REQUIRE(notif);
    BOOST_CHECK_EQUAL(notif.backend_pid(), pid1);
    BOOST_CHECK_EQUAL(notif.channel(), c1);
    BOOST_CHECK_EQUAL(notif.payload(), "wake up!"sv);
```

The io_sock.async_wait(io_sock.wait_read, ...) succeeds,
the IO loop stops as expected, but when I try to PQconsumeInput()
from the LIBPQ connection next (as shown in the code below)

```
Notification Connection::notification() {
    auto [lock, hndl] = ensure_handle();
    if (1 != PQconsumeInput(hndl)) {
        throw std::runtime_error(error_msg()); // <<<<<< THROWS
    }
    PGnotify_uptr notif{ PQnotifies(hndl) };
    return Notification(std::move(notif)); // may be null
}
```

that calls fail with this error message:

fatal error: in "NotificationTests/test_across_backends": class
std::runtime_error: could not receive data from server: Socket operation on
non-socket (0x00002736/10038)

LIBPQ didn't like what ASIO somehow did on the socket descriptor.
The LIBPQ doc says to select() the descriptor, but I thought ASIO would be
cleaner,
more C++, and perhaps more cross-platform.

Can somehow help, telling me if I'm using ASIO incorrectly here?

Thanks, --DD


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk