Fwd: Using ASIO to wait on a foreign socket being readable, with a timeout
Sorry, sent originally to the wrong list.
And this is on Windows 10 too, BTW.
Haven't tried Linux with that code yet. --DD
---------- Forwarded message ---------
From: Dominique Devienne
ASIO will assume ownership of the socket, which means it'll close the socket when it's destructed. If you don't want this, you have to call `release()` on it before destructing. That _might_ help, or it might not, ASIO might do different things to the socket as well. Depending on the platform, it'll use different techniques for waiting on the socket, might be epoll on linux, kqueue on BSD (including mac), etc... In my experience, if it's a pipe, it'll work with epoll on linux, but it might fail on BSD. YMMV. On Thu, 2024-01-18 at 19:44 +0100, Dominique Devienne via Boost-users wrote:
Sorry, sent originally to the wrong list. And this is on Windows 10 too, BTW. Haven't tried Linux with that code yet. --DD
---------- Forwarded message --------- From: Dominique Devienne
Date: Thu, Jan 18, 2024 at 7:18 PM Subject: Using ASIO to wait on a foreign socket being readable, with a timeout To: 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-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
On Thu, Jan 18, 2024 at 8:19 PM Martijn Otto
ASIO will assume ownership of the socket, which means it'll close the socket when it's destructed. If you don't want this, you have to call `release()` on it before destructing.
Thanks Martijn! Works for both outcomes now, and on Windows and Linux. Although the wait times are rather different on Windows and Linux! Linux adds 0.1ms overhead roughly, vs 20x - 100x that on Windows. --DD PS: If anyone knows a better way to achieve the objective I outlined, I'd appreciate. Win64 Debug: Waited 3,184 usec; Waited 107,721 usec Win64 Release: Waited 2,559 usec; Waited 111,448 usec Linux64 Release: Waited 132 usec; Waited 100,096 usec Linux64 Release: Waited 138 usec; Waited 100,101 usec Linux64 Release: Waited 110 usec; Waited 100,094 usec
participants (2)
-
Dominique Devienne
-
Martijn Otto