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/56b2792cf84fc78f9109b0ae122174e703... [5] https://github.com/boostorg/asio/blob/cba4d9791d77264814eccb53324d5d37669467... [6] https://mariadb.com/docs/server/reference/product-development/mariadb-intern... [7] https://mosquitto.org/api2/files/mosquitto-h.html#mosquitto_socket