[Capy review] On integrating nonblocking C libraries with corosio

Ruben Perez via Boost <[email protected]> Wed, 24 Jun 2026 17:16:02 +0200
Newsgroups gmane.comp.lib.boost.devel
Message-ID <CACR-mdKN_bKWs8h+YfWYwqu+v-TLt0pr5WDvhEJ_K39LdvLfkA@mail.gmail.com>
Corosio's tcp_socket::wait docs state:

"Wait for the socket to become ready in a given direction. [...]
useful for integrating with C libraries that own the I/O on a
nonblocking fd and only need readiness notification (e.g. libpq async,
libssh)."

I know that Klemens wrote a libssh wrapper [1] using this. Since the
docs mention libpq, I thought I would give it a try.

libssh allows setting a user-supplied socket using ssh_socket_set_fd
[2]. This works great with tcp_socket::wait: you create your corosio
socket and call ssh_socket_set_fd(sock.native_handle()).

libpq gives you a read-only socket handle with PQsocket [3]. This
means that the pattern above does not work here. With Asio, I can
write this:

asio::awaitable<void> co_main()
{
   PGconn* conn = PQconnectStart(
      "host=127.0.0.1 dbname=postgres user=postgres password=secret
sslmode=disable");
   if (!conn) {
      std::cerr << "Failed PQconnectStart\n";
      exit(1);
   }

   struct deleter {
      void operator()(asio::ip::tcp::socket* d) const
      {
         boost::system::error_code ec;
         d->release(ec);
      }
   };

   asio::ip::tcp::socket descr{co_await asio::this_coro::executor};

   bool finished = false;
   while (!finished) {
      switch (PQconnectPoll(conn)) {
         case PGRES_POLLING_READING:
         {
            std::cout << "wait_read\n";
            descr.assign(asio::ip::tcp::v4(), PQsocket(conn));
            std::unique_ptr<asio::ip::tcp::socket, deleter> guard{&descr};
            co_await descr.async_wait(asio::ip::tcp::socket::wait_read);
            break;
         }
         case PGRES_POLLING_WRITING:
         {
            std::cout << "wait_write\n";
            descr.assign(asio::ip::tcp::v4(), PQsocket(conn));
            std::unique_ptr<asio::ip::tcp::socket, deleter> guard{&descr};
            co_await descr.async_wait(asio::ip::tcp::socket::wait_write);
            break;
         }
         case PGRES_POLLING_FAILED:
            std::cerr << "Poll failed\n";
            finished = true;
            break;
         case PGRES_POLLING_OK:
         default:               finished = true; break;
      }
   }

   PQfinish(conn);
   co_return;
}

Which is a bit awful, but does work in Linux, and I think _should_
happen to work on Windows, too. Points to note here:

* PQsocket returns a _non owning_ socket handle. Asio (and corosio)
sockets are owning. We need to take extra steps to avoid the socket
being closed.
* PQsocket might return a different socket at each call. This happens
when trying several servers until one works.
* PQsocket returns an int even in Windows, where SOCKET is unsigned.
If the returned socket is valid, the value on Windows is the actual
SOCKET, cast to an int [4].
* asio::tcp::socket::assign takes a native_handle_type, which is an
int on POSIX, and a class constructible from SOCKET on Windows [5].

The problem I've found with Corosio is that (by design) there is no
tcp_socket::assign (because this takes a native type), so this pattern
doesn't work.

Considering that the docs mention libpq explicitly, what am I missing here?

Note that libmariadb [6] and libmosquitto [7] follow libpq's pattern.

Thanks,
Ruben.


[1] https://github.com/klemens-morgenstern/corosh
[2] https://api.libssh.org/stable/group__libssh__socket.html
[3] https://www.postgresql.org/docs/current/libpq-status.html
[4] https://github.com/postgres/postgres/blob/56b2792cf84fc78f9109b0ae122174e70379ad07/src/interfaces/libpq/fe-connect.c#L7770
[5] https://github.com/boostorg/asio/blob/cba4d9791d77264814eccb53324d5d37669467a3/include/boost/asio/detail/win_iocp_socket_service.hpp#L66
[6] https://mariadb.com/docs/server/reference/product-development/mariadb-internals/using-mariadb-with-your-programs-api/non-blocking-client-library/non-blocking-api-reference
[7] https://mosquitto.org/api2/files/mosquitto-h.html#mosquitto_socket
_______________________________________________
Boost mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://lists.boost.org/mailman3/lists/boost.lists.boost.org/
Archived at: https://lists.boost.org/archives/list/[email protected]/message/OPE4XC5NOKJKERKAKFHG23A5MJGHNGG3/