From 4a81ba6a65fee12cadfb1e75107c346891144b6a Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Thu, 16 Apr 2026 13:00:36 -0600 Subject: [PATCH] Consolidate reactor backends into parameterized templates The epoll, kqueue, and select backends each had 13 nearly-identical per-protocol files for the socket, service, and acceptor types of TCP, UDP, and Unix stream/datagram. Replace those 39 files with shared parameterized templates plus per-backend traits and types files, dropping each backend from 14 files (the 13 plus the scheduler) to 3 (scheduler, traits, types). New per-backend files: - {backend}_traits.hpp: platform-specific behavior (socket creation flags, write and accept policies, fd configuration, stream socket hook for SO_LINGER, descriptor_state) - {backend}_types.hpp: named final classes that instantiate the shared templates New shared templates in detail/reactor/: - reactor_stream_ops.hpp and reactor_datagram_ops.hpp: parameterized op types - reactor_socket_finals.hpp: reactor_stream_socket_impl, reactor_dgram_socket_impl, reactor_acceptor_impl - reactor_service_finals.hpp: per-protocol service implementations plus do_open_socket, do_assign_fd, and do_open_acceptor helpers - reactor_backend.hpp: the accept() method body, which needs all the named types to be complete The existing reactor base templates (reactor_basic_socket, reactor_stream_socket, reactor_datagram_socket, reactor_acceptor) gain ImplBase and Endpoint template parameters so a single template works for both IP and Unix domain sockets. io_context.cpp and the native/ tcp_socket, tcp_acceptor, and udp_socket headers switch to including {backend}_types.hpp in place of the individual service headers. --- .../epoll/epoll_local_datagram_service.hpp | 303 ------------- .../epoll/epoll_local_datagram_socket.hpp | 166 ------- .../epoll/epoll_local_stream_acceptor.hpp | 59 --- .../epoll_local_stream_acceptor_service.hpp | 298 ------------- .../epoll/epoll_local_stream_service.hpp | 222 ---------- .../epoll/epoll_local_stream_socket.hpp | 124 ------ .../corosio/native/detail/epoll/epoll_op.hpp | 143 ------- .../native/detail/epoll/epoll_scheduler.hpp | 13 +- .../detail/epoll/epoll_tcp_acceptor.hpp | 54 --- .../epoll/epoll_tcp_acceptor_service.hpp | 284 ------------ .../native/detail/epoll/epoll_tcp_service.hpp | 250 ----------- .../native/detail/epoll/epoll_tcp_socket.hpp | 77 ---- .../native/detail/epoll/epoll_traits.hpp | 151 +++++++ .../native/detail/epoll/epoll_types.hpp | 258 +++++++++++ .../native/detail/epoll/epoll_udp_service.hpp | 276 ------------ .../native/detail/epoll/epoll_udp_socket.hpp | 140 ------ .../kqueue/kqueue_local_datagram_service.hpp | 339 --------------- .../kqueue/kqueue_local_datagram_socket.hpp | 166 ------- .../kqueue/kqueue_local_stream_acceptor.hpp | 59 --- .../kqueue_local_stream_acceptor_service.hpp | 359 ---------------- .../kqueue/kqueue_local_stream_service.hpp | 256 ----------- .../kqueue/kqueue_local_stream_socket.hpp | 124 ------ .../native/detail/kqueue/kqueue_op.hpp | 192 --------- .../native/detail/kqueue/kqueue_scheduler.hpp | 25 +- .../detail/kqueue/kqueue_tcp_acceptor.hpp | 75 ---- .../kqueue/kqueue_tcp_acceptor_service.hpp | 339 --------------- .../detail/kqueue/kqueue_tcp_service.hpp | 357 ---------------- .../detail/kqueue/kqueue_tcp_socket.hpp | 87 ---- .../native/detail/kqueue/kqueue_traits.hpp | 255 +++++++++++ .../native/detail/kqueue/kqueue_types.hpp | 258 +++++++++++ .../detail/kqueue/kqueue_udp_service.hpp | 303 ------------- .../detail/kqueue/kqueue_udp_socket.hpp | 140 ------ .../detail/reactor/reactor_acceptor.hpp | 23 +- .../reactor/reactor_acceptor_service.hpp | 2 + .../native/detail/reactor/reactor_backend.hpp | 150 +++++++ .../detail/reactor/reactor_basic_socket.hpp | 32 +- .../detail/reactor/reactor_datagram_ops.hpp | 117 +++++ .../reactor/reactor_datagram_socket.hpp | 82 +++- .../detail/reactor/reactor_op_complete.hpp | 31 ++ .../detail/reactor/reactor_service_finals.hpp | 404 ++++++++++++++++++ .../detail/reactor/reactor_socket_finals.hpp | 190 ++++++++ .../detail/reactor/reactor_socket_service.hpp | 2 + .../detail/reactor/reactor_stream_ops.hpp | 111 +++++ .../detail/reactor/reactor_stream_socket.hpp | 63 ++- .../select/select_local_datagram_service.hpp | 348 --------------- .../select/select_local_datagram_socket.hpp | 166 ------- .../select/select_local_stream_acceptor.hpp | 59 --- .../select_local_stream_acceptor_service.hpp | 371 ---------------- .../select/select_local_stream_service.hpp | 274 ------------ .../select/select_local_stream_socket.hpp | 124 ------ .../native/detail/select/select_op.hpp | 193 --------- .../native/detail/select/select_scheduler.hpp | 13 +- .../detail/select/select_tcp_acceptor.hpp | 54 --- .../select/select_tcp_acceptor_service.hpp | 363 ---------------- .../detail/select/select_tcp_service.hpp | 251 ----------- .../detail/select/select_tcp_socket.hpp | 77 ---- .../native/detail/select/select_traits.hpp | 240 +++++++++++ .../native/detail/select/select_types.hpp | 258 +++++++++++ .../detail/select/select_udp_service.hpp | 319 -------------- .../detail/select/select_udp_socket.hpp | 140 ------ .../corosio/native/native_tcp_acceptor.hpp | 6 +- .../corosio/native/native_tcp_socket.hpp | 6 +- .../corosio/native/native_udp_socket.hpp | 6 +- src/corosio/src/io_context.cpp | 24 +- 64 files changed, 2649 insertions(+), 8002 deletions(-) delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_local_datagram_service.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_local_datagram_socket.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_local_stream_acceptor.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_local_stream_acceptor_service.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_local_stream_service.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_local_stream_socket.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_op.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_tcp_acceptor.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_tcp_acceptor_service.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_tcp_service.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_tcp_socket.hpp create mode 100644 include/boost/corosio/native/detail/epoll/epoll_traits.hpp create mode 100644 include/boost/corosio/native/detail/epoll/epoll_types.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_udp_service.hpp delete mode 100644 include/boost/corosio/native/detail/epoll/epoll_udp_socket.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_local_datagram_service.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_local_datagram_socket.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_local_stream_acceptor.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_local_stream_acceptor_service.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_local_stream_service.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_local_stream_socket.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_op.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_tcp_acceptor.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_tcp_acceptor_service.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_tcp_service.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_tcp_socket.hpp create mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_traits.hpp create mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_types.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_udp_service.hpp delete mode 100644 include/boost/corosio/native/detail/kqueue/kqueue_udp_socket.hpp create mode 100644 include/boost/corosio/native/detail/reactor/reactor_backend.hpp create mode 100644 include/boost/corosio/native/detail/reactor/reactor_datagram_ops.hpp create mode 100644 include/boost/corosio/native/detail/reactor/reactor_service_finals.hpp create mode 100644 include/boost/corosio/native/detail/reactor/reactor_socket_finals.hpp create mode 100644 include/boost/corosio/native/detail/reactor/reactor_stream_ops.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_local_datagram_service.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_local_datagram_socket.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_local_stream_acceptor.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_local_stream_acceptor_service.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_local_stream_service.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_local_stream_socket.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_op.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_tcp_acceptor.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_tcp_acceptor_service.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_tcp_service.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_tcp_socket.hpp create mode 100644 include/boost/corosio/native/detail/select/select_traits.hpp create mode 100644 include/boost/corosio/native/detail/select/select_types.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_udp_service.hpp delete mode 100644 include/boost/corosio/native/detail/select/select_udp_socket.hpp diff --git a/include/boost/corosio/native/detail/epoll/epoll_local_datagram_service.hpp b/include/boost/corosio/native/detail/epoll/epoll_local_datagram_service.hpp deleted file mode 100644 index 66e8a306f..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_local_datagram_service.hpp +++ /dev/null @@ -1,303 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_DATAGRAM_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_DATAGRAM_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class BOOST_COROSIO_DECL epoll_local_datagram_service final - : public reactor_socket_service< - epoll_local_datagram_service, - local_datagram_service, - epoll_scheduler, - epoll_local_datagram_socket> -{ -public: - explicit epoll_local_datagram_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - local_datagram_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code assign_socket( - local_datagram_socket::implementation& impl, - native_handle_type fd) override; - - std::error_code bind_socket( - local_datagram_socket::implementation& impl, - corosio::local_endpoint ep) override; -}; - -// Cancellation for connectionless ops - -inline void -epoll_local_send_to_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_local_recv_from_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Cancellation for connected-mode ops - -inline void -epoll_local_dgram_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_local_dgram_send_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_local_dgram_recv_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Completion handlers - -inline void -epoll_local_datagram_op::operator()() -{ - complete_io_op(*this); -} - -inline void -epoll_local_recv_from_op::operator()() -{ - complete_datagram_op(*this, this->source_out); -} - -inline void -epoll_local_dgram_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline void -epoll_local_dgram_recv_op::operator()() -{ - // Datagram completion: do not map 0 bytes to EOF - complete_dgram_recv_op(*this); -} - -// Socket construction/destruction - -inline epoll_local_datagram_socket::epoll_local_datagram_socket( - epoll_local_datagram_service& svc) noexcept - : reactor_datagram_socket(svc) -{ -} - -inline epoll_local_datagram_socket::~epoll_local_datagram_socket() = default; - -// Connectionless I/O - -inline std::coroutine_handle<> -epoll_local_datagram_socket::send_to( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - corosio::local_endpoint dest, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_send_to(h, ex, buf, dest, flags, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -epoll_local_datagram_socket::recv_from( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - corosio::local_endpoint* source, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv_from(h, ex, buf, source, flags, token, ec, bytes_out); -} - -// Connected-mode I/O - -inline std::coroutine_handle<> -epoll_local_datagram_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - corosio::local_endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - return do_connect(h, ex, ep, token, ec); -} - -inline std::coroutine_handle<> -epoll_local_datagram_socket::send( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_send(h, ex, buf, flags, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -epoll_local_datagram_socket::recv( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv(h, ex, buf, flags, token, ec, bytes_out); -} - -inline void -epoll_local_datagram_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -epoll_local_datagram_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -epoll_local_datagram_socket::release_socket() noexcept -{ - return this->do_release_socket(); -} - -// Service implementations - -inline std::error_code -epoll_local_datagram_service::open_socket( - local_datagram_socket::implementation& impl, - int family, - int type, - int protocol) -{ - auto* epoll_impl = static_cast(&impl); - epoll_impl->close_socket(); - - int fd = ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); - if (fd < 0) - return make_err(errno); - - epoll_impl->fd_ = fd; - - epoll_impl->desc_state_.fd = fd; - { - std::lock_guard lock(epoll_impl->desc_state_.mutex); - epoll_impl->desc_state_.read_op = nullptr; - epoll_impl->desc_state_.write_op = nullptr; - epoll_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &epoll_impl->desc_state_); - - return {}; -} - -inline std::error_code -epoll_local_datagram_service::assign_socket( - local_datagram_socket::implementation& impl, - native_handle_type fd) -{ - if (fd < 0) - return make_err(EBADF); - - auto* epoll_impl = static_cast(&impl); - epoll_impl->close_socket(); - - epoll_impl->fd_ = fd; - - epoll_impl->desc_state_.fd = fd; - { - std::lock_guard lock(epoll_impl->desc_state_.mutex); - epoll_impl->desc_state_.read_op = nullptr; - epoll_impl->desc_state_.write_op = nullptr; - epoll_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &epoll_impl->desc_state_); - - return {}; -} - -inline std::error_code -epoll_local_datagram_service::bind_socket( - local_datagram_socket::implementation& impl, - corosio::local_endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_DATAGRAM_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_local_datagram_socket.hpp b/include/boost/corosio/native/detail/epoll/epoll_local_datagram_socket.hpp deleted file mode 100644 index 81d210eb4..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_local_datagram_socket.hpp +++ /dev/null @@ -1,166 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_DATAGRAM_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_DATAGRAM_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -// Forward declarations -class epoll_local_datagram_service; -class epoll_local_datagram_socket; - -/// Base operation for local datagram sockets on epoll. -struct epoll_local_datagram_op - : reactor_op -{ - void operator()() override; -}; - -/// Connect operation for local datagram sockets. -struct epoll_local_dgram_connect_op final - : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Send-to operation for local datagram sockets. -struct epoll_local_send_to_op final - : reactor_send_to_op -{ - void cancel() noexcept override; -}; - -/// Recv-from operation for local datagram sockets. -struct epoll_local_recv_from_op final - : reactor_recv_from_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Connected send operation for local datagram sockets. -struct epoll_local_dgram_send_op final - : reactor_send_op -{ - void cancel() noexcept override; -}; - -/// Connected recv operation for local datagram sockets. -struct epoll_local_dgram_recv_op final - : reactor_recv_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Datagram socket implementation for local (Unix) sockets on epoll. -class epoll_local_datagram_socket final - : public reactor_datagram_socket< - epoll_local_datagram_socket, - epoll_local_datagram_service, - epoll_local_dgram_connect_op, - epoll_local_send_to_op, - epoll_local_recv_from_op, - epoll_local_dgram_send_op, - epoll_local_dgram_recv_op, - descriptor_state, - local_datagram_socket::implementation, - corosio::local_endpoint> -{ - friend class epoll_local_datagram_service; - -public: - explicit epoll_local_datagram_socket( - epoll_local_datagram_service& svc) noexcept; - ~epoll_local_datagram_socket() override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - corosio::local_endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> send_to( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - corosio::local_endpoint, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv_from( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - corosio::local_endpoint*, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> send( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::error_code shutdown( - local_datagram_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - std::error_code bind(corosio::local_endpoint ep) noexcept override - { - return do_bind(ep); - } - - corosio::local_endpoint remote_endpoint() const noexcept override - { - return reactor_datagram_socket::remote_endpoint(); - } - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_DATAGRAM_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_local_stream_acceptor.hpp b/include/boost/corosio/native/detail/epoll/epoll_local_stream_acceptor.hpp deleted file mode 100644 index 0ad27f663..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_local_stream_acceptor.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_ACCEPTOR_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_ACCEPTOR_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class epoll_local_stream_acceptor_service; - -/// Acceptor implementation for local stream sockets on epoll. -class epoll_local_stream_acceptor final - : public reactor_acceptor< - epoll_local_stream_acceptor, - epoll_local_stream_acceptor_service, - epoll_local_stream_op, - epoll_local_accept_op, - descriptor_state, - local_stream_acceptor::implementation, - local_endpoint> -{ - friend class epoll_local_stream_acceptor_service; - -public: - explicit epoll_local_stream_acceptor( - epoll_local_stream_acceptor_service& svc) noexcept; - - std::coroutine_handle<> accept( - std::coroutine_handle<>, - capy::executor_ref, - std::stop_token, - std::error_code*, - io_object::implementation**) override; - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_ACCEPTOR_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_local_stream_acceptor_service.hpp b/include/boost/corosio/native/detail/epoll/epoll_local_stream_acceptor_service.hpp deleted file mode 100644 index ec2b9b629..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_local_stream_acceptor_service.hpp +++ /dev/null @@ -1,298 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/* epoll local stream acceptor service implementation. - - Inherits from local_stream_acceptor_service to enable runtime - polymorphism. Uses key_type = local_stream_acceptor_service - for service lookup. -*/ -class BOOST_COROSIO_DECL epoll_local_stream_acceptor_service final - : public reactor_acceptor_service< - epoll_local_stream_acceptor_service, - local_stream_acceptor_service, - epoll_scheduler, - epoll_local_stream_acceptor, - epoll_local_stream_service> -{ - using base_type = reactor_acceptor_service< - epoll_local_stream_acceptor_service, - local_stream_acceptor_service, - epoll_scheduler, - epoll_local_stream_acceptor, - epoll_local_stream_service>; - friend base_type; - -public: - explicit epoll_local_stream_acceptor_service(capy::execution_context& ctx); - ~epoll_local_stream_acceptor_service() override; - - std::error_code open_acceptor_socket( - local_stream_acceptor::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code bind_acceptor( - local_stream_acceptor::implementation& impl, - corosio::local_endpoint ep) override; - std::error_code listen_acceptor( - local_stream_acceptor::implementation& impl, - int backlog) override; -}; - -inline void -epoll_local_accept_op::cancel() noexcept -{ - if (acceptor_impl_) - acceptor_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_local_accept_op::operator()() -{ - complete_accept_op(*this); -} - -inline epoll_local_stream_acceptor::epoll_local_stream_acceptor( - epoll_local_stream_acceptor_service& svc) noexcept - : reactor_acceptor(svc) -{ -} - -inline std::coroutine_handle<> -epoll_local_stream_acceptor::accept( - std::coroutine_handle<> h, - capy::executor_ref ex, - std::stop_token token, - std::error_code* ec, - io_object::implementation** impl_out) -{ - auto& op = acc_; - op.reset(); - op.h = h; - op.ex = ex; - op.ec_out = ec; - op.impl_out = impl_out; - op.fd = fd_; - op.start(token, this); - - sockaddr_storage peer_storage{}; - socklen_t addrlen; - int accepted; - do - { - addrlen = sizeof(peer_storage); - accepted = ::accept4( - fd_, reinterpret_cast(&peer_storage), &addrlen, - SOCK_NONBLOCK | SOCK_CLOEXEC); - } - while (accepted < 0 && errno == EINTR); - - if (accepted >= 0) - { - { - std::lock_guard lock(desc_state_.mutex); - desc_state_.read_ready = false; - } - - if (svc_.scheduler().try_consume_inline_budget()) - { - auto* socket_svc = svc_.stream_service(); - if (socket_svc) - { - auto& impl = - static_cast( - *socket_svc->construct()); - impl.set_socket(accepted); - - impl.desc_state_.fd = accepted; - { - std::lock_guard lock(impl.desc_state_.mutex); - impl.desc_state_.read_op = nullptr; - impl.desc_state_.write_op = nullptr; - impl.desc_state_.connect_op = nullptr; - } - socket_svc->scheduler().register_descriptor( - accepted, &impl.desc_state_); - - impl.set_endpoints( - local_endpoint_, - from_sockaddr_as(peer_storage, addrlen, corosio::local_endpoint{})); - - if (ec) - *ec = {}; - if (impl_out) - *impl_out = &impl; - } - else - { - ::close(accepted); - if (ec) - *ec = make_err(ENOENT); - if (impl_out) - *impl_out = nullptr; - } - op.cont_op.cont.h = h; - return dispatch_coro(ex, op.cont_op.cont); - } - - op.accepted_fd = accepted; - op.peer_storage = peer_storage; - op.peer_addrlen = addrlen; - op.complete(0, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - op.impl_ptr = shared_from_this(); - svc_.work_started(); - - std::lock_guard lock(desc_state_.mutex); - bool io_done = false; - if (desc_state_.read_ready) - { - desc_state_.read_ready = false; - op.perform_io(); - io_done = (op.errn != EAGAIN && op.errn != EWOULDBLOCK); - if (!io_done) - op.errn = 0; - } - - if (io_done || op.cancelled.load(std::memory_order_acquire)) - { - svc_.post(&op); - svc_.work_finished(); - } - else - { - desc_state_.read_op = &op; - } - return std::noop_coroutine(); - } - - op.complete(errno, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - // completion is always posted to scheduler queue, never inline. - return std::noop_coroutine(); -} - -inline void -epoll_local_stream_acceptor::cancel() noexcept -{ - do_cancel(); -} - -inline void -epoll_local_stream_acceptor::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -epoll_local_stream_acceptor::release_socket() noexcept -{ - return this->do_release_socket(); -} - -inline epoll_local_stream_acceptor_service:: - epoll_local_stream_acceptor_service(capy::execution_context& ctx) - : base_type(ctx) -{ - auto* svc = ctx_.find_service(); - stream_svc_ = svc - ? dynamic_cast(svc) - : nullptr; - assert(stream_svc_ && - "local_stream_service must be registered before acceptor service"); -} - -inline epoll_local_stream_acceptor_service:: - ~epoll_local_stream_acceptor_service() {} - -inline std::error_code -epoll_local_stream_acceptor_service::open_acceptor_socket( - local_stream_acceptor::implementation& impl, - int family, - int type, - int protocol) -{ - auto* epoll_impl = static_cast(&impl); - epoll_impl->close_socket(); - - int fd = ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); - if (fd < 0) - return make_err(errno); - - epoll_impl->fd_ = fd; - - // Set up descriptor state but do NOT register with epoll yet - epoll_impl->desc_state_.fd = fd; - { - std::lock_guard lock(epoll_impl->desc_state_.mutex); - epoll_impl->desc_state_.read_op = nullptr; - } - - return {}; -} - -inline std::error_code -epoll_local_stream_acceptor_service::bind_acceptor( - local_stream_acceptor::implementation& impl, corosio::local_endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -inline std::error_code -epoll_local_stream_acceptor_service::listen_acceptor( - local_stream_acceptor::implementation& impl, int backlog) -{ - return static_cast(&impl)->do_listen(backlog); -} - - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_local_stream_service.hpp b/include/boost/corosio/native/detail/epoll/epoll_local_stream_service.hpp deleted file mode 100644 index efd0b271e..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_local_stream_service.hpp +++ /dev/null @@ -1,222 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class BOOST_COROSIO_DECL epoll_local_stream_service final - : public reactor_socket_service< - epoll_local_stream_service, - local_stream_service, - epoll_scheduler, - epoll_local_stream_socket> -{ -public: - explicit epoll_local_stream_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - local_stream_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code assign_socket( - local_stream_socket::implementation& impl, - native_handle_type fd) override; -}; - -// Op implementations - -inline void -epoll_local_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_local_read_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_local_write_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_local_stream_op::operator()() -{ - complete_io_op(*this); -} - -inline void -epoll_local_connect_op::operator()() -{ - complete_connect_op(*this); -} - -// Socket implementations - -inline epoll_local_stream_socket::epoll_local_stream_socket( - epoll_local_stream_service& svc) noexcept - : reactor_stream_socket(svc) -{ -} - -inline epoll_local_stream_socket::~epoll_local_stream_socket() = default; - -inline std::coroutine_handle<> -epoll_local_stream_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - corosio::local_endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - return do_connect(h, ex, ep, token, ec); -} - -inline std::coroutine_handle<> -epoll_local_stream_socket::read_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_read_some(h, ex, param, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -epoll_local_stream_socket::write_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_write_some(h, ex, param, token, ec, bytes_out); -} - -inline void -epoll_local_stream_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -epoll_local_stream_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -epoll_local_stream_socket::release_socket() noexcept -{ - return this->do_release_socket(); -} - -// Service implementations - -inline std::error_code -epoll_local_stream_service::open_socket( - local_stream_socket::implementation& impl, - int family, - int type, - int protocol) -{ - auto* epoll_impl = static_cast(&impl); - epoll_impl->close_socket(); - - int fd = ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); - if (fd < 0) - return make_err(errno); - - epoll_impl->fd_ = fd; - - epoll_impl->desc_state_.fd = fd; - { - std::lock_guard lock(epoll_impl->desc_state_.mutex); - epoll_impl->desc_state_.read_op = nullptr; - epoll_impl->desc_state_.write_op = nullptr; - epoll_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &epoll_impl->desc_state_); - - return {}; -} - -inline std::error_code -epoll_local_stream_service::assign_socket( - local_stream_socket::implementation& impl, - native_handle_type fd) -{ - if (fd < 0) - return make_err(EBADF); - - auto* epoll_impl = static_cast(&impl); - epoll_impl->close_socket(); - - epoll_impl->fd_ = fd; - - epoll_impl->desc_state_.fd = fd; - { - std::lock_guard lock(epoll_impl->desc_state_.mutex); - epoll_impl->desc_state_.read_op = nullptr; - epoll_impl->desc_state_.write_op = nullptr; - epoll_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &epoll_impl->desc_state_); - - return {}; -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_local_stream_socket.hpp b/include/boost/corosio/native/detail/epoll/epoll_local_stream_socket.hpp deleted file mode 100644 index 0027961c6..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_local_stream_socket.hpp +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -// Forward declarations -class epoll_local_stream_service; -class epoll_local_stream_socket; -class epoll_local_stream_acceptor; - -/// Base operation for local stream sockets on epoll. -struct epoll_local_stream_op - : reactor_op -{ - void operator()() override; -}; - -/// Connect operation for local stream sockets. -struct epoll_local_connect_op final - : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Scatter-read operation for local stream sockets. -struct epoll_local_read_op final : reactor_read_op -{ - void cancel() noexcept override; -}; - -/// Gather-write operation for local stream sockets. -struct epoll_local_write_op final - : reactor_write_op -{ - void cancel() noexcept override; -}; - -/// Accept operation for local stream sockets. -struct epoll_local_accept_op final - : reactor_accept_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Stream socket implementation for local (Unix) sockets on epoll. -class epoll_local_stream_socket final - : public reactor_stream_socket< - epoll_local_stream_socket, - epoll_local_stream_service, - epoll_local_connect_op, - epoll_local_read_op, - epoll_local_write_op, - descriptor_state, - local_stream_socket::implementation, - corosio::local_endpoint> -{ - friend class epoll_local_stream_service; - -public: - explicit epoll_local_stream_socket( - epoll_local_stream_service& svc) noexcept; - ~epoll_local_stream_socket() override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - corosio::local_endpoint, - std::stop_token, - std::error_code*) override; - - std::error_code shutdown( - local_stream_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - std::coroutine_handle<> read_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> write_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_LOCAL_STREAM_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_op.hpp b/include/boost/corosio/native/detail/epoll/epoll_op.hpp deleted file mode 100644 index ec7a1911d..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_op.hpp +++ /dev/null @@ -1,143 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_OP_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_OP_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include - -/* - epoll Operation State - ===================== - - Each async I/O operation has a corresponding epoll_op-derived struct that - holds the operation's state while it's in flight. The socket impl owns - fixed slots for each operation type (conn_, rd_, wr_), so only one - operation of each type can be pending per socket at a time. - - Persistent Registration - ----------------------- - File descriptors are registered with epoll once (via descriptor_state) and - stay registered until closed. The descriptor_state tracks which operations - are pending (read_op, write_op, connect_op). When an event arrives, the - reactor dispatches to the appropriate pending operation. - - Impl Lifetime Management - ------------------------ - When cancel() posts an op to the scheduler's ready queue, the socket impl - might be destroyed before the scheduler processes the op. The `impl_ptr` - member holds a shared_ptr to the impl, keeping it alive until the op - completes. This is set by cancel() and cleared in operator() after the - coroutine is resumed. - - EOF Detection - ------------- - For reads, 0 bytes with no error means EOF. But an empty user buffer also - returns 0 bytes. The `empty_buffer_read` flag distinguishes these cases. - - SIGPIPE Prevention - ------------------ - Writes use sendmsg() with MSG_NOSIGNAL instead of writev() to prevent - SIGPIPE when the peer has closed. -*/ - -namespace boost::corosio::detail { - -// Forward declarations -class epoll_tcp_socket; -class epoll_tcp_acceptor; -struct epoll_op; - -// Forward declaration -class epoll_scheduler; - -/// Per-descriptor state for persistent epoll registration. -struct descriptor_state final : reactor_descriptor_state -{}; - -/// epoll base operation — thin wrapper over reactor_op. -struct epoll_op : reactor_op -{ - void operator()() override; -}; - -/// epoll connect operation. -struct epoll_connect_op final : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// epoll scatter-read operation. -struct epoll_read_op final : reactor_read_op -{ - void cancel() noexcept override; -}; - -/** Provides sendmsg(MSG_NOSIGNAL) with EINTR retry for epoll writes. */ -struct epoll_write_policy -{ - static ssize_t write(int fd, iovec* iovecs, int count) noexcept - { - msghdr msg{}; - msg.msg_iov = iovecs; - msg.msg_iovlen = static_cast(count); - - ssize_t n; - do - { - n = ::sendmsg(fd, &msg, MSG_NOSIGNAL); - } - while (n < 0 && errno == EINTR); - return n; - } -}; - -/// epoll gather-write operation. -struct epoll_write_op final : reactor_write_op -{ - void cancel() noexcept override; -}; - -/** Provides accept4(SOCK_NONBLOCK|SOCK_CLOEXEC) with EINTR retry. */ -struct epoll_accept_policy -{ - static int do_accept( - int fd, sockaddr_storage& peer, socklen_t& addrlen_out) noexcept - { - addrlen_out = sizeof(peer); - int new_fd; - do - { - new_fd = ::accept4( - fd, reinterpret_cast(&peer), &addrlen_out, - SOCK_NONBLOCK | SOCK_CLOEXEC); - } - while (new_fd < 0 && errno == EINTR); - return new_fd; - } -}; - -/// epoll accept operation. -struct epoll_accept_op final : reactor_accept_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_OP_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_scheduler.hpp b/include/boost/corosio/native/detail/epoll/epoll_scheduler.hpp index 857c8982b..aed650031 100644 --- a/include/boost/corosio/native/detail/epoll/epoll_scheduler.hpp +++ b/include/boost/corosio/native/detail/epoll/epoll_scheduler.hpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include #include @@ -43,9 +43,6 @@ namespace boost::corosio::detail { -struct epoll_op; -struct descriptor_state; - /** Linux scheduler using epoll for I/O multiplexing. This scheduler implements the scheduler interface using Linux epoll @@ -109,13 +106,13 @@ class BOOST_COROSIO_DECL epoll_scheduler final : public reactor_scheduler /** Register a descriptor for persistent monitoring. The fd is registered once and stays registered until explicitly - deregistered. Events are dispatched via descriptor_state which + deregistered. Events are dispatched via reactor_descriptor_state which tracks pending read/write/connect operations. @param fd The file descriptor to register. @param desc Pointer to descriptor data (stored in epoll_event.data.ptr). */ - void register_descriptor(int fd, descriptor_state* desc) const; + void register_descriptor(int fd, reactor_descriptor_state* desc) const; /** Deregister a persistently registered descriptor. @@ -244,7 +241,7 @@ epoll_scheduler::configure_reactor( } inline void -epoll_scheduler::register_descriptor(int fd, descriptor_state* desc) const +epoll_scheduler::register_descriptor(int fd, reactor_descriptor_state* desc) const { epoll_event ev{}; ev.events = EPOLLIN | EPOLLOUT | EPOLLET | EPOLLERR | EPOLLHUP; @@ -373,7 +370,7 @@ epoll_scheduler::run_task( } auto* desc = - static_cast(event_buffer_[i].data.ptr); + static_cast(event_buffer_[i].data.ptr); desc->add_ready_events(event_buffer_[i].events); bool expected = false; diff --git a/include/boost/corosio/native/detail/epoll/epoll_tcp_acceptor.hpp b/include/boost/corosio/native/detail/epoll/epoll_tcp_acceptor.hpp deleted file mode 100644 index e5ec50108..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_tcp_acceptor.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_ACCEPTOR_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_ACCEPTOR_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include -#include - -namespace boost::corosio::detail { - -class epoll_tcp_acceptor_service; - -/// Acceptor implementation for epoll backend. -class epoll_tcp_acceptor final - : public reactor_acceptor< - epoll_tcp_acceptor, - epoll_tcp_acceptor_service, - epoll_op, - epoll_accept_op, - descriptor_state> -{ - friend class epoll_tcp_acceptor_service; - -public: - explicit epoll_tcp_acceptor(epoll_tcp_acceptor_service& svc) noexcept; - - std::coroutine_handle<> accept( - std::coroutine_handle<>, - capy::executor_ref, - std::stop_token, - std::error_code*, - io_object::implementation**) override; - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_ACCEPTOR_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_tcp_acceptor_service.hpp b/include/boost/corosio/native/detail/epoll/epoll_tcp_acceptor_service.hpp deleted file mode 100644 index 1878b457f..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_tcp_acceptor_service.hpp +++ /dev/null @@ -1,284 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_ACCEPTOR_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_ACCEPTOR_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/** epoll acceptor service implementation. - - Derives from reactor_acceptor_service for shared construct/ - destroy/shutdown/close logic. Provides epoll-specific socket - creation (SOCK_NONBLOCK | SOCK_CLOEXEC) and dual-stack defaults. - Uses key_type = tcp_acceptor_service for service lookup. -*/ -class BOOST_COROSIO_DECL epoll_tcp_acceptor_service final - : public reactor_acceptor_service< - epoll_tcp_acceptor_service, - tcp_acceptor_service, - epoll_scheduler, - epoll_tcp_acceptor, - epoll_tcp_service> -{ - using base_type = reactor_acceptor_service< - epoll_tcp_acceptor_service, - tcp_acceptor_service, - epoll_scheduler, - epoll_tcp_acceptor, - epoll_tcp_service>; - friend base_type; - -public: - explicit epoll_tcp_acceptor_service(capy::execution_context& ctx); - ~epoll_tcp_acceptor_service() override; - - std::error_code open_acceptor_socket( - tcp_acceptor::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code - bind_acceptor(tcp_acceptor::implementation& impl, endpoint ep) override; - std::error_code - listen_acceptor(tcp_acceptor::implementation& impl, int backlog) override; -}; - -inline void -epoll_accept_op::cancel() noexcept -{ - if (acceptor_impl_) - acceptor_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_accept_op::operator()() -{ - complete_accept_op(*this); -} - -inline epoll_tcp_acceptor::epoll_tcp_acceptor( - epoll_tcp_acceptor_service& svc) noexcept - : reactor_acceptor(svc) -{ -} - -inline std::coroutine_handle<> -epoll_tcp_acceptor::accept( - std::coroutine_handle<> h, - capy::executor_ref ex, - std::stop_token token, - std::error_code* ec, - io_object::implementation** impl_out) -{ - auto& op = acc_; - op.reset(); - op.h = h; - op.ex = ex; - op.ec_out = ec; - op.impl_out = impl_out; - op.fd = fd_; - op.start(token, this); - - sockaddr_storage peer_storage{}; - socklen_t addrlen; - int accepted; - do - { - addrlen = sizeof(peer_storage); - accepted = ::accept4( - fd_, reinterpret_cast(&peer_storage), &addrlen, - SOCK_NONBLOCK | SOCK_CLOEXEC); - } - while (accepted < 0 && errno == EINTR); - - if (accepted >= 0) - { - { - std::lock_guard lock(desc_state_.mutex); - desc_state_.read_ready = false; - } - - if (svc_.scheduler().try_consume_inline_budget()) - { - auto* socket_svc = svc_.stream_service(); - if (socket_svc) - { - auto& impl = - static_cast(*socket_svc->construct()); - impl.set_socket(accepted); - - impl.desc_state_.fd = accepted; - { - std::lock_guard lock(impl.desc_state_.mutex); - impl.desc_state_.read_op = nullptr; - impl.desc_state_.write_op = nullptr; - impl.desc_state_.connect_op = nullptr; - } - socket_svc->scheduler().register_descriptor( - accepted, &impl.desc_state_); - - impl.set_endpoints( - local_endpoint_, from_sockaddr(peer_storage)); - - *ec = {}; - if (impl_out) - *impl_out = &impl; - } - else - { - ::close(accepted); - *ec = make_err(ENOENT); - if (impl_out) - *impl_out = nullptr; - } - op.cont_op.cont.h = h; - return dispatch_coro(ex, op.cont_op.cont); - } - - op.accepted_fd = accepted; - op.peer_storage = peer_storage; - op.peer_addrlen = addrlen; - op.complete(0, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - op.impl_ptr = shared_from_this(); - svc_.work_started(); - - std::lock_guard lock(desc_state_.mutex); - bool io_done = false; - if (desc_state_.read_ready) - { - desc_state_.read_ready = false; - op.perform_io(); - io_done = (op.errn != EAGAIN && op.errn != EWOULDBLOCK); - if (!io_done) - op.errn = 0; - } - - if (io_done || op.cancelled.load(std::memory_order_acquire)) - { - svc_.post(&op); - svc_.work_finished(); - } - else - { - desc_state_.read_op = &op; - } - return std::noop_coroutine(); - } - - op.complete(errno, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - // completion is always posted to scheduler queue, never inline. - return std::noop_coroutine(); -} - -inline void -epoll_tcp_acceptor::cancel() noexcept -{ - do_cancel(); -} - -inline void -epoll_tcp_acceptor::close_socket() noexcept -{ - do_close_socket(); -} - -inline epoll_tcp_acceptor_service::epoll_tcp_acceptor_service( - capy::execution_context& ctx) - : base_type(ctx) -{ - auto* svc = ctx_.find_service(); - stream_svc_ = svc ? dynamic_cast(svc) : nullptr; -} - -inline epoll_tcp_acceptor_service::~epoll_tcp_acceptor_service() {} - -inline std::error_code -epoll_tcp_acceptor_service::open_acceptor_socket( - tcp_acceptor::implementation& impl, int family, int type, int protocol) -{ - auto* epoll_impl = static_cast(&impl); - epoll_impl->close_socket(); - - int fd = ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); - if (fd < 0) - return make_err(errno); - - if (family == AF_INET6) - { - int val = 0; // dual-stack default - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); - } - - epoll_impl->fd_ = fd; - - // Set up descriptor state but do NOT register with epoll yet - epoll_impl->desc_state_.fd = fd; - { - std::lock_guard lock(epoll_impl->desc_state_.mutex); - epoll_impl->desc_state_.read_op = nullptr; - } - - return {}; -} - -inline std::error_code -epoll_tcp_acceptor_service::bind_acceptor( - tcp_acceptor::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -inline std::error_code -epoll_tcp_acceptor_service::listen_acceptor( - tcp_acceptor::implementation& impl, int backlog) -{ - return static_cast(&impl)->do_listen(backlog); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_ACCEPTOR_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_tcp_service.hpp b/include/boost/corosio/native/detail/epoll/epoll_tcp_service.hpp deleted file mode 100644 index 723ec2e0e..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_tcp_service.hpp +++ /dev/null @@ -1,250 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -/* - epoll Socket Implementation - =========================== - - Each I/O operation follows the same pattern: - 1. Try the syscall immediately (non-blocking socket) - 2. If it succeeds or fails with a real error, post to completion queue - 3. If EAGAIN/EWOULDBLOCK, register with epoll and wait - - This "try first" approach avoids unnecessary epoll round-trips for - operations that can complete immediately (common for small reads/writes - on fast local connections). - - One-Shot Registration - --------------------- - We use one-shot epoll registration: each operation registers, waits for - one event, then unregisters. This simplifies the state machine since we - don't need to track whether an fd is currently registered or handle - re-arming. The tradeoff is slightly more epoll_ctl calls, but the - simplicity is worth it. - - Cancellation - ------------ - See op.hpp for the completion/cancellation race handling via the - `registered` atomic. cancel() must complete pending operations (post - them with cancelled flag) so coroutines waiting on them can resume. - close_socket() calls cancel() first to ensure this. - - Impl Lifetime with shared_ptr - ----------------------------- - Socket impls use enable_shared_from_this. The service owns impls via - shared_ptr maps (impl_ptrs_) keyed by raw pointer for O(1) lookup and - removal. When a user calls close(), we call cancel() which posts pending - ops to the scheduler. - - CRITICAL: The posted ops must keep the impl alive until they complete. - Otherwise the scheduler would process a freed op (use-after-free). The - cancel() method captures shared_from_this() into op.impl_ptr before - posting. When the op completes, impl_ptr is cleared, allowing the impl - to be destroyed if no other references exist. - - Service Ownership - ----------------- - epoll_tcp_service owns all socket impls. destroy_impl() removes the - shared_ptr from the map, but the impl may survive if ops still hold - impl_ptr refs. shutdown() closes all sockets and clears the map; any - in-flight ops will complete and release their refs. -*/ - -namespace boost::corosio::detail { - -/** epoll TCP service implementation. - - Inherits from tcp_service to enable runtime polymorphism. - Uses key_type = tcp_service for service lookup. -*/ -class BOOST_COROSIO_DECL epoll_tcp_service final - : public reactor_socket_service< - epoll_tcp_service, - tcp_service, - epoll_scheduler, - epoll_tcp_socket> -{ -public: - explicit epoll_tcp_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - tcp_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code - bind_socket(tcp_socket::implementation& impl, endpoint ep) override; -}; - -inline void -epoll_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_read_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_write_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_op::operator()() -{ - complete_io_op(*this); -} - -inline void -epoll_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline epoll_tcp_socket::epoll_tcp_socket(epoll_tcp_service& svc) noexcept - : reactor_stream_socket(svc) -{ -} - -inline epoll_tcp_socket::~epoll_tcp_socket() = default; - -inline std::coroutine_handle<> -epoll_tcp_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - return do_connect(h, ex, ep, token, ec); -} - -inline std::coroutine_handle<> -epoll_tcp_socket::read_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_read_some(h, ex, param, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -epoll_tcp_socket::write_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_write_some(h, ex, param, token, ec, bytes_out); -} - -inline void -epoll_tcp_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -epoll_tcp_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline std::error_code -epoll_tcp_service::open_socket( - tcp_socket::implementation& impl, int family, int type, int protocol) -{ - auto* epoll_impl = static_cast(&impl); - epoll_impl->close_socket(); - - int fd = ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); - if (fd < 0) - return make_err(errno); - - if (family == AF_INET6) - { - int one = 1; - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - } - - epoll_impl->fd_ = fd; - - // Register fd with epoll (edge-triggered mode) - epoll_impl->desc_state_.fd = fd; - { - std::lock_guard lock(epoll_impl->desc_state_.mutex); - epoll_impl->desc_state_.read_op = nullptr; - epoll_impl->desc_state_.write_op = nullptr; - epoll_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &epoll_impl->desc_state_); - - return {}; -} - -inline std::error_code -epoll_tcp_service::bind_socket( - tcp_socket::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_tcp_socket.hpp b/include/boost/corosio/native/detail/epoll/epoll_tcp_socket.hpp deleted file mode 100644 index 9a60ffdc5..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_tcp_socket.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include -#include - -namespace boost::corosio::detail { - -class epoll_tcp_service; - -/// Stream socket implementation for epoll backend. -class epoll_tcp_socket final - : public reactor_stream_socket< - epoll_tcp_socket, - epoll_tcp_service, - epoll_connect_op, - epoll_read_op, - epoll_write_op, - descriptor_state> -{ - friend class epoll_tcp_service; - -public: - explicit epoll_tcp_socket(epoll_tcp_service& svc) noexcept; - ~epoll_tcp_socket() override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> read_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> write_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::error_code shutdown(tcp_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TCP_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_traits.hpp b/include/boost/corosio/native/detail/epoll/epoll_traits.hpp new file mode 100644 index 000000000..8938abfcc --- /dev/null +++ b/include/boost/corosio/native/detail/epoll/epoll_traits.hpp @@ -0,0 +1,151 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP + +#include + +#if BOOST_COROSIO_HAS_EPOLL + +#include +#include + +#include + +#include +#include +#include + +/* epoll backend traits. + + Captures the platform-specific behavior of the Linux epoll backend: + atomic SOCK_NONBLOCK|SOCK_CLOEXEC on socket(), accept4() for + accepted connections, and sendmsg(MSG_NOSIGNAL) for writes. +*/ + +namespace boost::corosio::detail { + +class epoll_scheduler; + +struct epoll_traits +{ + using scheduler_type = epoll_scheduler; + using desc_state_type = reactor_descriptor_state; + + static constexpr bool needs_write_notification = false; + + // No extra per-socket state or lifecycle hooks needed for epoll. + struct stream_socket_hook + { + std::error_code on_set_option( + int fd, int level, int optname, + void const* data, std::size_t size) noexcept + { + if (::setsockopt( + fd, level, optname, data, + static_cast(size)) != 0) + return make_err(errno); + return {}; + } + static void pre_shutdown(int) noexcept {} + static void pre_destroy(int) noexcept {} + }; + + struct write_policy + { + static ssize_t write(int fd, iovec* iovecs, int count) noexcept + { + msghdr msg{}; + msg.msg_iov = iovecs; + msg.msg_iovlen = static_cast(count); + + ssize_t n; + do + { + n = ::sendmsg(fd, &msg, MSG_NOSIGNAL); + } + while (n < 0 && errno == EINTR); + return n; + } + }; + + struct accept_policy + { + static int do_accept( + int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept + { + addrlen = sizeof(peer); + int new_fd; + do + { + new_fd = ::accept4( + fd, reinterpret_cast(&peer), &addrlen, + SOCK_NONBLOCK | SOCK_CLOEXEC); + } + while (new_fd < 0 && errno == EINTR); + return new_fd; + } + }; + + // Create a nonblocking, close-on-exec socket using Linux's atomic flags. + static int create_socket(int family, int type, int protocol) noexcept + { + return ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); + } + + // Apply protocol-specific options after socket creation. + // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort). + static std::error_code + configure_ip_socket(int fd, int family) noexcept + { + if (family == AF_INET6) + { + int one = 1; + (void)::setsockopt( + fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + } + return {}; + } + + // Apply protocol-specific options for acceptor sockets. + // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort). + static std::error_code + configure_ip_acceptor(int fd, int family) noexcept + { + if (family == AF_INET6) + { + int val = 0; + (void)::setsockopt( + fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); + } + return {}; + } + + // No extra configuration needed for local (unix) sockets on epoll. + static std::error_code + configure_local_socket(int /*fd*/) noexcept + { + return {}; + } + + // Non-mutating validation for fds adopted via assign(). Used when + // the caller retains fd ownership responsibility. + static std::error_code + validate_assigned_fd(int /*fd*/) noexcept + { + return {}; + } +}; + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_HAS_EPOLL + +#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_types.hpp b/include/boost/corosio/native/detail/epoll/epoll_types.hpp new file mode 100644 index 000000000..d94f502b1 --- /dev/null +++ b/include/boost/corosio/native/detail/epoll/epoll_types.hpp @@ -0,0 +1,258 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TYPES_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TYPES_HPP + +#include + +#if BOOST_COROSIO_HAS_EPOLL + +/* Named per-backend types for the epoll reactor. + + Each class is a final, named wrapper around the parameterized + reactor_*_impl templates. Forward-declarable from backend.hpp + so the concrete layer never pulls in platform headers. +*/ + +#include +#include +#include +#include + +namespace boost::corosio::detail { + +// Forward declarations for cross-references. +class epoll_tcp_socket; +class epoll_tcp_service; +class epoll_tcp_acceptor; +class epoll_tcp_acceptor_service; +class epoll_udp_socket; +class epoll_udp_service; +class epoll_local_stream_socket; +class epoll_local_stream_service; +class epoll_local_stream_acceptor; +class epoll_local_stream_acceptor_service; +class epoll_local_datagram_socket; +class epoll_local_datagram_service; + +// --- Stream sockets --- + +class epoll_tcp_socket final + : public reactor_stream_socket_impl< + epoll_tcp_socket, epoll_traits, epoll_tcp_service, + epoll_tcp_acceptor, tcp_socket::implementation, endpoint> +{ + using base_type = reactor_stream_socket_impl< + epoll_tcp_socket, epoll_traits, epoll_tcp_service, + epoll_tcp_acceptor, tcp_socket::implementation, endpoint>; + friend epoll_tcp_service; +public: + explicit epoll_tcp_socket(epoll_tcp_service& svc) noexcept + : base_type(svc) {} +}; + +class epoll_local_stream_socket final + : public reactor_stream_socket_impl< + epoll_local_stream_socket, epoll_traits, + epoll_local_stream_service, epoll_local_stream_acceptor, + local_stream_socket::implementation, corosio::local_endpoint> +{ + using base_type = reactor_stream_socket_impl< + epoll_local_stream_socket, epoll_traits, + epoll_local_stream_service, epoll_local_stream_acceptor, + local_stream_socket::implementation, corosio::local_endpoint>; + friend epoll_local_stream_service; +public: + explicit epoll_local_stream_socket(epoll_local_stream_service& svc) noexcept + : base_type(svc) {} + + native_handle_type release_socket() noexcept override + { + hook_ = {}; + return this->do_release_socket(); + } +}; + +// --- Datagram sockets --- + +class epoll_udp_socket final + : public reactor_dgram_socket_impl< + epoll_udp_socket, epoll_traits, epoll_udp_service, + epoll_tcp_acceptor, udp_socket::implementation, endpoint> +{ + using base_type = reactor_dgram_socket_impl< + epoll_udp_socket, epoll_traits, epoll_udp_service, + epoll_tcp_acceptor, udp_socket::implementation, endpoint>; + friend epoll_udp_service; +public: + explicit epoll_udp_socket(epoll_udp_service& svc) noexcept + : base_type(svc) {} +}; + +class epoll_local_datagram_socket final + : public reactor_dgram_socket_impl< + epoll_local_datagram_socket, epoll_traits, + epoll_local_datagram_service, epoll_tcp_acceptor, + local_datagram_socket::implementation, corosio::local_endpoint> +{ + using base_type = reactor_dgram_socket_impl< + epoll_local_datagram_socket, epoll_traits, + epoll_local_datagram_service, epoll_tcp_acceptor, + local_datagram_socket::implementation, corosio::local_endpoint>; + friend epoll_local_datagram_service; +public: + explicit epoll_local_datagram_socket(epoll_local_datagram_service& svc) noexcept + : base_type(svc) {} + + std::error_code shutdown(corosio::shutdown_type what) noexcept override + { + return this->do_shutdown(static_cast(what)); + } + + std::error_code bind(corosio::local_endpoint ep) noexcept override + { + return this->do_bind(ep); + } + + native_handle_type release_socket() noexcept override + { + return this->do_release_socket(); + } +}; + +// --- Acceptors --- + +class epoll_tcp_acceptor final + : public reactor_acceptor_impl< + epoll_tcp_acceptor, epoll_traits, + epoll_tcp_acceptor_service, epoll_tcp_socket, + tcp_acceptor::implementation, endpoint> +{ + using base_type = reactor_acceptor_impl< + epoll_tcp_acceptor, epoll_traits, + epoll_tcp_acceptor_service, epoll_tcp_socket, + tcp_acceptor::implementation, endpoint>; + friend epoll_tcp_acceptor_service; +public: + explicit epoll_tcp_acceptor(epoll_tcp_acceptor_service& svc) noexcept + : base_type(svc) {} +}; + +class epoll_local_stream_acceptor final + : public reactor_acceptor_impl< + epoll_local_stream_acceptor, epoll_traits, + epoll_local_stream_acceptor_service, + epoll_local_stream_socket, + local_stream_acceptor::implementation, corosio::local_endpoint> +{ + using base_type = reactor_acceptor_impl< + epoll_local_stream_acceptor, epoll_traits, + epoll_local_stream_acceptor_service, + epoll_local_stream_socket, + local_stream_acceptor::implementation, corosio::local_endpoint>; + friend epoll_local_stream_acceptor_service; +public: + explicit epoll_local_stream_acceptor( + epoll_local_stream_acceptor_service& svc) noexcept + : base_type(svc) {} + + native_handle_type release_socket() noexcept override + { + return this->do_release_socket(); + } +}; + +// --- Services --- + +class BOOST_COROSIO_DECL epoll_tcp_service final + : public reactor_tcp_service_impl< + epoll_tcp_service, epoll_traits, epoll_tcp_socket> +{ + using base_type = reactor_tcp_service_impl< + epoll_tcp_service, epoll_traits, epoll_tcp_socket>; +public: + explicit epoll_tcp_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL epoll_local_stream_service final + : public reactor_local_stream_service_impl< + epoll_local_stream_service, epoll_traits, + epoll_local_stream_socket> +{ + using base_type = reactor_local_stream_service_impl< + epoll_local_stream_service, epoll_traits, + epoll_local_stream_socket>; +public: + explicit epoll_local_stream_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL epoll_udp_service final + : public reactor_udp_service_impl< + epoll_udp_service, epoll_traits, epoll_udp_socket> +{ + using base_type = reactor_udp_service_impl< + epoll_udp_service, epoll_traits, epoll_udp_socket>; +public: + explicit epoll_udp_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL epoll_local_datagram_service final + : public reactor_local_dgram_service_impl< + epoll_local_datagram_service, epoll_traits, + epoll_local_datagram_socket> +{ + using base_type = reactor_local_dgram_service_impl< + epoll_local_datagram_service, epoll_traits, + epoll_local_datagram_socket>; +public: + explicit epoll_local_datagram_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL epoll_tcp_acceptor_service final + : public reactor_acceptor_service_impl< + epoll_tcp_acceptor_service, epoll_traits, + tcp_acceptor_service, epoll_tcp_acceptor, + epoll_tcp_service, endpoint> +{ + using base_type = reactor_acceptor_service_impl< + epoll_tcp_acceptor_service, epoll_traits, + tcp_acceptor_service, epoll_tcp_acceptor, + epoll_tcp_service, endpoint>; +public: + explicit epoll_tcp_acceptor_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL epoll_local_stream_acceptor_service final + : public reactor_acceptor_service_impl< + epoll_local_stream_acceptor_service, epoll_traits, + local_stream_acceptor_service, + epoll_local_stream_acceptor, + epoll_local_stream_service, corosio::local_endpoint> +{ + using base_type = reactor_acceptor_service_impl< + epoll_local_stream_acceptor_service, epoll_traits, + local_stream_acceptor_service, + epoll_local_stream_acceptor, + epoll_local_stream_service, corosio::local_endpoint>; +public: + explicit epoll_local_stream_acceptor_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_HAS_EPOLL + +#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TYPES_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_udp_service.hpp b/include/boost/corosio/native/detail/epoll/epoll_udp_service.hpp deleted file mode 100644 index ce2e137ad..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_udp_service.hpp +++ /dev/null @@ -1,276 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/** epoll UDP service implementation. - - Inherits from udp_service to enable runtime polymorphism. - Uses key_type = udp_service for service lookup. -*/ -class BOOST_COROSIO_DECL epoll_udp_service final - : public reactor_socket_service< - epoll_udp_service, - udp_service, - epoll_scheduler, - epoll_udp_socket> -{ -public: - explicit epoll_udp_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_datagram_socket( - udp_socket::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code - bind_datagram(udp_socket::implementation& impl, endpoint ep) override; -}; - -// Cancellation for connectionless ops - -inline void -epoll_send_to_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_recv_from_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Cancellation for connected-mode ops - -inline void -epoll_udp_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_send_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -epoll_recv_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Completion handlers - -inline void -epoll_datagram_op::operator()() -{ - complete_io_op(*this); -} - -inline void -epoll_recv_from_op::operator()() -{ - complete_datagram_op(*this, this->source_out); -} - -inline void -epoll_udp_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline void -epoll_recv_op::operator()() -{ - complete_io_op(*this); -} - -// Socket construction/destruction - -inline epoll_udp_socket::epoll_udp_socket(epoll_udp_service& svc) noexcept - : reactor_datagram_socket(svc) -{ -} - -inline epoll_udp_socket::~epoll_udp_socket() = default; - -// Connectionless I/O - -inline std::coroutine_handle<> -epoll_udp_socket::send_to( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - endpoint dest, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_send_to(h, ex, buf, dest, flags, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -epoll_udp_socket::recv_from( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - endpoint* source, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv_from(h, ex, buf, source, flags, token, ec, bytes_out); -} - -// Connected-mode I/O - -inline std::coroutine_handle<> -epoll_udp_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - return do_connect(h, ex, ep, token, ec); -} - -inline std::coroutine_handle<> -epoll_udp_socket::send( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_send(h, ex, buf, flags, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -epoll_udp_socket::recv( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv(h, ex, buf, flags, token, ec, bytes_out); -} - -inline endpoint -epoll_udp_socket::remote_endpoint() const noexcept -{ - return reactor_datagram_socket::remote_endpoint(); -} - -inline void -epoll_udp_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -epoll_udp_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline std::error_code -epoll_udp_service::open_datagram_socket( - udp_socket::implementation& impl, int family, int type, int protocol) -{ - auto* epoll_impl = static_cast(&impl); - epoll_impl->close_socket(); - - int fd = ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); - if (fd < 0) - return make_err(errno); - - if (family == AF_INET6) - { - int one = 1; - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - } - - epoll_impl->fd_ = fd; - - epoll_impl->desc_state_.fd = fd; - { - std::lock_guard lock(epoll_impl->desc_state_.mutex); - epoll_impl->desc_state_.read_op = nullptr; - epoll_impl->desc_state_.write_op = nullptr; - epoll_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &epoll_impl->desc_state_); - - return {}; -} - -inline std::error_code -epoll_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/epoll/epoll_udp_socket.hpp b/include/boost/corosio/native/detail/epoll/epoll_udp_socket.hpp deleted file mode 100644 index 1742fc312..000000000 --- a/include/boost/corosio/native/detail/epoll/epoll_udp_socket.hpp +++ /dev/null @@ -1,140 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_EPOLL - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class epoll_udp_service; -class epoll_udp_socket; - -/// epoll datagram base operation. -struct epoll_datagram_op : reactor_op -{ - void operator()() override; -}; - -/// epoll send_to operation. -struct epoll_send_to_op final : reactor_send_to_op -{ - void cancel() noexcept override; -}; - -/// epoll recv_from operation. -struct epoll_recv_from_op final : reactor_recv_from_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// epoll connect operation for UDP. -struct epoll_udp_connect_op final : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// epoll connected send operation. -struct epoll_send_op final : reactor_send_op -{ - void cancel() noexcept override; -}; - -/// epoll connected recv operation. -struct epoll_recv_op final : reactor_recv_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Datagram socket implementation for epoll backend. -class epoll_udp_socket final - : public reactor_datagram_socket< - epoll_udp_socket, - epoll_udp_service, - epoll_udp_connect_op, - epoll_send_to_op, - epoll_recv_from_op, - epoll_send_op, - epoll_recv_op, - descriptor_state> -{ - friend class epoll_udp_service; - -public: - explicit epoll_udp_socket(epoll_udp_service& svc) noexcept; - ~epoll_udp_socket() override; - - std::coroutine_handle<> send_to( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - endpoint, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv_from( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - endpoint*, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> send( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - endpoint remote_endpoint() const noexcept override; - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_EPOLL - -#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_local_datagram_service.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_local_datagram_service.hpp deleted file mode 100644 index ef170a74a..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_local_datagram_service.hpp +++ /dev/null @@ -1,339 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_DATAGRAM_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_DATAGRAM_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class BOOST_COROSIO_DECL kqueue_local_datagram_service final - : public reactor_socket_service< - kqueue_local_datagram_service, - local_datagram_service, - kqueue_scheduler, - kqueue_local_datagram_socket> -{ -public: - explicit kqueue_local_datagram_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - local_datagram_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code assign_socket( - local_datagram_socket::implementation& impl, - native_handle_type fd) override; - - std::error_code bind_socket( - local_datagram_socket::implementation& impl, - corosio::local_endpoint ep) override; -}; - -// Cancellation for connectionless ops - -inline void -kqueue_local_send_to_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_local_recv_from_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Cancellation for connected-mode ops - -inline void -kqueue_local_dgram_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_local_dgram_send_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_local_dgram_recv_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Completion handlers - -inline void -kqueue_local_datagram_op::operator()() -{ - complete_io_op(*this); -} - -inline void -kqueue_local_recv_from_op::operator()() -{ - complete_datagram_op(*this, this->source_out); -} - -inline void -kqueue_local_dgram_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline void -kqueue_local_dgram_recv_op::operator()() -{ - // Datagram completion: do not map 0 bytes to EOF - complete_dgram_recv_op(*this); -} - -// Socket construction/destruction - -inline kqueue_local_datagram_socket::kqueue_local_datagram_socket( - kqueue_local_datagram_service& svc) noexcept - : reactor_datagram_socket(svc) -{ -} - -inline kqueue_local_datagram_socket::~kqueue_local_datagram_socket() = default; - -// Connectionless I/O - -inline std::coroutine_handle<> -kqueue_local_datagram_socket::send_to( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - corosio::local_endpoint dest, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_send_to(h, ex, buf, dest, flags, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -kqueue_local_datagram_socket::recv_from( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - corosio::local_endpoint* source, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv_from(h, ex, buf, source, flags, token, ec, bytes_out); -} - -// Connected-mode I/O - -inline std::coroutine_handle<> -kqueue_local_datagram_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - corosio::local_endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - return do_connect(h, ex, ep, token, ec); -} - -inline std::coroutine_handle<> -kqueue_local_datagram_socket::send( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_send(h, ex, buf, flags, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -kqueue_local_datagram_socket::recv( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv(h, ex, buf, flags, token, ec, bytes_out); -} - -inline void -kqueue_local_datagram_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -kqueue_local_datagram_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -kqueue_local_datagram_socket::release_socket() noexcept -{ - return this->do_release_socket(); -} - -// Service implementations - -inline std::error_code -kqueue_local_datagram_service::open_socket( - local_datagram_socket::implementation& impl, - int family, - int type, - int protocol) -{ - auto* kq_impl = static_cast(&impl); - kq_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - // Set non-blocking - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - // Set close-on-exec - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - // Suppress SIGPIPE on this socket; sendmsg() has no MSG_NOSIGNAL - // equivalent on macOS/FreeBSD. -#ifdef SO_NOSIGPIPE - int one = 1; - if (::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) != 0) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } -#endif - - kq_impl->fd_ = fd; - - kq_impl->desc_state_.fd = fd; - { - std::lock_guard lock(kq_impl->desc_state_.mutex); - kq_impl->desc_state_.read_op = nullptr; - kq_impl->desc_state_.write_op = nullptr; - kq_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &kq_impl->desc_state_); - - return {}; -} - -inline std::error_code -kqueue_local_datagram_service::assign_socket( - local_datagram_socket::implementation& impl, - native_handle_type fd) -{ - if (fd < 0) - return make_err(EBADF); - - auto* kq_impl = static_cast(&impl); - kq_impl->close_socket(); - - kq_impl->fd_ = fd; - - kq_impl->desc_state_.fd = fd; - { - std::lock_guard lock(kq_impl->desc_state_.mutex); - kq_impl->desc_state_.read_op = nullptr; - kq_impl->desc_state_.write_op = nullptr; - kq_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &kq_impl->desc_state_); - - return {}; -} - -inline std::error_code -kqueue_local_datagram_service::bind_socket( - local_datagram_socket::implementation& impl, - corosio::local_endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_DATAGRAM_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_local_datagram_socket.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_local_datagram_socket.hpp deleted file mode 100644 index e86b8519f..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_local_datagram_socket.hpp +++ /dev/null @@ -1,166 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_DATAGRAM_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_DATAGRAM_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -// Forward declarations -class kqueue_local_datagram_service; -class kqueue_local_datagram_socket; - -/// Base operation for local datagram sockets on kqueue. -struct kqueue_local_datagram_op - : reactor_op -{ - void operator()() override; -}; - -/// Connect operation for local datagram sockets. -struct kqueue_local_dgram_connect_op final - : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Send-to operation for local datagram sockets. -struct kqueue_local_send_to_op final - : reactor_send_to_op -{ - void cancel() noexcept override; -}; - -/// Recv-from operation for local datagram sockets. -struct kqueue_local_recv_from_op final - : reactor_recv_from_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Connected send operation for local datagram sockets. -struct kqueue_local_dgram_send_op final - : reactor_send_op -{ - void cancel() noexcept override; -}; - -/// Connected recv operation for local datagram sockets. -struct kqueue_local_dgram_recv_op final - : reactor_recv_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Datagram socket implementation for local (Unix) sockets on kqueue. -class kqueue_local_datagram_socket final - : public reactor_datagram_socket< - kqueue_local_datagram_socket, - kqueue_local_datagram_service, - kqueue_local_dgram_connect_op, - kqueue_local_send_to_op, - kqueue_local_recv_from_op, - kqueue_local_dgram_send_op, - kqueue_local_dgram_recv_op, - descriptor_state, - local_datagram_socket::implementation, - corosio::local_endpoint> -{ - friend class kqueue_local_datagram_service; - -public: - explicit kqueue_local_datagram_socket( - kqueue_local_datagram_service& svc) noexcept; - ~kqueue_local_datagram_socket() override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - corosio::local_endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> send_to( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - corosio::local_endpoint, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv_from( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - corosio::local_endpoint*, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> send( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::error_code shutdown( - local_datagram_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - std::error_code bind(corosio::local_endpoint ep) noexcept override - { - return do_bind(ep); - } - - corosio::local_endpoint remote_endpoint() const noexcept override - { - return reactor_datagram_socket::remote_endpoint(); - } - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_DATAGRAM_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_acceptor.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_acceptor.hpp deleted file mode 100644 index af2c7f39a..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_acceptor.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_ACCEPTOR_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_ACCEPTOR_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class kqueue_local_stream_acceptor_service; - -/// Acceptor implementation for local stream sockets on kqueue. -class kqueue_local_stream_acceptor final - : public reactor_acceptor< - kqueue_local_stream_acceptor, - kqueue_local_stream_acceptor_service, - kqueue_local_stream_op, - kqueue_local_accept_op, - descriptor_state, - local_stream_acceptor::implementation, - local_endpoint> -{ - friend class kqueue_local_stream_acceptor_service; - -public: - explicit kqueue_local_stream_acceptor( - kqueue_local_stream_acceptor_service& svc) noexcept; - - std::coroutine_handle<> accept( - std::coroutine_handle<>, - capy::executor_ref, - std::stop_token, - std::error_code*, - io_object::implementation**) override; - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_ACCEPTOR_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_acceptor_service.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_acceptor_service.hpp deleted file mode 100644 index 9b274d7ee..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_acceptor_service.hpp +++ /dev/null @@ -1,359 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/* kqueue local stream acceptor service implementation. - - Inherits from local_stream_acceptor_service to enable runtime - polymorphism. Uses key_type = local_stream_acceptor_service - for service lookup. -*/ -class BOOST_COROSIO_DECL kqueue_local_stream_acceptor_service final - : public reactor_acceptor_service< - kqueue_local_stream_acceptor_service, - local_stream_acceptor_service, - kqueue_scheduler, - kqueue_local_stream_acceptor, - kqueue_local_stream_service> -{ - using base_type = reactor_acceptor_service< - kqueue_local_stream_acceptor_service, - local_stream_acceptor_service, - kqueue_scheduler, - kqueue_local_stream_acceptor, - kqueue_local_stream_service>; - friend base_type; - -public: - explicit kqueue_local_stream_acceptor_service(capy::execution_context& ctx); - ~kqueue_local_stream_acceptor_service() override; - - std::error_code open_acceptor_socket( - local_stream_acceptor::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code bind_acceptor( - local_stream_acceptor::implementation& impl, - corosio::local_endpoint ep) override; - std::error_code listen_acceptor( - local_stream_acceptor::implementation& impl, - int backlog) override; -}; - -inline void -kqueue_local_accept_op::cancel() noexcept -{ - if (acceptor_impl_) - acceptor_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_local_accept_op::operator()() -{ - complete_accept_op(*this); -} - -inline kqueue_local_stream_acceptor::kqueue_local_stream_acceptor( - kqueue_local_stream_acceptor_service& svc) noexcept - : reactor_acceptor(svc) -{ -} - -inline std::coroutine_handle<> -kqueue_local_stream_acceptor::accept( - std::coroutine_handle<> h, - capy::executor_ref ex, - std::stop_token token, - std::error_code* ec, - io_object::implementation** impl_out) -{ - auto& op = acc_; - op.reset(); - op.h = h; - op.ex = ex; - op.ec_out = ec; - op.impl_out = impl_out; - op.fd = fd_; - op.start(token, this); - - sockaddr_storage peer_storage{}; - socklen_t addrlen; - - int accepted; - do - { - addrlen = sizeof(peer_storage); - accepted = ::accept( - fd_, reinterpret_cast(&peer_storage), &addrlen); - } - while (accepted < 0 && errno == EINTR); - - if (accepted >= 0) - { - // Set non-blocking and close-on-exec on the accepted socket - int flags = ::fcntl(accepted, F_GETFL, 0); - if (flags == -1 || ::fcntl(accepted, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(accepted); - op.complete(errn, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - if (::fcntl(accepted, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(accepted); - op.complete(errn, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - // SO_NOSIGPIPE before budget check so both inline and - // queued paths have it applied (macOS lacks MSG_NOSIGNAL) -#ifdef SO_NOSIGPIPE - int one = 1; - if (::setsockopt( - accepted, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) == -1) - { - int errn = errno; - ::close(accepted); - op.complete(errn, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } -#endif - - { - std::lock_guard lock(desc_state_.mutex); - desc_state_.read_ready = false; - } - - if (svc_.scheduler().try_consume_inline_budget()) - { - auto* socket_svc = svc_.stream_service(); - if (socket_svc) - { - auto& impl = - static_cast( - *socket_svc->construct()); - impl.set_socket(accepted); - - impl.desc_state_.fd = accepted; - { - std::lock_guard lock(impl.desc_state_.mutex); - impl.desc_state_.read_op = nullptr; - impl.desc_state_.write_op = nullptr; - impl.desc_state_.connect_op = nullptr; - } - socket_svc->scheduler().register_descriptor( - accepted, &impl.desc_state_); - - impl.set_endpoints( - local_endpoint_, - from_sockaddr_as(peer_storage, addrlen, corosio::local_endpoint{})); - - *ec = {}; - if (impl_out) - *impl_out = &impl; - } - else - { - ::close(accepted); - *ec = make_err(ENOENT); - if (impl_out) - *impl_out = nullptr; - } - op.cont_op.cont.h = h; - return dispatch_coro(ex, op.cont_op.cont); - } - - op.accepted_fd = accepted; - op.peer_storage = peer_storage; - op.peer_addrlen = addrlen; - op.complete(0, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - op.impl_ptr = shared_from_this(); - svc_.work_started(); - - std::lock_guard lock(desc_state_.mutex); - bool io_done = false; - if (desc_state_.read_ready) - { - desc_state_.read_ready = false; - op.perform_io(); - io_done = (op.errn != EAGAIN && op.errn != EWOULDBLOCK); - if (!io_done) - op.errn = 0; - } - - if (io_done || op.cancelled.load(std::memory_order_acquire)) - { - svc_.post(&op); - svc_.work_finished(); - } - else - { - desc_state_.read_op = &op; - } - return std::noop_coroutine(); - } - - op.complete(errno, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); -} - -inline void -kqueue_local_stream_acceptor::cancel() noexcept -{ - do_cancel(); -} - -inline void -kqueue_local_stream_acceptor::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -kqueue_local_stream_acceptor::release_socket() noexcept -{ - return this->do_release_socket(); -} - -inline kqueue_local_stream_acceptor_service:: - kqueue_local_stream_acceptor_service(capy::execution_context& ctx) - : base_type(ctx) -{ - auto* svc = ctx_.find_service(); - stream_svc_ = svc - ? dynamic_cast(svc) - : nullptr; - assert(stream_svc_ && - "local_stream_service must be registered before acceptor service"); -} - -inline kqueue_local_stream_acceptor_service:: - ~kqueue_local_stream_acceptor_service() {} - -inline std::error_code -kqueue_local_stream_acceptor_service::open_acceptor_socket( - local_stream_acceptor::implementation& impl, - int family, - int type, - int protocol) -{ - auto* kq_impl = static_cast(&impl); - kq_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - // Set non-blocking and close-on-exec - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - // SO_NOSIGPIPE on macOS (where MSG_NOSIGNAL doesn't exist) -#ifdef SO_NOSIGPIPE - int nosig = 1; - ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &nosig, sizeof(nosig)); -#endif - - kq_impl->fd_ = fd; - - // Set up descriptor state but do NOT register with kqueue yet - kq_impl->desc_state_.fd = fd; - { - std::lock_guard lock(kq_impl->desc_state_.mutex); - kq_impl->desc_state_.read_op = nullptr; - } - - return {}; -} - -inline std::error_code -kqueue_local_stream_acceptor_service::bind_acceptor( - local_stream_acceptor::implementation& impl, corosio::local_endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -inline std::error_code -kqueue_local_stream_acceptor_service::listen_acceptor( - local_stream_acceptor::implementation& impl, int backlog) -{ - return static_cast(&impl)->do_listen(backlog); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_service.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_service.hpp deleted file mode 100644 index e2b489cca..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_service.hpp +++ /dev/null @@ -1,256 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class BOOST_COROSIO_DECL kqueue_local_stream_service final - : public reactor_socket_service< - kqueue_local_stream_service, - local_stream_service, - kqueue_scheduler, - kqueue_local_stream_socket> -{ -public: - explicit kqueue_local_stream_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - local_stream_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code assign_socket( - local_stream_socket::implementation& impl, - native_handle_type fd) override; -}; - -// Op implementations - -inline void -kqueue_local_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_local_read_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_local_write_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_local_stream_op::operator()() -{ - complete_io_op(*this); -} - -inline void -kqueue_local_connect_op::operator()() -{ - complete_connect_op(*this); -} - -// Socket implementations - -inline kqueue_local_stream_socket::kqueue_local_stream_socket( - kqueue_local_stream_service& svc) noexcept - : reactor_stream_socket(svc) -{ -} - -inline kqueue_local_stream_socket::~kqueue_local_stream_socket() = default; - -inline std::coroutine_handle<> -kqueue_local_stream_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - corosio::local_endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - return do_connect(h, ex, ep, token, ec); -} - -inline std::coroutine_handle<> -kqueue_local_stream_socket::read_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_read_some(h, ex, param, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -kqueue_local_stream_socket::write_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_write_some(h, ex, param, token, ec, bytes_out); -} - -inline void -kqueue_local_stream_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -kqueue_local_stream_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -kqueue_local_stream_socket::release_socket() noexcept -{ - return this->do_release_socket(); -} - -// Service implementations - -inline std::error_code -kqueue_local_stream_service::open_socket( - local_stream_socket::implementation& impl, - int family, - int type, - int protocol) -{ - auto* kq_impl = static_cast(&impl); - kq_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - // Set non-blocking - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - // Set close-on-exec - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - // Suppress SIGPIPE on this socket; writev() has no MSG_NOSIGNAL - // equivalent, so SO_NOSIGPIPE is required on macOS/FreeBSD. - int one = 1; - if (::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) != 0) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - kq_impl->fd_ = fd; - - kq_impl->desc_state_.fd = fd; - { - std::lock_guard lock(kq_impl->desc_state_.mutex); - kq_impl->desc_state_.read_op = nullptr; - kq_impl->desc_state_.write_op = nullptr; - kq_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &kq_impl->desc_state_); - - return {}; -} - -inline std::error_code -kqueue_local_stream_service::assign_socket( - local_stream_socket::implementation& impl, - native_handle_type fd) -{ - if (fd < 0) - return make_err(EBADF); - - auto* kq_impl = static_cast(&impl); - kq_impl->close_socket(); - - kq_impl->fd_ = fd; - - kq_impl->desc_state_.fd = fd; - { - std::lock_guard lock(kq_impl->desc_state_.mutex); - kq_impl->desc_state_.read_op = nullptr; - kq_impl->desc_state_.write_op = nullptr; - kq_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &kq_impl->desc_state_); - - return {}; -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_socket.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_socket.hpp deleted file mode 100644 index 8e097e538..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_local_stream_socket.hpp +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -// Forward declarations -class kqueue_local_stream_service; -class kqueue_local_stream_socket; -class kqueue_local_stream_acceptor; - -/// Base operation for local stream sockets on kqueue. -struct kqueue_local_stream_op - : reactor_op -{ - void operator()() override; -}; - -/// Connect operation for local stream sockets. -struct kqueue_local_connect_op final - : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Scatter-read operation for local stream sockets. -struct kqueue_local_read_op final : reactor_read_op -{ - void cancel() noexcept override; -}; - -/// Gather-write operation for local stream sockets. -struct kqueue_local_write_op final - : reactor_write_op -{ - void cancel() noexcept override; -}; - -/// Accept operation for local stream sockets. -struct kqueue_local_accept_op final - : reactor_accept_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Stream socket implementation for local (Unix) sockets on kqueue. -class kqueue_local_stream_socket final - : public reactor_stream_socket< - kqueue_local_stream_socket, - kqueue_local_stream_service, - kqueue_local_connect_op, - kqueue_local_read_op, - kqueue_local_write_op, - descriptor_state, - local_stream_socket::implementation, - corosio::local_endpoint> -{ - friend class kqueue_local_stream_service; - -public: - explicit kqueue_local_stream_socket( - kqueue_local_stream_service& svc) noexcept; - ~kqueue_local_stream_socket() override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - corosio::local_endpoint, - std::stop_token, - std::error_code*) override; - - std::error_code shutdown( - local_stream_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - std::coroutine_handle<> read_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> write_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_LOCAL_STREAM_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_op.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_op.hpp deleted file mode 100644 index b90761b55..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_op.hpp +++ /dev/null @@ -1,192 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_OP_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_OP_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include - -#include -#include - -/* - kqueue Operation State - ====================== - - Each async I/O operation has a corresponding kqueue_op-derived struct that - holds the operation's state while it's in flight. The socket impl owns - fixed slots for each operation type (conn_, rd_, wr_), so only one - operation of each type can be pending per socket at a time. - - Persistent Registration - ----------------------- - File descriptors are registered with kqueue once (via descriptor_state) and - stay registered until closed. Uses EV_CLEAR for edge-triggered semantics - (equivalent to epoll's EPOLLET). The descriptor_state tracks which operations - are pending (read_op, write_op, connect_op). When an event arrives, the - reactor dispatches to the appropriate pending operation. - - Impl Lifetime Management - ------------------------ - When cancel() posts an op to the scheduler's ready queue, the socket impl - might be destroyed before the scheduler processes the op. The `impl_ptr` - member holds a shared_ptr to the impl, keeping it alive until the op - completes. This is set by cancel() and cleared in operator() after the - coroutine is resumed. - - EOF Detection - ------------- - For reads, 0 bytes with no error means EOF. But an empty user buffer also - returns 0 bytes. The `empty_buffer_read` flag distinguishes these cases. - - SIGPIPE Prevention - ------------------ - SO_NOSIGPIPE is set on each socket at creation time (see sockets.cpp). - Writes use writev() which is safe because the socket-level option suppresses - SIGPIPE delivery. -*/ - -namespace boost::corosio::detail { - -// Aliases for shared reactor event constants. -// Kept for backward compatibility in kqueue-specific code. -static constexpr std::uint32_t kqueue_event_read = reactor_event_read; -static constexpr std::uint32_t kqueue_event_write = reactor_event_write; -static constexpr std::uint32_t kqueue_event_error = reactor_event_error; - -// Forward declarations -class kqueue_tcp_socket; -class kqueue_tcp_acceptor; -struct kqueue_op; - -class kqueue_scheduler; - -/// Per-descriptor state for persistent kqueue registration. -struct descriptor_state final : reactor_descriptor_state -{}; - -/// kqueue base operation — thin wrapper over reactor_op. -struct kqueue_op : reactor_op -{ - void operator()() override; -}; - -/// kqueue connect operation. -struct kqueue_connect_op final : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// kqueue scatter-read operation. -struct kqueue_read_op final : reactor_read_op -{ - void cancel() noexcept override; -}; - -/** Provides writev() for kqueue writes. - - SO_NOSIGPIPE is set on the socket at creation time (macOS lacks - MSG_NOSIGNAL), so writev() is safe from SIGPIPE. -*/ -struct kqueue_write_policy -{ - static ssize_t write(int fd, iovec* iovecs, int count) noexcept - { - ssize_t n; - do - { - n = ::writev(fd, iovecs, count); - } - while (n < 0 && errno == EINTR); - return n; - } -}; - -/// kqueue gather-write operation. -struct kqueue_write_op final : reactor_write_op -{ - void cancel() noexcept override; -}; - -/** Provides accept() + fcntl() + SO_NOSIGPIPE for kqueue accepts. - - Unlike Linux's accept4(), BSD accept() does not support atomic - flag setting. Non-blocking, close-on-exec, and SIGPIPE suppression - are applied via separate syscalls after accept(). -*/ -struct kqueue_accept_policy -{ - static int do_accept( - int fd, sockaddr_storage& peer, socklen_t& addrlen_out) noexcept - { - addrlen_out = sizeof(peer); - int new_fd; - do - { - addrlen_out = sizeof(peer); - new_fd = ::accept( - fd, reinterpret_cast(&peer), &addrlen_out); - } - while (new_fd < 0 && errno == EINTR); - - if (new_fd < 0) - return new_fd; - - int flags = ::fcntl(new_fd, F_GETFL, 0); - if (flags == -1 || ::fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int err = errno; - ::close(new_fd); - errno = err; - return -1; - } - - if (::fcntl(new_fd, F_SETFD, FD_CLOEXEC) == -1) - { - int err = errno; - ::close(new_fd); - errno = err; - return -1; - } - - // macOS lacks MSG_NOSIGNAL - int one = 1; - if (::setsockopt(new_fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) == - -1) - { - int err = errno; - ::close(new_fd); - errno = err; - return -1; - } - - return new_fd; - } -}; - -/// kqueue accept operation. -struct kqueue_accept_op final - : reactor_accept_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_OP_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_scheduler.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_scheduler.hpp index d5ba38421..60ae2f21a 100644 --- a/include/boost/corosio/native/detail/kqueue/kqueue_scheduler.hpp +++ b/include/boost/corosio/native/detail/kqueue/kqueue_scheduler.hpp @@ -20,7 +20,7 @@ #include -#include +#include #include #include #include @@ -46,7 +46,6 @@ namespace boost::corosio::detail { struct kqueue_op; -struct descriptor_state; /** macOS/BSD scheduler using kqueue for I/O multiplexing. @@ -126,14 +125,14 @@ class BOOST_COROSIO_DECL kqueue_scheduler final : public reactor_scheduler Adds EVFILT_READ and EVFILT_WRITE (both EV_CLEAR) for @a fd and stores @a desc in the kevent udata field so that the - reactor can dispatch events to the correct descriptor_state. + reactor can dispatch events to the correct reactor_descriptor_state. @param fd The file descriptor to register. - @param desc Pointer to the caller-owned descriptor_state. + @param desc Pointer to the caller-owned reactor_descriptor_state. @throws std::system_error if kevent(EV_ADD) fails. */ - void register_descriptor(int fd, descriptor_state* desc) const; + void register_descriptor(int fd, reactor_descriptor_state* desc) const; /** Deregister a persistently registered descriptor. @@ -227,7 +226,7 @@ kqueue_scheduler::configure_reactor( } inline void -kqueue_scheduler::register_descriptor(int fd, descriptor_state* desc) const +kqueue_scheduler::register_descriptor(int fd, reactor_descriptor_state* desc) const { struct kevent changes[2]; EV_SET( @@ -240,7 +239,7 @@ kqueue_scheduler::register_descriptor(int fd, descriptor_state* desc) const if (::kevent(kq_fd_, changes, 2, nullptr, 0, nullptr) < 0) detail::throw_system_error(make_err(errno), "kevent (register)"); - desc->registered_events = kqueue_event_read | kqueue_event_write; + desc->registered_events = reactor_event_read | reactor_event_write; desc->fd = fd; desc->scheduler_ = this; desc->mutex.set_enabled(!single_threaded_); @@ -349,26 +348,26 @@ kqueue_scheduler::run_task( } auto* desc = - static_cast(event_buffer_[i].udata); + static_cast(event_buffer_[i].udata); if (!desc) continue; std::uint32_t ready = 0; if (event_buffer_[i].filter == EVFILT_READ) - ready |= kqueue_event_read; + ready |= reactor_event_read; else if (event_buffer_[i].filter == EVFILT_WRITE) - ready |= kqueue_event_write; + ready |= reactor_event_write; if (event_buffer_[i].flags & EV_ERROR) - ready |= kqueue_event_error; + ready |= reactor_event_error; if (event_buffer_[i].flags & EV_EOF) { if (event_buffer_[i].filter == EVFILT_READ) - ready |= kqueue_event_read; + ready |= reactor_event_read; if (event_buffer_[i].fflags != 0) - ready |= kqueue_event_error; + ready |= reactor_event_error; } desc->add_ready_events(ready); diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_tcp_acceptor.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_tcp_acceptor.hpp deleted file mode 100644 index 124b6e4e7..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_tcp_acceptor.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_ACCEPTOR_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_ACCEPTOR_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include -#include - -namespace boost::corosio::detail { - -class kqueue_tcp_acceptor_service; - -/// Acceptor implementation for kqueue backend. -class kqueue_tcp_acceptor final - : public reactor_acceptor< - kqueue_tcp_acceptor, - kqueue_tcp_acceptor_service, - kqueue_op, - kqueue_accept_op, - descriptor_state> -{ - friend class kqueue_tcp_acceptor_service; - -public: - explicit kqueue_tcp_acceptor(kqueue_tcp_acceptor_service& svc) noexcept; - - /** Initiate an asynchronous accept on the listening socket. - - Attempts a synchronous accept first. If the socket would block - (EAGAIN), the operation is parked in desc_state_ until the - reactor delivers a read-readiness event, at which point the - accept is retried. On completion (success, error, or - cancellation) the operation is posted to the scheduler and - @a caller is resumed via @a ex. - - Only one accept may be outstanding at a time; overlapping - calls produce undefined behavior. - - @param caller Coroutine handle resumed on completion. - @param ex Executor through which @a caller is resumed. - @param token Stop token for cancellation. - @param ec Points to storage for the result error code. - @param out_impl Points to storage for the accepted socket impl. - - @return std::noop_coroutine() unconditionally. - */ - std::coroutine_handle<> accept( - std::coroutine_handle<> caller, - capy::executor_ref ex, - std::stop_token token, - std::error_code* ec, - io_object::implementation** out_impl) override; - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_ACCEPTOR_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_tcp_acceptor_service.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_tcp_acceptor_service.hpp deleted file mode 100644 index 6309f5def..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_tcp_acceptor_service.hpp +++ /dev/null @@ -1,339 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_ACCEPTOR_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_ACCEPTOR_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/** kqueue acceptor service implementation. - - Inherits from tcp_acceptor_service to enable runtime polymorphism. - Uses key_type = tcp_acceptor_service for service lookup. -*/ -class BOOST_COROSIO_DECL kqueue_tcp_acceptor_service final - : public reactor_acceptor_service< - kqueue_tcp_acceptor_service, - tcp_acceptor_service, - kqueue_scheduler, - kqueue_tcp_acceptor, - kqueue_tcp_service> -{ - using base_type = reactor_acceptor_service< - kqueue_tcp_acceptor_service, - tcp_acceptor_service, - kqueue_scheduler, - kqueue_tcp_acceptor, - kqueue_tcp_service>; - friend base_type; - -public: - explicit kqueue_tcp_acceptor_service(capy::execution_context& ctx); - ~kqueue_tcp_acceptor_service() override; - - std::error_code open_acceptor_socket( - tcp_acceptor::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code - bind_acceptor(tcp_acceptor::implementation& impl, endpoint ep) override; - std::error_code - listen_acceptor(tcp_acceptor::implementation& impl, int backlog) override; -}; - -inline void -kqueue_accept_op::cancel() noexcept -{ - if (acceptor_impl_) - acceptor_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_accept_op::operator()() -{ - complete_accept_op(*this); -} - -inline kqueue_tcp_acceptor::kqueue_tcp_acceptor( - kqueue_tcp_acceptor_service& svc) noexcept - : reactor_acceptor(svc) -{ -} - -inline std::coroutine_handle<> -kqueue_tcp_acceptor::accept( - std::coroutine_handle<> h, - capy::executor_ref ex, - std::stop_token token, - std::error_code* ec, - io_object::implementation** impl_out) -{ - auto& op = acc_; - op.reset(); - op.h = h; - op.ex = ex; - op.ec_out = ec; - op.impl_out = impl_out; - op.fd = fd_; - op.start(token, this); - - sockaddr_storage peer_storage{}; - socklen_t addrlen = sizeof(peer_storage); - - // FreeBSD: Can use accept4(fd_, addr, addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC) - int accepted = - ::accept(fd_, reinterpret_cast(&peer_storage), &addrlen); - - if (accepted >= 0) - { - // Set non-blocking and close-on-exec on the accepted socket - int flags = ::fcntl(accepted, F_GETFL, 0); - if (flags == -1 || ::fcntl(accepted, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(accepted); - op.complete(errn, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - if (::fcntl(accepted, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(accepted); - op.complete(errn, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - // SO_NOSIGPIPE before budget check so both inline and - // queued paths have it applied (macOS lacks MSG_NOSIGNAL) - int one = 1; - if (::setsockopt( - accepted, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) == -1) - { - int errn = errno; - ::close(accepted); - op.complete(errn, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - { - std::lock_guard lock(desc_state_.mutex); - desc_state_.read_ready = false; - } - - if (svc_.scheduler().try_consume_inline_budget()) - { - auto* socket_svc = svc_.stream_service(); - if (socket_svc) - { - auto& impl = - static_cast(*socket_svc->construct()); - impl.set_socket(accepted); - - impl.desc_state_.fd = accepted; - { - std::lock_guard lock(impl.desc_state_.mutex); - impl.desc_state_.read_op = nullptr; - impl.desc_state_.write_op = nullptr; - impl.desc_state_.connect_op = nullptr; - } - socket_svc->scheduler().register_descriptor( - accepted, &impl.desc_state_); - - impl.set_endpoints( - local_endpoint_, from_sockaddr(peer_storage)); - - *ec = {}; - if (impl_out) - *impl_out = &impl; - } - else - { - ::close(accepted); - *ec = make_err(ENOENT); - if (impl_out) - *impl_out = nullptr; - } - op.cont_op.cont.h = h; - return dispatch_coro(ex, op.cont_op.cont); - } - - op.accepted_fd = accepted; - op.peer_storage = peer_storage; - op.peer_addrlen = addrlen; - op.complete(0, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - op.impl_ptr = shared_from_this(); - svc_.work_started(); - - std::lock_guard lock(desc_state_.mutex); - bool io_done = false; - if (desc_state_.read_ready) - { - desc_state_.read_ready = false; - op.perform_io(); - io_done = (op.errn != EAGAIN && op.errn != EWOULDBLOCK); - if (!io_done) - op.errn = 0; - } - - if (io_done || op.cancelled.load(std::memory_order_acquire)) - { - svc_.post(&op); - svc_.work_finished(); - } - else - { - desc_state_.read_op = &op; - } - return std::noop_coroutine(); - } - - op.complete(errno, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); -} - -inline void -kqueue_tcp_acceptor::cancel() noexcept -{ - do_cancel(); -} - -inline void -kqueue_tcp_acceptor::close_socket() noexcept -{ - do_close_socket(); -} - -inline kqueue_tcp_acceptor_service::kqueue_tcp_acceptor_service( - capy::execution_context& ctx) - : base_type(ctx) -{ - auto* svc = ctx_.find_service(); - stream_svc_ = svc ? dynamic_cast(svc) : nullptr; -} - -inline kqueue_tcp_acceptor_service::~kqueue_tcp_acceptor_service() {} - -inline std::error_code -kqueue_tcp_acceptor_service::open_acceptor_socket( - tcp_acceptor::implementation& impl, int family, int type, int protocol) -{ - auto* kq_impl = static_cast(&impl); - kq_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - // Set non-blocking and close-on-exec - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - if (family == AF_INET6) - { - int val = 0; // dual-stack default - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); - } - - // SO_NOSIGPIPE on macOS (where MSG_NOSIGNAL doesn't exist) -#ifdef SO_NOSIGPIPE - int nosig = 1; - ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &nosig, sizeof(nosig)); -#endif - - kq_impl->fd_ = fd; - - // Set up descriptor state but do NOT register with kqueue yet - kq_impl->desc_state_.fd = fd; - { - std::lock_guard lock(kq_impl->desc_state_.mutex); - kq_impl->desc_state_.read_op = nullptr; - } - - return {}; -} - -inline std::error_code -kqueue_tcp_acceptor_service::bind_acceptor( - tcp_acceptor::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -inline std::error_code -kqueue_tcp_acceptor_service::listen_acceptor( - tcp_acceptor::implementation& impl, int backlog) -{ - return static_cast(&impl)->do_listen(backlog); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_ACCEPTOR_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_tcp_service.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_tcp_service.hpp deleted file mode 100644 index ed82586d1..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_tcp_service.hpp +++ /dev/null @@ -1,357 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -/* - kqueue Socket Implementation - ============================ - - Each I/O operation follows the same pattern: - 1. Try the syscall speculatively (readv/writev) before suspending - 2. On success, return via symmetric transfer (the "pump" fast path) - 3. On budget exhaustion, post to the scheduler queue for fairness - 4. On EAGAIN, register_op() parks the op in the descriptor_state - - The speculative path avoids scheduler queue, mutex, and reactor - round-trips entirely. An inline budget limits consecutive inline - completions to prevent starvation of other connections. - - Cancellation - ------------ - See op.hpp for the completion/cancellation race handling via the - descriptor_state mutex. cancel() must complete pending operations (post - them with cancelled flag) so coroutines waiting on them can resume. - close_socket() calls cancel() first to ensure this. - - Impl Lifetime with shared_ptr - ----------------------------- - Socket impls use enable_shared_from_this. The service owns impls via - shared_ptr maps (impl_ptrs_) keyed by raw pointer for O(1) lookup and - removal. When a user calls close(), we call cancel() which posts pending - ops to the scheduler. - - CRITICAL: The posted ops must keep the impl alive until they complete. - Otherwise the scheduler would process a freed op (use-after-free). The - cancel() method captures shared_from_this() into op.impl_ptr before - posting. When the op completes, impl_ptr is cleared, allowing the impl - to be destroyed if no other references exist. - - Service Ownership - ----------------- - kqueue_tcp_service owns all socket impls. destroy_impl() removes the - shared_ptr from the map, but the impl may survive if ops still hold - impl_ptr refs. shutdown() closes all sockets and clears the map; any - in-flight ops will complete and release their refs. -*/ - -/* - kqueue socket implementation - ============================ - - Each kqueue_tcp_socket owns a descriptor_state that is persistently - registered with kqueue (EVFILT_READ + EVFILT_WRITE, both EV_CLEAR for - edge-triggered semantics). The descriptor_state tracks three operation - slots (read_op, write_op, connect_op) and two ready flags - (read_ready, write_ready) under a per-descriptor mutex. - - Speculative I/O and the pump - ---------------------------- - read_some() and write_some() attempt the syscall (readv/writev) - speculatively before suspending the caller. If data is available the - result is returned via symmetric transfer — no scheduler queue, no - mutex, no reactor round-trip. An inline budget limits consecutive - inline completions to prevent starvation of other connections. - - When the speculative attempt returns EAGAIN, register_op() parks the - operation in its descriptor_state slot under the per-descriptor mutex. - If a cached ready flag fires before parking, register_op() retries - the I/O once under the mutex. This eliminates the cached_initiator - coroutine frame that previously trampolined into do_read_io() / - do_write_io() after the caller suspended. - - Ready-flag protocol - ------------------- - When a kqueue event fires and no operation is pending for that - direction, the reactor sets the corresponding ready flag instead of - dropping the event. When register_op() finds the ready flag set, it - performs I/O immediately rather than parking. This prevents lost - wakeups under edge-triggered notification. -*/ - -namespace boost::corosio::detail { - -/** kqueue TCP service implementation. - - Inherits from tcp_service to enable runtime polymorphism. - Uses key_type = tcp_service for service lookup. -*/ -class BOOST_COROSIO_DECL kqueue_tcp_service final - : public reactor_socket_service< - kqueue_tcp_service, - tcp_service, - kqueue_scheduler, - kqueue_tcp_socket> -{ - using base_service = reactor_socket_service< - kqueue_tcp_service, - tcp_service, - kqueue_scheduler, - kqueue_tcp_socket>; - friend base_service; - - // Clear SO_LINGER before close so the destructor doesn't block - // and close() sends FIN instead of RST. RST doesn't reliably - // trigger EV_EOF on macOS kqueue. - static void reset_linger(kqueue_tcp_socket* impl) noexcept - { - if (impl->user_set_linger_ && impl->fd_ >= 0) - { - struct ::linger lg; - lg.l_onoff = 0; - lg.l_linger = 0; - ::setsockopt(impl->fd_, SOL_SOCKET, SO_LINGER, &lg, sizeof(lg)); - } - } - - void pre_shutdown(kqueue_tcp_socket* impl) noexcept - { - reset_linger(impl); - } - - void pre_destroy(kqueue_tcp_socket* impl) noexcept - { - reset_linger(impl); - } - -public: - explicit kqueue_tcp_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - tcp_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code - bind_socket(tcp_socket::implementation& impl, endpoint ep) override; -}; - -inline void -kqueue_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_read_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_write_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_op::operator()() -{ - complete_io_op(*this); -} - -inline void -kqueue_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline kqueue_tcp_socket::kqueue_tcp_socket(kqueue_tcp_service& svc) noexcept - : reactor_stream_socket(svc) -{ -} - -inline kqueue_tcp_socket::~kqueue_tcp_socket() = default; - -inline std::coroutine_handle<> -kqueue_tcp_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - return do_connect(h, ex, ep, token, ec); -} - -inline std::coroutine_handle<> -kqueue_tcp_socket::read_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_read_some(h, ex, param, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -kqueue_tcp_socket::write_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_write_some(h, ex, param, token, ec, bytes_out); -} - -inline std::error_code -kqueue_tcp_socket::set_option( - int level, int optname, void const* data, std::size_t size) noexcept -{ - if (::setsockopt(fd_, level, optname, data, static_cast(size)) != - 0) - return make_err(errno); - if (level == SOL_SOCKET && optname == SO_LINGER && - size >= sizeof(struct ::linger)) - user_set_linger_ = - static_cast(data)->l_onoff != 0; - return {}; -} - -inline void -kqueue_tcp_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -kqueue_tcp_socket::close_socket() noexcept -{ - do_close_socket(); - user_set_linger_ = false; -} - -inline std::error_code -kqueue_tcp_service::open_socket( - tcp_socket::implementation& impl, int family, int type, int protocol) -{ - auto* kq_impl = static_cast(&impl); - kq_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - if (family == AF_INET6) - { - int v6only = 1; - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)); - } - - // Set non-blocking - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - // Set close-on-exec - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - // Suppress SIGPIPE on this socket; writev() has no MSG_NOSIGNAL - // equivalent, so SO_NOSIGPIPE is required on macOS/FreeBSD. - int one = 1; - if (::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) != 0) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - kq_impl->fd_ = fd; - - // Register fd with kqueue (edge-triggered mode via EV_CLEAR) - kq_impl->desc_state_.fd = fd; - { - std::lock_guard lock(kq_impl->desc_state_.mutex); - kq_impl->desc_state_.read_op = nullptr; - kq_impl->desc_state_.write_op = nullptr; - kq_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &kq_impl->desc_state_); - - return {}; -} - -inline std::error_code -kqueue_tcp_service::bind_socket( - tcp_socket::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_tcp_socket.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_tcp_socket.hpp deleted file mode 100644 index 258d10a71..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_tcp_socket.hpp +++ /dev/null @@ -1,87 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include -#include - -namespace boost::corosio::detail { - -class kqueue_tcp_service; - -/// Stream socket implementation for kqueue backend. -class kqueue_tcp_socket final - : public reactor_stream_socket< - kqueue_tcp_socket, - kqueue_tcp_service, - kqueue_connect_op, - kqueue_read_op, - kqueue_write_op, - descriptor_state> -{ - friend class kqueue_tcp_service; - - bool user_set_linger_ = false; - -public: - explicit kqueue_tcp_socket(kqueue_tcp_service& svc) noexcept; - ~kqueue_tcp_socket(); - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> read_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> write_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - /// Track SO_LINGER for macOS kqueue workaround. - std::error_code set_option( - int level, - int optname, - void const* data, - std::size_t size) noexcept override; - - std::error_code shutdown(tcp_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TCP_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_traits.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_traits.hpp new file mode 100644 index 000000000..3b22e044c --- /dev/null +++ b/include/boost/corosio/native/detail/kqueue/kqueue_traits.hpp @@ -0,0 +1,255 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TRAITS_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TRAITS_HPP + +#include + +#if BOOST_COROSIO_HAS_KQUEUE + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* kqueue backend traits. + + Captures the platform-specific behavior of the BSD/macOS kqueue backend: + manual fcntl for O_NONBLOCK/FD_CLOEXEC, mandatory SO_NOSIGPIPE (macOS + lacks MSG_NOSIGNAL), writev() for writes, and accept()+fcntl for + accepted connections. +*/ + +namespace boost::corosio::detail { + +class kqueue_scheduler; + +struct kqueue_traits +{ + using scheduler_type = kqueue_scheduler; + using desc_state_type = reactor_descriptor_state; + + static constexpr bool needs_write_notification = false; + + /* macOS kqueue workaround: RST doesn't reliably trigger EV_EOF. + If the user sets SO_LINGER, we clear it before close so the + destructor doesn't block and close() sends FIN instead of RST. + + The hook tracks whether the user explicitly set SO_LINGER via + set_option(). On pre_shutdown/pre_destroy, if the flag is set, + we reset linger to off before the fd is closed. + */ + struct stream_socket_hook + { + bool user_set_linger_ = false; + + std::error_code on_set_option( + int fd, int level, int optname, + void const* data, std::size_t size) noexcept + { + if (::setsockopt( + fd, level, optname, data, + static_cast(size)) != 0) + return make_err(errno); + + if (level == SOL_SOCKET && optname == SO_LINGER && + size >= sizeof(struct ::linger)) + user_set_linger_ = + static_cast(data)->l_onoff != 0; + + return {}; + } + + void pre_shutdown(int fd) noexcept + { + reset_linger(fd); + } + + void pre_destroy(int fd) noexcept + { + reset_linger(fd); + } + + private: + void reset_linger(int fd) noexcept + { + if (user_set_linger_ && fd >= 0) + { + struct ::linger lg; + lg.l_onoff = 0; + lg.l_linger = 0; + ::setsockopt(fd, SOL_SOCKET, SO_LINGER, &lg, sizeof(lg)); + } + user_set_linger_ = false; + } + }; + + struct write_policy + { + static ssize_t write(int fd, iovec* iovecs, int count) noexcept + { + ssize_t n; + do + { + n = ::writev(fd, iovecs, count); + } + while (n < 0 && errno == EINTR); + return n; + } + }; + + struct accept_policy + { + static int do_accept( + int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept + { + int new_fd; + do + { + addrlen = sizeof(peer); + new_fd = ::accept( + fd, reinterpret_cast(&peer), &addrlen); + } + while (new_fd < 0 && errno == EINTR); + + if (new_fd < 0) + return new_fd; + + int flags = ::fcntl(new_fd, F_GETFL, 0); + if (flags == -1 || + ::fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) == -1) + { + int err = errno; + ::close(new_fd); + errno = err; + return -1; + } + + if (::fcntl(new_fd, F_SETFD, FD_CLOEXEC) == -1) + { + int err = errno; + ::close(new_fd); + errno = err; + return -1; + } + +#ifndef BOOST_COROSIO_MRDOCS + // SO_NOSIGPIPE is mandatory on kqueue platforms (macOS lacks + // MSG_NOSIGNAL). Skipped under MRDOCS so the docs build can + // parse this header on Linux, where SO_NOSIGPIPE is absent. + int one = 1; + if (::setsockopt( + new_fd, SOL_SOCKET, SO_NOSIGPIPE, + &one, sizeof(one)) == -1) + { + int err = errno; + ::close(new_fd); + errno = err; + return -1; + } +#endif + + return new_fd; + } + }; + + // Create a plain socket. Fd options are applied by configure_*(). + static int create_socket(int family, int type, int protocol) noexcept + { + return ::socket(family, type, protocol); + } + + // Set O_NONBLOCK, FD_CLOEXEC, and SO_NOSIGPIPE on a new fd. + // Caller is responsible for closing fd on error. + static std::error_code set_fd_options(int fd) noexcept + { + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags == -1) + return make_err(errno); + if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + return make_err(errno); + if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + return make_err(errno); + +#ifndef BOOST_COROSIO_MRDOCS + // SO_NOSIGPIPE is mandatory on kqueue platforms (see accept_policy). + int one = 1; + if (::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) != 0) + return make_err(errno); +#endif + + return {}; + } + + // Apply protocol-specific options after socket creation. + // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort). + static std::error_code + configure_ip_socket(int fd, int family) noexcept + { + auto ec = set_fd_options(fd); + if (ec) + return ec; + + if (family == AF_INET6) + { + int v6only = 1; + (void)::setsockopt( + fd, IPPROTO_IPV6, IPV6_V6ONLY, + &v6only, sizeof(v6only)); + } + return {}; + } + + // Apply protocol-specific options for acceptor sockets. + // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort). + static std::error_code + configure_ip_acceptor(int fd, int family) noexcept + { + auto ec = set_fd_options(fd); + if (ec) + return ec; + + if (family == AF_INET6) + { + int val = 0; + (void)::setsockopt( + fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); + } + return {}; + } + + // Apply options for local (unix) sockets. + static std::error_code + configure_local_socket(int fd) noexcept + { + return set_fd_options(fd); + } + + // Non-mutating validation for fds adopted via assign(). Used when + // the caller retains fd ownership responsibility. + static std::error_code + validate_assigned_fd(int /*fd*/) noexcept + { + return {}; + } +}; + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_HAS_KQUEUE + +#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TRAITS_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_types.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_types.hpp new file mode 100644 index 000000000..3c4caf51e --- /dev/null +++ b/include/boost/corosio/native/detail/kqueue/kqueue_types.hpp @@ -0,0 +1,258 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TYPES_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TYPES_HPP + +#include + +#if BOOST_COROSIO_HAS_KQUEUE + +/* Named per-backend types for the kqueue reactor. + + Each class is a final, named wrapper around the parameterized + reactor_*_impl templates. Forward-declarable from backend.hpp + so the concrete layer never pulls in platform headers. +*/ + +#include +#include +#include +#include + +namespace boost::corosio::detail { + +// Forward declarations for cross-references. +class kqueue_tcp_socket; +class kqueue_tcp_service; +class kqueue_tcp_acceptor; +class kqueue_tcp_acceptor_service; +class kqueue_udp_socket; +class kqueue_udp_service; +class kqueue_local_stream_socket; +class kqueue_local_stream_service; +class kqueue_local_stream_acceptor; +class kqueue_local_stream_acceptor_service; +class kqueue_local_datagram_socket; +class kqueue_local_datagram_service; + +// --- Stream sockets --- + +class kqueue_tcp_socket final + : public reactor_stream_socket_impl< + kqueue_tcp_socket, kqueue_traits, kqueue_tcp_service, + kqueue_tcp_acceptor, tcp_socket::implementation, endpoint> +{ + using base_type = reactor_stream_socket_impl< + kqueue_tcp_socket, kqueue_traits, kqueue_tcp_service, + kqueue_tcp_acceptor, tcp_socket::implementation, endpoint>; + friend kqueue_tcp_service; +public: + explicit kqueue_tcp_socket(kqueue_tcp_service& svc) noexcept + : base_type(svc) {} +}; + +class kqueue_local_stream_socket final + : public reactor_stream_socket_impl< + kqueue_local_stream_socket, kqueue_traits, + kqueue_local_stream_service, kqueue_local_stream_acceptor, + local_stream_socket::implementation, corosio::local_endpoint> +{ + using base_type = reactor_stream_socket_impl< + kqueue_local_stream_socket, kqueue_traits, + kqueue_local_stream_service, kqueue_local_stream_acceptor, + local_stream_socket::implementation, corosio::local_endpoint>; + friend kqueue_local_stream_service; +public: + explicit kqueue_local_stream_socket(kqueue_local_stream_service& svc) noexcept + : base_type(svc) {} + + native_handle_type release_socket() noexcept override + { + hook_ = {}; + return this->do_release_socket(); + } +}; + +// --- Datagram sockets --- + +class kqueue_udp_socket final + : public reactor_dgram_socket_impl< + kqueue_udp_socket, kqueue_traits, kqueue_udp_service, + kqueue_tcp_acceptor, udp_socket::implementation, endpoint> +{ + using base_type = reactor_dgram_socket_impl< + kqueue_udp_socket, kqueue_traits, kqueue_udp_service, + kqueue_tcp_acceptor, udp_socket::implementation, endpoint>; + friend kqueue_udp_service; +public: + explicit kqueue_udp_socket(kqueue_udp_service& svc) noexcept + : base_type(svc) {} +}; + +class kqueue_local_datagram_socket final + : public reactor_dgram_socket_impl< + kqueue_local_datagram_socket, kqueue_traits, + kqueue_local_datagram_service, kqueue_tcp_acceptor, + local_datagram_socket::implementation, corosio::local_endpoint> +{ + using base_type = reactor_dgram_socket_impl< + kqueue_local_datagram_socket, kqueue_traits, + kqueue_local_datagram_service, kqueue_tcp_acceptor, + local_datagram_socket::implementation, corosio::local_endpoint>; + friend kqueue_local_datagram_service; +public: + explicit kqueue_local_datagram_socket(kqueue_local_datagram_service& svc) noexcept + : base_type(svc) {} + + std::error_code shutdown(corosio::shutdown_type what) noexcept override + { + return this->do_shutdown(static_cast(what)); + } + + std::error_code bind(corosio::local_endpoint ep) noexcept override + { + return this->do_bind(ep); + } + + native_handle_type release_socket() noexcept override + { + return this->do_release_socket(); + } +}; + +// --- Acceptors --- + +class kqueue_tcp_acceptor final + : public reactor_acceptor_impl< + kqueue_tcp_acceptor, kqueue_traits, + kqueue_tcp_acceptor_service, kqueue_tcp_socket, + tcp_acceptor::implementation, endpoint> +{ + using base_type = reactor_acceptor_impl< + kqueue_tcp_acceptor, kqueue_traits, + kqueue_tcp_acceptor_service, kqueue_tcp_socket, + tcp_acceptor::implementation, endpoint>; + friend kqueue_tcp_acceptor_service; +public: + explicit kqueue_tcp_acceptor(kqueue_tcp_acceptor_service& svc) noexcept + : base_type(svc) {} +}; + +class kqueue_local_stream_acceptor final + : public reactor_acceptor_impl< + kqueue_local_stream_acceptor, kqueue_traits, + kqueue_local_stream_acceptor_service, + kqueue_local_stream_socket, + local_stream_acceptor::implementation, corosio::local_endpoint> +{ + using base_type = reactor_acceptor_impl< + kqueue_local_stream_acceptor, kqueue_traits, + kqueue_local_stream_acceptor_service, + kqueue_local_stream_socket, + local_stream_acceptor::implementation, corosio::local_endpoint>; + friend kqueue_local_stream_acceptor_service; +public: + explicit kqueue_local_stream_acceptor( + kqueue_local_stream_acceptor_service& svc) noexcept + : base_type(svc) {} + + native_handle_type release_socket() noexcept override + { + return this->do_release_socket(); + } +}; + +// --- Services --- + +class BOOST_COROSIO_DECL kqueue_tcp_service final + : public reactor_tcp_service_impl< + kqueue_tcp_service, kqueue_traits, kqueue_tcp_socket> +{ + using base_type = reactor_tcp_service_impl< + kqueue_tcp_service, kqueue_traits, kqueue_tcp_socket>; +public: + explicit kqueue_tcp_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL kqueue_local_stream_service final + : public reactor_local_stream_service_impl< + kqueue_local_stream_service, kqueue_traits, + kqueue_local_stream_socket> +{ + using base_type = reactor_local_stream_service_impl< + kqueue_local_stream_service, kqueue_traits, + kqueue_local_stream_socket>; +public: + explicit kqueue_local_stream_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL kqueue_udp_service final + : public reactor_udp_service_impl< + kqueue_udp_service, kqueue_traits, kqueue_udp_socket> +{ + using base_type = reactor_udp_service_impl< + kqueue_udp_service, kqueue_traits, kqueue_udp_socket>; +public: + explicit kqueue_udp_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL kqueue_local_datagram_service final + : public reactor_local_dgram_service_impl< + kqueue_local_datagram_service, kqueue_traits, + kqueue_local_datagram_socket> +{ + using base_type = reactor_local_dgram_service_impl< + kqueue_local_datagram_service, kqueue_traits, + kqueue_local_datagram_socket>; +public: + explicit kqueue_local_datagram_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL kqueue_tcp_acceptor_service final + : public reactor_acceptor_service_impl< + kqueue_tcp_acceptor_service, kqueue_traits, + tcp_acceptor_service, kqueue_tcp_acceptor, + kqueue_tcp_service, endpoint> +{ + using base_type = reactor_acceptor_service_impl< + kqueue_tcp_acceptor_service, kqueue_traits, + tcp_acceptor_service, kqueue_tcp_acceptor, + kqueue_tcp_service, endpoint>; +public: + explicit kqueue_tcp_acceptor_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL kqueue_local_stream_acceptor_service final + : public reactor_acceptor_service_impl< + kqueue_local_stream_acceptor_service, kqueue_traits, + local_stream_acceptor_service, + kqueue_local_stream_acceptor, + kqueue_local_stream_service, corosio::local_endpoint> +{ + using base_type = reactor_acceptor_service_impl< + kqueue_local_stream_acceptor_service, kqueue_traits, + local_stream_acceptor_service, + kqueue_local_stream_acceptor, + kqueue_local_stream_service, corosio::local_endpoint>; +public: + explicit kqueue_local_stream_acceptor_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_HAS_KQUEUE + +#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TYPES_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_udp_service.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_udp_service.hpp deleted file mode 100644 index 901d01647..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_udp_service.hpp +++ /dev/null @@ -1,303 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_UDP_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_UDP_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/** kqueue UDP service implementation. - - Inherits from udp_service to enable runtime polymorphism. - Uses key_type = udp_service for service lookup. -*/ -class BOOST_COROSIO_DECL kqueue_udp_service final - : public reactor_socket_service< - kqueue_udp_service, - udp_service, - kqueue_scheduler, - kqueue_udp_socket> -{ -public: - explicit kqueue_udp_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_datagram_socket( - udp_socket::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code - bind_datagram(udp_socket::implementation& impl, endpoint ep) override; -}; - -// Cancellation for connectionless ops - -inline void -kqueue_send_to_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_recv_from_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Cancellation for connected-mode ops - -inline void -kqueue_udp_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_send_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -kqueue_recv_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Completion handlers - -inline void -kqueue_datagram_op::operator()() -{ - complete_io_op(*this); -} - -inline void -kqueue_recv_from_op::operator()() -{ - complete_datagram_op(*this, this->source_out); -} - -inline void -kqueue_udp_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline void -kqueue_recv_op::operator()() -{ - complete_io_op(*this); -} - -// Socket construction/destruction - -inline kqueue_udp_socket::kqueue_udp_socket(kqueue_udp_service& svc) noexcept - : reactor_datagram_socket(svc) -{ -} - -inline kqueue_udp_socket::~kqueue_udp_socket() = default; - -// Connectionless I/O - -inline std::coroutine_handle<> -kqueue_udp_socket::send_to( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - endpoint dest, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_send_to(h, ex, buf, dest, flags, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -kqueue_udp_socket::recv_from( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - endpoint* source, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv_from(h, ex, buf, source, flags, token, ec, bytes_out); -} - -// Connected-mode I/O - -inline std::coroutine_handle<> -kqueue_udp_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - return do_connect(h, ex, ep, token, ec); -} - -inline std::coroutine_handle<> -kqueue_udp_socket::send( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_send(h, ex, buf, flags, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -kqueue_udp_socket::recv( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv(h, ex, buf, flags, token, ec, bytes_out); -} - -inline endpoint -kqueue_udp_socket::remote_endpoint() const noexcept -{ - return reactor_datagram_socket::remote_endpoint(); -} - -inline void -kqueue_udp_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -kqueue_udp_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline std::error_code -kqueue_udp_service::open_datagram_socket( - udp_socket::implementation& impl, int family, int type, int protocol) -{ - auto* kq_impl = static_cast(&impl); - kq_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - if (family == AF_INET6) - { - int v6only = 1; - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)); - } - - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - // SO_NOSIGPIPE on macOS (where MSG_NOSIGNAL doesn't exist) -#ifdef SO_NOSIGPIPE - { - int one = 1; - ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - } -#endif - - kq_impl->fd_ = fd; - - kq_impl->desc_state_.fd = fd; - { - std::lock_guard lock(kq_impl->desc_state_.mutex); - kq_impl->desc_state_.read_op = nullptr; - kq_impl->desc_state_.write_op = nullptr; - kq_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &kq_impl->desc_state_); - - return {}; -} - -inline std::error_code -kqueue_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_UDP_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/kqueue/kqueue_udp_socket.hpp b/include/boost/corosio/native/detail/kqueue/kqueue_udp_socket.hpp deleted file mode 100644 index edab923a2..000000000 --- a/include/boost/corosio/native/detail/kqueue/kqueue_udp_socket.hpp +++ /dev/null @@ -1,140 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_UDP_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_UDP_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_KQUEUE - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class kqueue_udp_service; -class kqueue_udp_socket; - -/// kqueue datagram base operation. -struct kqueue_datagram_op : reactor_op -{ - void operator()() override; -}; - -/// kqueue send_to operation. -struct kqueue_send_to_op final : reactor_send_to_op -{ - void cancel() noexcept override; -}; - -/// kqueue recv_from operation. -struct kqueue_recv_from_op final : reactor_recv_from_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// kqueue connect operation for UDP. -struct kqueue_udp_connect_op final : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// kqueue connected send operation. -struct kqueue_send_op final : reactor_send_op -{ - void cancel() noexcept override; -}; - -/// kqueue connected recv operation. -struct kqueue_recv_op final : reactor_recv_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Datagram socket implementation for kqueue backend. -class kqueue_udp_socket final - : public reactor_datagram_socket< - kqueue_udp_socket, - kqueue_udp_service, - kqueue_udp_connect_op, - kqueue_send_to_op, - kqueue_recv_from_op, - kqueue_send_op, - kqueue_recv_op, - descriptor_state> -{ - friend class kqueue_udp_service; - -public: - explicit kqueue_udp_socket(kqueue_udp_service& svc) noexcept; - ~kqueue_udp_socket() override; - - std::coroutine_handle<> send_to( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - endpoint, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv_from( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - endpoint*, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> send( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - endpoint remote_endpoint() const noexcept override; - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_KQUEUE - -#endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_UDP_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/reactor/reactor_acceptor.hpp b/include/boost/corosio/native/detail/reactor/reactor_acceptor.hpp index 213f729cb..1c5847da4 100644 --- a/include/boost/corosio/native/detail/reactor/reactor_acceptor.hpp +++ b/include/boost/corosio/native/detail/reactor/reactor_acceptor.hpp @@ -59,6 +59,8 @@ class reactor_acceptor { friend Derived; +protected: + // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) explicit reactor_acceptor(Service& svc) noexcept : svc_(svc) {} protected: @@ -124,12 +126,28 @@ class reactor_acceptor local_endpoint_ = std::move(ep); } + /// Assign the fd and initialize descriptor state for the acceptor. + void init_acceptor_fd(int fd) noexcept + { + fd_ = fd; + desc_state_.fd = fd; + { + std::lock_guard lock(desc_state_.mutex); + desc_state_.read_op = nullptr; + } + } + /// Return a reference to the owning service. Service& service() noexcept { return svc_; } + void cancel() noexcept override { do_cancel(); } + + /// Close the acceptor (non-virtual, called by the service). + void close_socket() noexcept { do_close_socket(); } + /** Cancel a single pending operation. Claims the operation from the read_op descriptor slot @@ -139,10 +157,7 @@ class reactor_acceptor */ void cancel_single_op(Op& op) noexcept; - /** Cancel the pending accept operation. - - Invoked by the derived class's cancel() override. - */ + /** Cancel the pending accept operation. */ void do_cancel() noexcept; /** Close the acceptor and cancel pending operations. diff --git a/include/boost/corosio/native/detail/reactor/reactor_acceptor_service.hpp b/include/boost/corosio/native/detail/reactor/reactor_acceptor_service.hpp index 27f893a70..daf8a5ae2 100644 --- a/include/boost/corosio/native/detail/reactor/reactor_acceptor_service.hpp +++ b/include/boost/corosio/native/detail/reactor/reactor_acceptor_service.hpp @@ -47,6 +47,8 @@ class reactor_acceptor_service : public ServiceBase friend Derived; using state_type = reactor_service_state; +protected: + // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) explicit reactor_acceptor_service(capy::execution_context& ctx) : ctx_(ctx) , state_( diff --git a/include/boost/corosio/native/detail/reactor/reactor_backend.hpp b/include/boost/corosio/native/detail/reactor/reactor_backend.hpp new file mode 100644 index 000000000..74674f5b2 --- /dev/null +++ b/include/boost/corosio/native/detail/reactor/reactor_backend.hpp @@ -0,0 +1,150 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_BACKEND_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_BACKEND_HPP + +/* Reactor backend: acceptor accept() implementation. + + Contains the accept() method body for reactor_acceptor_impl, + which needs all socket/service types to be complete. Included + by per-backend type files (epoll_types.hpp, etc.) after all + named types are defined. +*/ + +#include +#include +#include +#include + +#include + +namespace boost::corosio::detail { + +// ============================================================ +// Acceptor accept() implementation +// ============================================================ + +template +std::coroutine_handle<> +reactor_acceptor_impl::accept( + std::coroutine_handle<> h, + capy::executor_ref ex, + std::stop_token token, + std::error_code* ec, + io_object::implementation** impl_out) +{ + auto& op = this->acc_; + op.reset(); + op.h = h; + op.ex = ex; + op.ec_out = ec; + op.impl_out = impl_out; + op.fd = this->fd_; + op.start(token, static_cast(this)); + + sockaddr_storage peer_storage{}; + socklen_t peer_addrlen = 0; + + int accepted = Traits::accept_policy::do_accept( + this->fd_, peer_storage, peer_addrlen); + + if (accepted >= 0) + { + { + std::lock_guard lock(this->desc_state_.mutex); + this->desc_state_.read_ready = false; + } + + if (this->svc_.scheduler().try_consume_inline_budget()) + { + auto* socket_svc = this->svc_.stream_service(); + if (socket_svc) + { + auto& impl = + static_cast(*socket_svc->construct()); + impl.set_socket(accepted); + + impl.desc_state_.fd = accepted; + { + std::lock_guard lock(impl.desc_state_.mutex); + impl.desc_state_.read_op = nullptr; + impl.desc_state_.write_op = nullptr; + impl.desc_state_.connect_op = nullptr; + } + socket_svc->scheduler().register_descriptor( + accepted, &impl.desc_state_); + + impl.set_endpoints( + this->local_endpoint_, + from_sockaddr_as( + peer_storage, peer_addrlen, Endpoint{})); + + *ec = {}; + if (impl_out) + *impl_out = &impl; + } + else + { + ::close(accepted); + *ec = make_err(ENOENT); + if (impl_out) + *impl_out = nullptr; + } + op.cont_op.cont.h = h; + return dispatch_coro(ex, op.cont_op.cont); + } + + op.accepted_fd = accepted; + op.peer_storage = peer_storage; + op.peer_addrlen = peer_addrlen; + op.complete(0, 0); + op.impl_ptr = this->shared_from_this(); + this->svc_.post(&op); + return std::noop_coroutine(); + } + + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + op.impl_ptr = this->shared_from_this(); + this->svc_.work_started(); + + std::lock_guard lock(this->desc_state_.mutex); + bool io_done = false; + if (this->desc_state_.read_ready) + { + this->desc_state_.read_ready = false; + op.perform_io(); + io_done = (op.errn != EAGAIN && op.errn != EWOULDBLOCK); + if (!io_done) + op.errn = 0; + } + + if (io_done || op.cancelled.load(std::memory_order_acquire)) + { + this->svc_.post(&op); + this->svc_.work_finished(); + } + else + { + this->desc_state_.read_op = &op; + } + return std::noop_coroutine(); + } + + op.complete(errno, 0); + op.impl_ptr = this->shared_from_this(); + this->svc_.post(&op); + return std::noop_coroutine(); +} + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_BACKEND_HPP diff --git a/include/boost/corosio/native/detail/reactor/reactor_basic_socket.hpp b/include/boost/corosio/native/detail/reactor/reactor_basic_socket.hpp index 12c12cbd6..4ea2c2d14 100644 --- a/include/boost/corosio/native/detail/reactor/reactor_basic_socket.hpp +++ b/include/boost/corosio/native/detail/reactor/reactor_basic_socket.hpp @@ -157,6 +157,20 @@ class reactor_basic_socket return {}; } + /// Assign the fd, initialize descriptor state, and register with the reactor. + void init_and_register(int fd) noexcept + { + fd_ = fd; + desc_state_.fd = fd; + { + std::lock_guard lock(desc_state_.mutex); + desc_state_.read_op = nullptr; + desc_state_.write_op = nullptr; + desc_state_.connect_op = nullptr; + } + svc_.scheduler().register_descriptor(fd, &desc_state_); + } + /** Register an op with the reactor. Handles cached edge events and deferred cancellation. @@ -168,7 +182,8 @@ class reactor_basic_socket Op& op, reactor_op_base*& desc_slot, bool& ready_flag, - bool& cancel_flag) noexcept; + bool& cancel_flag, + bool is_write_direction = false) noexcept; /** Cancel a single pending operation. @@ -216,7 +231,8 @@ reactor_basic_socket::register_ Op& op, reactor_op_base*& desc_slot, bool& ready_flag, - bool& cancel_flag) noexcept + bool& cancel_flag, + bool is_write_direction) noexcept { svc_.work_started(); @@ -245,6 +261,18 @@ reactor_basic_socket::register_ else { desc_slot = &op; + + // Select must rebuild its fd_sets when a write-direction op + // is parked, so select() watches for writability. Compiled + // away to nothing for epoll and kqueue. + if constexpr (requires { Service::needs_write_notification; }) + { + if constexpr (Service::needs_write_notification) + { + if (is_write_direction) + svc_.scheduler().notify_reactor(); + } + } } } diff --git a/include/boost/corosio/native/detail/reactor/reactor_datagram_ops.hpp b/include/boost/corosio/native/detail/reactor/reactor_datagram_ops.hpp new file mode 100644 index 000000000..e1909e955 --- /dev/null +++ b/include/boost/corosio/native/detail/reactor/reactor_datagram_ops.hpp @@ -0,0 +1,117 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_DATAGRAM_OPS_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_DATAGRAM_OPS_HPP + +#include +#include + +namespace boost::corosio::detail { + +/* Parameterized datagram op types for reactor backends. + + @tparam Traits Backend traits (epoll_traits, kqueue_traits, etc.) + @tparam Socket The concrete datagram socket type (forward-declared). + @tparam DummyAcc Acceptor type placeholder (datagrams have no acceptor). + @tparam Endpoint The endpoint type (endpoint or local_endpoint). +*/ + +template +struct reactor_dgram_base_op + : reactor_op +{ + void operator()() override; + void cancel() noexcept override; +}; + +template +struct reactor_dgram_connect_op final + : reactor_connect_op< + reactor_dgram_base_op, + Endpoint> +{ + void operator()() override; +}; + +template +struct reactor_dgram_send_to_op final + : reactor_send_to_op< + reactor_dgram_base_op> +{ +}; + +template +struct reactor_dgram_recv_from_op final + : reactor_recv_from_op< + reactor_dgram_base_op, + Endpoint> +{ + void operator()() override; +}; + +template +struct reactor_dgram_send_op final + : reactor_send_op< + reactor_dgram_base_op> +{ +}; + +template +struct reactor_dgram_recv_op final + : reactor_recv_op< + reactor_dgram_base_op> +{ + void operator()() override; +}; + +// --- Deferred implementations --- + +template +void +reactor_dgram_base_op::operator()() +{ + complete_io_op(*this); +} + +template +void +reactor_dgram_base_op::cancel() noexcept +{ + if (this->socket_impl_) + this->socket_impl_->cancel_single_op(*this); + else + this->request_cancel(); +} + +template +void +reactor_dgram_connect_op::operator()() +{ + complete_connect_op(*this); +} + +template +void +reactor_dgram_recv_from_op::operator()() +{ + complete_datagram_op(*this, this->source_out); +} + +template +void +reactor_dgram_recv_op::operator()() +{ + // Datagram completion: zero-length datagrams are valid, not EOF. + complete_datagram_op(*this); +} + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_DATAGRAM_OPS_HPP diff --git a/include/boost/corosio/native/detail/reactor/reactor_datagram_socket.hpp b/include/boost/corosio/native/detail/reactor/reactor_datagram_socket.hpp index 6243c8182..fe64df443 100644 --- a/include/boost/corosio/native/detail/reactor/reactor_datagram_socket.hpp +++ b/include/boost/corosio/native/detail/reactor/reactor_datagram_socket.hpp @@ -11,6 +11,7 @@ #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_DATAGRAM_SOCKET_HPP #include +#include #include #include #include @@ -82,6 +83,8 @@ class reactor_datagram_socket friend base_type; friend Derived; +protected: + // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) explicit reactor_datagram_socket(Service& svc) noexcept : base_type(svc) {} protected: @@ -111,6 +114,81 @@ class reactor_datagram_socket return remote_endpoint_; } + // --- Virtual method overrides (satisfy ImplBase pure virtuals) --- + + std::coroutine_handle<> send_to( + std::coroutine_handle<> h, + capy::executor_ref ex, + buffer_param buf, + Endpoint dest, + int flags, + std::stop_token token, + std::error_code* ec, + std::size_t* bytes_out) override + { + return do_send_to(h, ex, buf, dest, flags, token, ec, bytes_out); + } + + std::coroutine_handle<> recv_from( + std::coroutine_handle<> h, + capy::executor_ref ex, + buffer_param buf, + Endpoint* source, + int flags, + std::stop_token token, + std::error_code* ec, + std::size_t* bytes_out) override + { + return do_recv_from(h, ex, buf, source, flags, token, ec, bytes_out); + } + + std::coroutine_handle<> connect( + std::coroutine_handle<> h, + capy::executor_ref ex, + Endpoint ep, + std::stop_token token, + std::error_code* ec) override + { + return do_connect(h, ex, ep, token, ec); + } + + std::coroutine_handle<> send( + std::coroutine_handle<> h, + capy::executor_ref ex, + buffer_param buf, + int flags, + std::stop_token token, + std::error_code* ec, + std::size_t* bytes_out) override + { + return do_send(h, ex, buf, flags, token, ec, bytes_out); + } + + std::coroutine_handle<> recv( + std::coroutine_handle<> h, + capy::executor_ref ex, + buffer_param buf, + int flags, + std::stop_token token, + std::error_code* ec, + std::size_t* bytes_out) override + { + return do_recv(h, ex, buf, flags, token, ec, bytes_out); + } + + void cancel() noexcept override + { + this->do_cancel(); + } + + // --- End virtual overrides --- + + /// Close the socket (non-virtual, called by the service). + void close_socket() noexcept + { + do_close_socket(); + } + /// Cache local and remote endpoints. void set_endpoints(Endpoint local, Endpoint remote) noexcept { @@ -399,7 +477,7 @@ reactor_datagram_socket< this->register_op( op, this->desc_state_.write_op, this->desc_state_.write_ready, - this->desc_state_.write_cancel_pending); + this->desc_state_.write_cancel_pending, true); return std::noop_coroutine(); } @@ -715,7 +793,7 @@ reactor_datagram_socket< this->register_op( op, this->desc_state_.write_op, this->desc_state_.write_ready, - this->desc_state_.write_cancel_pending); + this->desc_state_.write_cancel_pending, true); return std::noop_coroutine(); } diff --git a/include/boost/corosio/native/detail/reactor/reactor_op_complete.hpp b/include/boost/corosio/native/detail/reactor/reactor_op_complete.hpp index 17675bf89..8ab02749a 100644 --- a/include/boost/corosio/native/detail/reactor/reactor_op_complete.hpp +++ b/include/boost/corosio/native/detail/reactor/reactor_op_complete.hpp @@ -249,6 +249,37 @@ complete_accept_op(Op& op) recorded sockaddr_storage into the caller's endpoint pointer. Then resumes the caller via symmetric transfer. + @tparam Op The concrete datagram operation type. + @param op The operation to complete. +*/ +template +void +complete_datagram_op(Op& op) +{ + op.stop_cb.reset(); + op.socket_impl_->desc_state_.scheduler_->reset_inline_budget(); + + if (op.cancelled.load(std::memory_order_acquire)) + *op.ec_out = capy::error::canceled; + else if (op.errn != 0) + *op.ec_out = make_err(op.errn); + else + *op.ec_out = {}; + + *op.bytes_out = op.bytes_transferred; + + op.cont_op.cont.h = op.h; + capy::executor_ref saved_ex(op.ex); + auto prevent = std::move(op.impl_ptr); + dispatch_coro(saved_ex, op.cont_op.cont).resume(); +} + +/** Complete a datagram operation with source endpoint capture. + + For recv_from operations, writes the source endpoint from the + recorded sockaddr_storage into the caller's endpoint pointer. + Then resumes the caller via symmetric transfer. + @tparam Op The concrete datagram operation type. @param op The operation to complete. @param source_out Optional pointer to store source endpoint diff --git a/include/boost/corosio/native/detail/reactor/reactor_service_finals.hpp b/include/boost/corosio/native/detail/reactor/reactor_service_finals.hpp new file mode 100644 index 000000000..2581550a4 --- /dev/null +++ b/include/boost/corosio/native/detail/reactor/reactor_service_finals.hpp @@ -0,0 +1,404 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP + +/* Parameterized service implementation bases for reactor backends. + + One template per protocol (TCP, local stream, UDP, local datagram, + acceptor). Named per-backend classes (e.g. epoll_tcp_service) inherit + from these as final. The Derived parameter (CRTP) flows through to + reactor_socket_service so construct() creates the correct named type. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +namespace boost::corosio::detail { + +// ============================================================ +// Shared socket creation helpers +// ============================================================ + +template +std::error_code +do_open_socket( + SocketFinal* socket_impl, + int family, int type, int protocol, + bool is_ip) noexcept +{ + socket_impl->close_socket(); + + int fd = Traits::create_socket(family, type, protocol); + if (fd < 0) + return make_err(errno); + + std::error_code ec = is_ip + ? Traits::configure_ip_socket(fd, family) + : Traits::configure_local_socket(fd); + + if (ec) + { + ::close(fd); + return ec; + } + + socket_impl->init_and_register(fd); + return {}; +} + +template +std::error_code +do_assign_fd( + SocketFinal* socket_impl, + int fd, + int expected_type) noexcept +{ + if (fd < 0) + return make_err(EBADF); + + socket_impl->close_socket(); + + // Validate that fd is actually an AF_UNIX socket of the expected type. + { + sockaddr_storage st{}; + socklen_t st_len = sizeof(st); + if (::getsockname( + fd, reinterpret_cast(&st), &st_len) != 0) + return make_err(errno); + if (st.ss_family != AF_UNIX) + return make_err(EAFNOSUPPORT); + + int sock_type = 0; + socklen_t opt_len = sizeof(sock_type); + if (::getsockopt( + fd, SOL_SOCKET, SO_TYPE, &sock_type, &opt_len) != 0) + return make_err(errno); + if (sock_type != expected_type) + return make_err(EPROTOTYPE); + } + + // Adopt-only: do not mutate the caller's fd flags. Callers + // pass fds they have already configured (e.g., from socketpair + // or SCM_RIGHTS). Only non-mutating validation is performed. + if (auto ec = Traits::validate_assigned_fd(fd)) + return ec; + + socket_impl->init_and_register(fd); + + // Best-effort: refresh endpoint caches. + using endpoint_type = std::remove_cvref_t< + decltype(socket_impl->local_endpoint())>; + + endpoint_type local_ep{}; + sockaddr_storage local_storage{}; + socklen_t local_len = sizeof(local_storage); + if (::getsockname( + fd, reinterpret_cast(&local_storage), &local_len) == 0) + local_ep = from_sockaddr_as(local_storage, local_len, endpoint_type{}); + + endpoint_type remote_ep{}; + sockaddr_storage peer_storage{}; + socklen_t peer_len = sizeof(peer_storage); + if (::getpeername( + fd, reinterpret_cast(&peer_storage), &peer_len) == 0) + remote_ep = from_sockaddr_as(peer_storage, peer_len, endpoint_type{}); + + socket_impl->set_endpoints(local_ep, remote_ep); + + return {}; +} + +template +std::error_code +do_open_acceptor( + AccFinal* acc_impl, + int family, int type, int protocol, + bool is_ip) noexcept +{ + acc_impl->close_socket(); + + int fd = Traits::create_socket(family, type, protocol); + if (fd < 0) + return make_err(errno); + + std::error_code ec = is_ip + ? Traits::configure_ip_acceptor(fd, family) + : Traits::configure_local_socket(fd); + + if (ec) + { + ::close(fd); + return ec; + } + + acc_impl->init_acceptor_fd(fd); + return {}; +} + +// ============================================================ +// TCP service +// ============================================================ + +template +class reactor_tcp_service_impl + : public reactor_socket_service< + Derived, + tcp_service, + typename Traits::scheduler_type, + SocketFinal> +{ + using base_service = reactor_socket_service< + Derived, tcp_service, + typename Traits::scheduler_type, SocketFinal>; + friend Derived; + friend base_service; + + explicit reactor_tcp_service_impl(capy::execution_context& ctx) + : base_service(ctx) {} + +public: + static constexpr bool needs_write_notification = + Traits::needs_write_notification; + + std::error_code open_socket( + tcp_socket::implementation& impl, + int family, int type, int protocol) override + { + return do_open_socket( + static_cast(&impl), + family, type, protocol, true); + } + + std::error_code bind_socket( + tcp_socket::implementation& impl, endpoint ep) override + { + return static_cast(&impl)->do_bind(ep); + } + + void pre_shutdown(SocketFinal* impl) noexcept + { + impl->hook_.pre_shutdown(impl->native_handle()); + } + + void pre_destroy(SocketFinal* impl) noexcept + { + impl->hook_.pre_destroy(impl->native_handle()); + } +}; + +// ============================================================ +// Local stream service +// ============================================================ + +template +class reactor_local_stream_service_impl + : public reactor_socket_service< + Derived, + local_stream_service, + typename Traits::scheduler_type, + SocketFinal> +{ + using base_service = reactor_socket_service< + Derived, local_stream_service, + typename Traits::scheduler_type, SocketFinal>; + friend Derived; + friend base_service; + + explicit reactor_local_stream_service_impl(capy::execution_context& ctx) + : base_service(ctx) {} + +public: + static constexpr bool needs_write_notification = + Traits::needs_write_notification; + + std::error_code open_socket( + local_stream_socket::implementation& impl, + int family, int type, int protocol) override + { + return do_open_socket( + static_cast(&impl), + family, type, protocol, false); + } + + std::error_code assign_socket( + local_stream_socket::implementation& impl, int fd) override + { + return do_assign_fd( + static_cast(&impl), fd, SOCK_STREAM); + } +}; + +// ============================================================ +// UDP service +// ============================================================ + +template +class reactor_udp_service_impl + : public reactor_socket_service< + Derived, + udp_service, + typename Traits::scheduler_type, + SocketFinal> +{ + using base_service = reactor_socket_service< + Derived, udp_service, + typename Traits::scheduler_type, SocketFinal>; + friend Derived; + friend base_service; + + explicit reactor_udp_service_impl(capy::execution_context& ctx) + : base_service(ctx) {} + +public: + static constexpr bool needs_write_notification = + Traits::needs_write_notification; + + std::error_code open_datagram_socket( + udp_socket::implementation& impl, + int family, int type, int protocol) override + { + return do_open_socket( + static_cast(&impl), + family, type, protocol, true); + } + + std::error_code bind_datagram( + udp_socket::implementation& impl, endpoint ep) override + { + return static_cast(&impl)->do_bind(ep); + } +}; + +// ============================================================ +// Local datagram service +// ============================================================ + +template +class reactor_local_dgram_service_impl + : public reactor_socket_service< + Derived, + local_datagram_service, + typename Traits::scheduler_type, + SocketFinal> +{ + using base_service = reactor_socket_service< + Derived, local_datagram_service, + typename Traits::scheduler_type, SocketFinal>; + friend Derived; + friend base_service; + + explicit reactor_local_dgram_service_impl(capy::execution_context& ctx) + : base_service(ctx) {} + +public: + static constexpr bool needs_write_notification = + Traits::needs_write_notification; + + std::error_code open_socket( + local_datagram_socket::implementation& impl, + int family, int type, int protocol) override + { + return do_open_socket( + static_cast(&impl), + family, type, protocol, false); + } + + std::error_code assign_socket( + local_datagram_socket::implementation& impl, int fd) override + { + return do_assign_fd( + static_cast(&impl), fd, SOCK_DGRAM); + } + + std::error_code bind_socket( + local_datagram_socket::implementation& impl, + corosio::local_endpoint ep) override + { + return static_cast(&impl)->do_bind(ep); + } +}; + +// ============================================================ +// Acceptor service +// ============================================================ + +template +class reactor_acceptor_service_impl + : public reactor_acceptor_service< + Derived, + ServiceBase, + typename Traits::scheduler_type, + AccFinal, + StreamServiceFinal> +{ + using base_service = reactor_acceptor_service< + Derived, + ServiceBase, + typename Traits::scheduler_type, + AccFinal, + StreamServiceFinal>; + friend Derived; + friend base_service; + + explicit reactor_acceptor_service_impl(capy::execution_context& ctx) + : base_service(ctx) + { + // Look up the concrete stream service directly by its type. + this->stream_svc_ = + this->ctx_.template find_service(); + } + +public: + std::error_code open_acceptor_socket( + typename AccFinal::impl_base_type& impl, + int family, int type, int protocol) override + { + return do_open_acceptor( + static_cast(&impl), + family, type, protocol, + std::is_same_v); + } + + std::error_code bind_acceptor( + typename AccFinal::impl_base_type& impl, + Endpoint ep) override + { + return static_cast(&impl)->do_bind(ep); + } + + std::error_code listen_acceptor( + typename AccFinal::impl_base_type& impl, + int backlog) override + { + return static_cast(&impl)->do_listen(backlog); + } +}; + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP diff --git a/include/boost/corosio/native/detail/reactor/reactor_socket_finals.hpp b/include/boost/corosio/native/detail/reactor/reactor_socket_finals.hpp new file mode 100644 index 000000000..da28f6e4c --- /dev/null +++ b/include/boost/corosio/native/detail/reactor/reactor_socket_finals.hpp @@ -0,0 +1,190 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SOCKET_FINALS_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SOCKET_FINALS_HPP + +/* Parameterized socket, datagram, and acceptor implementation bases. + + Named per-backend classes (e.g. epoll_tcp_socket) inherit from + these templates, supplying the concrete service/peer types. The + per-backend type files ({backend}_types.hpp) define the final classes. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace boost::corosio::detail { + +// ============================================================ +// Stream socket implementation base +// ============================================================ + +/** Intermediate base for reactor stream sockets. + + Holds the per-socket hook (e.g., kqueue SO_LINGER tracking), + the set_option override, and the close/release shadows. + Named per-backend classes inherit from this as final. + + @tparam Derived The named final class (CRTP self). + @tparam Traits Backend traits (epoll_traits, etc.). + @tparam Service The concrete service type. + @tparam AcceptorType The concrete acceptor type (for op base). + @tparam ImplBase The public vtable base. + @tparam Endpoint endpoint or local_endpoint. +*/ +template +class reactor_stream_socket_impl + : public reactor_stream_socket< + Derived, + Service, + reactor_stream_connect_op, + reactor_stream_read_op, + reactor_stream_write_op, + typename Traits::desc_state_type, + ImplBase, + Endpoint> +{ + friend Derived; + friend Service; + + explicit reactor_stream_socket_impl(Service& svc) noexcept + : reactor_stream_socket_impl::reactor_stream_socket(svc) + { + } + +public: + using impl_base_type = ImplBase; + + // Per-socket hook state (e.g., kqueue SO_LINGER tracking). + [[no_unique_address]] typename Traits::stream_socket_hook hook_; + + ~reactor_stream_socket_impl() override = default; + + std::error_code set_option( + int level, int optname, + void const* data, std::size_t size) noexcept override + { + return hook_.on_set_option(this->fd_, level, optname, data, size); + } + + // Shadows reactor_stream_socket::close_socket so the hook fires on + // every fd close path. + void close_socket() noexcept + { + hook_.pre_shutdown(this->fd_); + this->do_close_socket(); + } +}; + +// ============================================================ +// Datagram socket implementation base +// ============================================================ + +/** Intermediate base for reactor datagram sockets. + + @tparam Derived The named final class (CRTP self). + @tparam Traits Backend traits. + @tparam Service The concrete datagram service type. + @tparam AcceptorType The concrete acceptor type (placeholder for op base). + @tparam ImplBase The public vtable base. + @tparam Endpoint endpoint or local_endpoint. +*/ +template +class reactor_dgram_socket_impl + : public reactor_datagram_socket< + Derived, + Service, + reactor_dgram_connect_op, + reactor_dgram_send_to_op, + reactor_dgram_recv_from_op, + reactor_dgram_send_op, + reactor_dgram_recv_op, + typename Traits::desc_state_type, + ImplBase, + Endpoint> +{ + friend Derived; + friend Service; + + explicit reactor_dgram_socket_impl(Service& svc) noexcept + : reactor_dgram_socket_impl::reactor_datagram_socket(svc) + { + } + +public: + using impl_base_type = ImplBase; + + ~reactor_dgram_socket_impl() override = default; +}; + +// ============================================================ +// Acceptor implementation base +// ============================================================ + +/** Intermediate base for reactor stream acceptors. + + @tparam Derived The named final class (CRTP self). + @tparam Traits Backend traits. + @tparam Service The concrete acceptor service type. + @tparam SocketFinal The concrete stream socket type (for accept). + @tparam AccImplBase The public vtable base. + @tparam Endpoint endpoint or local_endpoint. +*/ +template +class reactor_acceptor_impl + : public reactor_acceptor< + Derived, + Service, + reactor_stream_base_op, + reactor_stream_accept_op, + typename Traits::desc_state_type, + AccImplBase, + Endpoint> +{ + friend Derived; + friend Service; + + explicit reactor_acceptor_impl(Service& svc) noexcept + : reactor_acceptor_impl::reactor_acceptor(svc) + { + } + +public: + using impl_base_type = AccImplBase; + + ~reactor_acceptor_impl() override = default; + + std::coroutine_handle<> accept( + std::coroutine_handle<>, + capy::executor_ref, + std::stop_token, + std::error_code*, + io_object::implementation**) override; +}; + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SOCKET_FINALS_HPP diff --git a/include/boost/corosio/native/detail/reactor/reactor_socket_service.hpp b/include/boost/corosio/native/detail/reactor/reactor_socket_service.hpp index f391ee637..cdae31f6f 100644 --- a/include/boost/corosio/native/detail/reactor/reactor_socket_service.hpp +++ b/include/boost/corosio/native/detail/reactor/reactor_socket_service.hpp @@ -38,6 +38,8 @@ class reactor_socket_service : public ServiceBase friend Derived; using state_type = reactor_service_state; +protected: + // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) explicit reactor_socket_service(capy::execution_context& ctx) : state_( std::make_unique( diff --git a/include/boost/corosio/native/detail/reactor/reactor_stream_ops.hpp b/include/boost/corosio/native/detail/reactor/reactor_stream_ops.hpp new file mode 100644 index 000000000..f35941652 --- /dev/null +++ b/include/boost/corosio/native/detail/reactor/reactor_stream_ops.hpp @@ -0,0 +1,111 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_STREAM_OPS_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_STREAM_OPS_HPP + +#include +#include + +namespace boost::corosio::detail { + +/* Parameterized stream op types for reactor backends. + + Given a Traits type (providing write_policy, accept_policy) and + forward-declared Socket/Acceptor types, generates all the concrete + op types needed for a stream socket. The cancel() and operator()() + bodies reference Socket/Acceptor but are only instantiated when the + vtable of a derived type is emitted -- at which point those types + are complete. + + @tparam Traits Backend traits (epoll_traits, kqueue_traits, etc.) + @tparam Socket The concrete stream socket type (forward-declared). + @tparam Acceptor The concrete stream acceptor type (forward-declared). + @tparam Endpoint The endpoint type (endpoint or local_endpoint). +*/ + +template +struct reactor_stream_base_op + : reactor_op +{ + void operator()() override; + void cancel() noexcept override; +}; + +template +struct reactor_stream_connect_op final + : reactor_connect_op< + reactor_stream_base_op, + Endpoint> +{ + void operator()() override; +}; + +template +struct reactor_stream_read_op final + : reactor_read_op< + reactor_stream_base_op> +{ +}; + +template +struct reactor_stream_write_op final + : reactor_write_op< + reactor_stream_base_op, + typename Traits::write_policy> +{ +}; + +template +struct reactor_stream_accept_op final + : reactor_accept_op< + reactor_stream_base_op, + typename Traits::accept_policy> +{ + void operator()() override; +}; + +// --- Deferred implementations (instantiated when Socket/Acceptor are complete) --- + +template +void +reactor_stream_base_op::operator()() +{ + complete_io_op(*this); +} + +template +void +reactor_stream_base_op::cancel() noexcept +{ + if (this->socket_impl_) + this->socket_impl_->cancel_single_op(*this); + else if (this->acceptor_impl_) + this->acceptor_impl_->cancel_single_op(*this); + else + this->request_cancel(); +} + +template +void +reactor_stream_connect_op::operator()() +{ + complete_connect_op(*this); +} + +template +void +reactor_stream_accept_op::operator()() +{ + complete_accept_op(*this); +} + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_STREAM_OPS_HPP diff --git a/include/boost/corosio/native/detail/reactor/reactor_stream_socket.hpp b/include/boost/corosio/native/detail/reactor/reactor_stream_socket.hpp index e14dc9f51..d96e350ee 100644 --- a/include/boost/corosio/native/detail/reactor/reactor_stream_socket.hpp +++ b/include/boost/corosio/native/detail/reactor/reactor_stream_socket.hpp @@ -11,6 +11,7 @@ #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_STREAM_SOCKET_HPP #include +#include #include #include #include @@ -66,6 +67,8 @@ class reactor_stream_socket friend base_type; friend Derived; +protected: + // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) explicit reactor_stream_socket(Service& svc) noexcept : base_type(svc) {} protected: @@ -89,10 +92,60 @@ class reactor_stream_socket return remote_endpoint_; } - /** Shut down part or all of the full-duplex connection. + // --- Virtual method overrides (satisfy ImplBase pure virtuals) --- + + std::coroutine_handle<> connect( + std::coroutine_handle<> h, + capy::executor_ref ex, + Endpoint ep, + std::stop_token token, + std::error_code* ec) override + { + return do_connect(h, ex, ep, token, ec); + } + + std::coroutine_handle<> read_some( + std::coroutine_handle<> h, + capy::executor_ref ex, + buffer_param param, + std::stop_token token, + std::error_code* ec, + std::size_t* bytes_out) override + { + return do_read_some(h, ex, param, token, ec, bytes_out); + } + + std::coroutine_handle<> write_some( + std::coroutine_handle<> h, + capy::executor_ref ex, + buffer_param param, + std::stop_token token, + std::error_code* ec, + std::size_t* bytes_out) override + { + return do_write_some(h, ex, param, token, ec, bytes_out); + } + + std::error_code + shutdown(corosio::shutdown_type what) noexcept override + { + return do_shutdown(static_cast(what)); + } - Not an override — concrete backend classes forward their - ImplBase-typed shutdown() here. + void cancel() noexcept override + { + this->do_cancel(); + } + + // --- End virtual overrides --- + + /// Close the socket (non-virtual, called by the service). + void close_socket() noexcept + { + this->do_close_socket(); + } + + /** Shut down part or all of the full-duplex connection. @param what 0 = receive, 1 = send, 2 = both. */ @@ -292,7 +345,7 @@ reactor_stream_socketregister_op( op, this->desc_state_.connect_op, this->desc_state_.write_ready, - this->desc_state_.connect_cancel_pending); + this->desc_state_.connect_cancel_pending, true); return std::noop_coroutine(); } @@ -475,7 +528,7 @@ reactor_stream_socketregister_op( op, this->desc_state_.write_op, this->desc_state_.write_ready, - this->desc_state_.write_cancel_pending); + this->desc_state_.write_cancel_pending, true); return std::noop_coroutine(); } diff --git a/include/boost/corosio/native/detail/select/select_local_datagram_service.hpp b/include/boost/corosio/native/detail/select/select_local_datagram_service.hpp deleted file mode 100644 index df7d43aa0..000000000 --- a/include/boost/corosio/native/detail/select/select_local_datagram_service.hpp +++ /dev/null @@ -1,348 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_DATAGRAM_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_DATAGRAM_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class BOOST_COROSIO_DECL select_local_datagram_service final - : public reactor_socket_service< - select_local_datagram_service, - local_datagram_service, - select_scheduler, - select_local_datagram_socket> -{ -public: - explicit select_local_datagram_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - local_datagram_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code assign_socket( - local_datagram_socket::implementation& impl, - native_handle_type fd) override; - - std::error_code bind_socket( - local_datagram_socket::implementation& impl, - corosio::local_endpoint ep) override; -}; - -// Cancellation for connectionless ops - -inline void -select_local_send_to_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_local_recv_from_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Cancellation for connected-mode ops - -inline void -select_local_dgram_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_local_dgram_send_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_local_dgram_recv_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Completion handlers - -inline void -select_local_datagram_op::operator()() -{ - complete_io_op(*this); -} - -inline void -select_local_recv_from_op::operator()() -{ - complete_datagram_op(*this, this->source_out); -} - -inline void -select_local_dgram_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline void -select_local_dgram_recv_op::operator()() -{ - // Datagram completion: do not map 0 bytes to EOF - complete_dgram_recv_op(*this); -} - -// Socket construction/destruction - -inline select_local_datagram_socket::select_local_datagram_socket( - select_local_datagram_service& svc) noexcept - : reactor_datagram_socket(svc) -{ -} - -inline select_local_datagram_socket::~select_local_datagram_socket() = default; - -// Connectionless I/O - -inline std::coroutine_handle<> -select_local_datagram_socket::send_to( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - corosio::local_endpoint dest, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - auto result = do_send_to(h, ex, buf, dest, flags, token, ec, bytes_out); - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline std::coroutine_handle<> -select_local_datagram_socket::recv_from( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - corosio::local_endpoint* source, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv_from(h, ex, buf, source, flags, token, ec, bytes_out); -} - -// Connected-mode I/O - -inline std::coroutine_handle<> -select_local_datagram_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - corosio::local_endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - auto result = do_connect(h, ex, ep, token, ec); - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline std::coroutine_handle<> -select_local_datagram_socket::send( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - auto result = do_send(h, ex, buf, flags, token, ec, bytes_out); - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline std::coroutine_handle<> -select_local_datagram_socket::recv( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv(h, ex, buf, flags, token, ec, bytes_out); -} - -inline void -select_local_datagram_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -select_local_datagram_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -select_local_datagram_socket::release_socket() noexcept -{ - return this->do_release_socket(); -} - -// Service implementations - -inline std::error_code -select_local_datagram_service::open_socket( - local_datagram_socket::implementation& impl, - int family, - int type, - int protocol) -{ - auto* select_impl = static_cast(&impl); - select_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - -#ifdef SO_NOSIGPIPE - { - int one = 1; - ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - } -#endif - - if (fd >= FD_SETSIZE) - { - ::close(fd); - return make_err(EMFILE); - } - - select_impl->fd_ = fd; - - select_impl->desc_state_.fd = fd; - { - std::lock_guard lock(select_impl->desc_state_.mutex); - select_impl->desc_state_.read_op = nullptr; - select_impl->desc_state_.write_op = nullptr; - select_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &select_impl->desc_state_); - - return {}; -} - -inline std::error_code -select_local_datagram_service::assign_socket( - local_datagram_socket::implementation& impl, - native_handle_type fd) -{ - if (fd < 0 || fd >= FD_SETSIZE) - return make_err(fd < 0 ? EBADF : EMFILE); - - auto* select_impl = static_cast(&impl); - select_impl->close_socket(); - - select_impl->fd_ = fd; - - select_impl->desc_state_.fd = fd; - { - std::lock_guard lock(select_impl->desc_state_.mutex); - select_impl->desc_state_.read_op = nullptr; - select_impl->desc_state_.write_op = nullptr; - select_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &select_impl->desc_state_); - - return {}; -} - -inline std::error_code -select_local_datagram_service::bind_socket( - local_datagram_socket::implementation& impl, - corosio::local_endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_DATAGRAM_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/select/select_local_datagram_socket.hpp b/include/boost/corosio/native/detail/select/select_local_datagram_socket.hpp deleted file mode 100644 index 55e0e88d0..000000000 --- a/include/boost/corosio/native/detail/select/select_local_datagram_socket.hpp +++ /dev/null @@ -1,166 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_DATAGRAM_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_DATAGRAM_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -// Forward declarations -class select_local_datagram_service; -class select_local_datagram_socket; - -/// Base operation for local datagram sockets on select. -struct select_local_datagram_op - : reactor_op -{ - void operator()() override; -}; - -/// Connect operation for local datagram sockets. -struct select_local_dgram_connect_op final - : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Send-to operation for local datagram sockets. -struct select_local_send_to_op final - : reactor_send_to_op -{ - void cancel() noexcept override; -}; - -/// Recv-from operation for local datagram sockets. -struct select_local_recv_from_op final - : reactor_recv_from_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Connected send operation for local datagram sockets. -struct select_local_dgram_send_op final - : reactor_send_op -{ - void cancel() noexcept override; -}; - -/// Connected recv operation for local datagram sockets. -struct select_local_dgram_recv_op final - : reactor_recv_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Datagram socket implementation for local (Unix) sockets on select. -class select_local_datagram_socket final - : public reactor_datagram_socket< - select_local_datagram_socket, - select_local_datagram_service, - select_local_dgram_connect_op, - select_local_send_to_op, - select_local_recv_from_op, - select_local_dgram_send_op, - select_local_dgram_recv_op, - select_descriptor_state, - local_datagram_socket::implementation, - corosio::local_endpoint> -{ - friend class select_local_datagram_service; - -public: - explicit select_local_datagram_socket( - select_local_datagram_service& svc) noexcept; - ~select_local_datagram_socket() override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - corosio::local_endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> send_to( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - corosio::local_endpoint, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv_from( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - corosio::local_endpoint*, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> send( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::error_code shutdown( - local_datagram_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - std::error_code bind(corosio::local_endpoint ep) noexcept override - { - return do_bind(ep); - } - - corosio::local_endpoint remote_endpoint() const noexcept override - { - return reactor_datagram_socket::remote_endpoint(); - } - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_DATAGRAM_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/select/select_local_stream_acceptor.hpp b/include/boost/corosio/native/detail/select/select_local_stream_acceptor.hpp deleted file mode 100644 index 52ee10054..000000000 --- a/include/boost/corosio/native/detail/select/select_local_stream_acceptor.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_ACCEPTOR_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_ACCEPTOR_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class select_local_stream_acceptor_service; - -/// Acceptor implementation for local stream sockets on select. -class select_local_stream_acceptor final - : public reactor_acceptor< - select_local_stream_acceptor, - select_local_stream_acceptor_service, - select_local_stream_op, - select_local_accept_op, - select_descriptor_state, - local_stream_acceptor::implementation, - local_endpoint> -{ - friend class select_local_stream_acceptor_service; - -public: - explicit select_local_stream_acceptor( - select_local_stream_acceptor_service& svc) noexcept; - - std::coroutine_handle<> accept( - std::coroutine_handle<>, - capy::executor_ref, - std::stop_token, - std::error_code*, - io_object::implementation**) override; - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_ACCEPTOR_HPP diff --git a/include/boost/corosio/native/detail/select/select_local_stream_acceptor_service.hpp b/include/boost/corosio/native/detail/select/select_local_stream_acceptor_service.hpp deleted file mode 100644 index 5adcb1cc0..000000000 --- a/include/boost/corosio/native/detail/select/select_local_stream_acceptor_service.hpp +++ /dev/null @@ -1,371 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/* select local stream acceptor service implementation. - - Inherits from local_stream_acceptor_service to enable runtime - polymorphism. Uses key_type = local_stream_acceptor_service - for service lookup. -*/ -class BOOST_COROSIO_DECL select_local_stream_acceptor_service final - : public reactor_acceptor_service< - select_local_stream_acceptor_service, - local_stream_acceptor_service, - select_scheduler, - select_local_stream_acceptor, - select_local_stream_service> -{ - using base_type = reactor_acceptor_service< - select_local_stream_acceptor_service, - local_stream_acceptor_service, - select_scheduler, - select_local_stream_acceptor, - select_local_stream_service>; - friend base_type; - -public: - explicit select_local_stream_acceptor_service( - capy::execution_context& ctx); - ~select_local_stream_acceptor_service() override; - - std::error_code open_acceptor_socket( - local_stream_acceptor::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code bind_acceptor( - local_stream_acceptor::implementation& impl, - corosio::local_endpoint ep) override; - std::error_code listen_acceptor( - local_stream_acceptor::implementation& impl, - int backlog) override; -}; - -inline void -select_local_accept_op::cancel() noexcept -{ - if (acceptor_impl_) - acceptor_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_local_accept_op::operator()() -{ - complete_accept_op(*this); -} - -inline select_local_stream_acceptor::select_local_stream_acceptor( - select_local_stream_acceptor_service& svc) noexcept - : reactor_acceptor(svc) -{ -} - -inline std::coroutine_handle<> -select_local_stream_acceptor::accept( - std::coroutine_handle<> h, - capy::executor_ref ex, - std::stop_token token, - std::error_code* ec, - io_object::implementation** impl_out) -{ - auto& op = acc_; - op.reset(); - op.h = h; - op.ex = ex; - op.ec_out = ec; - op.impl_out = impl_out; - op.fd = fd_; - op.start(token, this); - - sockaddr_storage peer_storage{}; - socklen_t addrlen; - int accepted; - do - { - addrlen = sizeof(peer_storage); - accepted = - ::accept(fd_, reinterpret_cast(&peer_storage), &addrlen); - } - while (accepted < 0 && errno == EINTR); - - if (accepted >= 0) - { - if (accepted >= FD_SETSIZE) - { - ::close(accepted); - op.complete(EMFILE, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - int flags = ::fcntl(accepted, F_GETFL, 0); - if (flags == -1) - { - int err = errno; - ::close(accepted); - op.complete(err, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (::fcntl(accepted, F_SETFL, flags | O_NONBLOCK) == -1) - { - int err = errno; - ::close(accepted); - op.complete(err, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (::fcntl(accepted, F_SETFD, FD_CLOEXEC) == -1) - { - int err = errno; - ::close(accepted); - op.complete(err, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - -#ifdef SO_NOSIGPIPE - { - int one = 1; - ::setsockopt( - accepted, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - } -#endif - - { - std::lock_guard lock(desc_state_.mutex); - desc_state_.read_ready = false; - } - - if (svc_.scheduler().try_consume_inline_budget()) - { - auto* socket_svc = svc_.stream_service(); - if (socket_svc) - { - auto& impl = - static_cast( - *socket_svc->construct()); - impl.set_socket(accepted); - - impl.desc_state_.fd = accepted; - { - std::lock_guard lock(impl.desc_state_.mutex); - impl.desc_state_.read_op = nullptr; - impl.desc_state_.write_op = nullptr; - impl.desc_state_.connect_op = nullptr; - } - socket_svc->scheduler().register_descriptor( - accepted, &impl.desc_state_); - - impl.set_endpoints( - local_endpoint_, - from_sockaddr_as(peer_storage, addrlen, corosio::local_endpoint{})); - - *ec = {}; - if (impl_out) - *impl_out = &impl; - } - else - { - ::close(accepted); - *ec = make_err(ENOENT); - if (impl_out) - *impl_out = nullptr; - } - op.cont_op.cont.h = h; - return dispatch_coro(ex, op.cont_op.cont); - } - - op.accepted_fd = accepted; - op.peer_storage = peer_storage; - op.peer_addrlen = addrlen; - op.complete(0, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - op.impl_ptr = shared_from_this(); - svc_.work_started(); - - std::lock_guard lock(desc_state_.mutex); - bool io_done = false; - if (desc_state_.read_ready) - { - desc_state_.read_ready = false; - op.perform_io(); - io_done = (op.errn != EAGAIN && op.errn != EWOULDBLOCK); - if (!io_done) - op.errn = 0; - } - - if (io_done || op.cancelled.load(std::memory_order_acquire)) - { - svc_.post(&op); - svc_.work_finished(); - } - else - { - desc_state_.read_op = &op; - } - return std::noop_coroutine(); - } - - op.complete(errno, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); -} - -inline void -select_local_stream_acceptor::cancel() noexcept -{ - do_cancel(); -} - -inline void -select_local_stream_acceptor::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -select_local_stream_acceptor::release_socket() noexcept -{ - return this->do_release_socket(); -} - -inline select_local_stream_acceptor_service:: - select_local_stream_acceptor_service(capy::execution_context& ctx) - : base_type(ctx) -{ - auto* svc = ctx_.find_service(); - stream_svc_ = svc - ? dynamic_cast(svc) - : nullptr; - assert(stream_svc_ && - "local_stream_service must be registered before acceptor service"); -} - -inline select_local_stream_acceptor_service:: - ~select_local_stream_acceptor_service() {} - -inline std::error_code -select_local_stream_acceptor_service::open_acceptor_socket( - local_stream_acceptor::implementation& impl, - int family, - int type, - int protocol) -{ - auto* select_impl = static_cast(&impl); - select_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - if (fd >= FD_SETSIZE) - { - ::close(fd); - return make_err(EMFILE); - } - - select_impl->fd_ = fd; - - // Set up descriptor state but do NOT register with select yet - // (registration happens in do_listen via reactor_acceptor base) - select_impl->desc_state_.fd = fd; - { - std::lock_guard lock(select_impl->desc_state_.mutex); - select_impl->desc_state_.read_op = nullptr; - } - - return {}; -} - -inline std::error_code -select_local_stream_acceptor_service::bind_acceptor( - local_stream_acceptor::implementation& impl, corosio::local_endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -inline std::error_code -select_local_stream_acceptor_service::listen_acceptor( - local_stream_acceptor::implementation& impl, int backlog) -{ - return static_cast(&impl)->do_listen(backlog); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_ACCEPTOR_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/select/select_local_stream_service.hpp b/include/boost/corosio/native/detail/select/select_local_stream_service.hpp deleted file mode 100644 index 049a84212..000000000 --- a/include/boost/corosio/native/detail/select/select_local_stream_service.hpp +++ /dev/null @@ -1,274 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - Each I/O op tries the syscall speculatively; only registers with - the reactor on EAGAIN. Fd is registered once at open time and - stays registered until close. The reactor only marks ready_events_; - actual I/O happens in invoke_deferred_io(). cancel() captures - shared_from_this() into op.impl_ptr to keep the impl alive. -*/ - -namespace boost::corosio::detail { - -class BOOST_COROSIO_DECL select_local_stream_service final - : public reactor_socket_service< - select_local_stream_service, - local_stream_service, - select_scheduler, - select_local_stream_socket> -{ -public: - explicit select_local_stream_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - local_stream_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code assign_socket( - local_stream_socket::implementation& impl, - native_handle_type fd) override; -}; - -// Op implementations - -inline void -select_local_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_local_read_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_local_write_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_local_stream_op::operator()() -{ - complete_io_op(*this); -} - -inline void -select_local_connect_op::operator()() -{ - complete_connect_op(*this); -} - -// Socket implementations - -inline select_local_stream_socket::select_local_stream_socket( - select_local_stream_service& svc) noexcept - : reactor_stream_socket(svc) -{ -} - -inline select_local_stream_socket::~select_local_stream_socket() = default; - -inline std::coroutine_handle<> -select_local_stream_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - corosio::local_endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - auto result = do_connect(h, ex, ep, token, ec); - // Rebuild fd_sets so select() watches for writability - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline std::coroutine_handle<> -select_local_stream_socket::read_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_read_some(h, ex, param, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -select_local_stream_socket::write_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - auto result = do_write_some(h, ex, param, token, ec, bytes_out); - // Rebuild fd_sets so select() watches for writability - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline void -select_local_stream_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -select_local_stream_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline native_handle_type -select_local_stream_socket::release_socket() noexcept -{ - return this->do_release_socket(); -} - -// Service implementations - -inline std::error_code -select_local_stream_service::open_socket( - local_stream_socket::implementation& impl, - int family, - int type, - int protocol) -{ - auto* select_impl = static_cast(&impl); - select_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - -#ifdef SO_NOSIGPIPE - { - int one = 1; - ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - } -#endif - - if (fd >= FD_SETSIZE) - { - ::close(fd); - return make_err(EMFILE); - } - - select_impl->fd_ = fd; - - select_impl->desc_state_.fd = fd; - { - std::lock_guard lock(select_impl->desc_state_.mutex); - select_impl->desc_state_.read_op = nullptr; - select_impl->desc_state_.write_op = nullptr; - select_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &select_impl->desc_state_); - - return {}; -} - -inline std::error_code -select_local_stream_service::assign_socket( - local_stream_socket::implementation& impl, - native_handle_type fd) -{ - if (fd < 0 || fd >= FD_SETSIZE) - return make_err(fd < 0 ? EBADF : EMFILE); - - auto* select_impl = static_cast(&impl); - select_impl->close_socket(); - - select_impl->fd_ = fd; - - select_impl->desc_state_.fd = fd; - { - std::lock_guard lock(select_impl->desc_state_.mutex); - select_impl->desc_state_.read_op = nullptr; - select_impl->desc_state_.write_op = nullptr; - select_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &select_impl->desc_state_); - - return {}; -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/select/select_local_stream_socket.hpp b/include/boost/corosio/native/detail/select/select_local_stream_socket.hpp deleted file mode 100644 index 156729b03..000000000 --- a/include/boost/corosio/native/detail/select/select_local_stream_socket.hpp +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -// Forward declarations -class select_local_stream_service; -class select_local_stream_socket; -class select_local_stream_acceptor; - -/// Base operation for local stream sockets on select. -struct select_local_stream_op - : reactor_op -{ - void operator()() override; -}; - -/// Connect operation for local stream sockets. -struct select_local_connect_op final - : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Scatter-read operation for local stream sockets. -struct select_local_read_op final : reactor_read_op -{ - void cancel() noexcept override; -}; - -/// Gather-write operation for local stream sockets. -struct select_local_write_op final - : reactor_write_op -{ - void cancel() noexcept override; -}; - -/// Accept operation for local stream sockets. -struct select_local_accept_op final - : reactor_accept_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Stream socket implementation for local (Unix) sockets on select. -class select_local_stream_socket final - : public reactor_stream_socket< - select_local_stream_socket, - select_local_stream_service, - select_local_connect_op, - select_local_read_op, - select_local_write_op, - select_descriptor_state, - local_stream_socket::implementation, - corosio::local_endpoint> -{ - friend class select_local_stream_service; - -public: - explicit select_local_stream_socket( - select_local_stream_service& svc) noexcept; - ~select_local_stream_socket() override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - corosio::local_endpoint, - std::stop_token, - std::error_code*) override; - - std::error_code shutdown( - local_stream_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - std::coroutine_handle<> read_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> write_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - void cancel() noexcept override; - void close_socket() noexcept; - native_handle_type release_socket() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_LOCAL_STREAM_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/select/select_op.hpp b/include/boost/corosio/native/detail/select/select_op.hpp deleted file mode 100644 index 91d5b7e94..000000000 --- a/include/boost/corosio/native/detail/select/select_op.hpp +++ /dev/null @@ -1,193 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_OP_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_OP_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include - -#include -#include -#include -#include - -/* - File descriptors are registered with the select scheduler once (via - select_descriptor_state) and stay registered until closed. - - select() is level-triggered but the descriptor_state pattern - (designed for edge-triggered) works correctly: is_enqueued_ CAS - prevents double-enqueue, add_ready_events is idempotent, and - EAGAIN ops stay parked until the next select() re-reports readiness. - - cancel() captures shared_from_this() into op.impl_ptr to prevent - use-after-free when the socket is closed with pending ops. - - Writes use sendmsg(MSG_NOSIGNAL) on Linux. On macOS/BSD where - MSG_NOSIGNAL may be absent, SO_NOSIGPIPE is set at socket creation - and accepted-socket setup instead. -*/ - -namespace boost::corosio::detail { - -// Forward declarations -class select_tcp_socket; -class select_tcp_acceptor; -struct select_op; - -// Forward declaration -class select_scheduler; - -/// Per-descriptor state for persistent select registration. -struct select_descriptor_state final : reactor_descriptor_state -{}; - -/// select base operation — thin wrapper over reactor_op. -struct select_op : reactor_op -{ - void operator()() override; -}; - -/// select connect operation. -struct select_connect_op final : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// select scatter-read operation. -struct select_read_op final : reactor_read_op -{ - void cancel() noexcept override; -}; - -/** Provides sendmsg() with EINTR retry for select writes. - - Uses MSG_NOSIGNAL where available (Linux). On platforms without - it (macOS/BSD), SO_NOSIGPIPE is set at socket creation time - and flags=0 is used here. -*/ -struct select_write_policy -{ - static ssize_t write(int fd, iovec* iovecs, int count) noexcept - { - msghdr msg{}; - msg.msg_iov = iovecs; - msg.msg_iovlen = static_cast(count); - -#ifdef MSG_NOSIGNAL - constexpr int send_flags = MSG_NOSIGNAL; -#else - constexpr int send_flags = 0; -#endif - - ssize_t n; - do - { - n = ::sendmsg(fd, &msg, send_flags); - } - while (n < 0 && errno == EINTR); - return n; - } -}; - -/// select gather-write operation. -struct select_write_op final : reactor_write_op -{ - void cancel() noexcept override; -}; - -/** Provides accept() + fcntl(O_NONBLOCK|FD_CLOEXEC) with FD_SETSIZE check. - - Uses accept() instead of accept4() for broader POSIX compatibility. -*/ -struct select_accept_policy -{ - static int do_accept( - int fd, sockaddr_storage& peer, socklen_t& addrlen_out) noexcept - { - addrlen_out = sizeof(peer); - int new_fd; - do - { - addrlen_out = sizeof(peer); - new_fd = ::accept( - fd, reinterpret_cast(&peer), &addrlen_out); - } - while (new_fd < 0 && errno == EINTR); - - if (new_fd < 0) - return new_fd; - - if (new_fd >= FD_SETSIZE) - { - ::close(new_fd); - errno = EINVAL; - return -1; - } - - int flags = ::fcntl(new_fd, F_GETFL, 0); - if (flags == -1) - { - int err = errno; - ::close(new_fd); - errno = err; - return -1; - } - - if (::fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int err = errno; - ::close(new_fd); - errno = err; - return -1; - } - - if (::fcntl(new_fd, F_SETFD, FD_CLOEXEC) == -1) - { - int err = errno; - ::close(new_fd); - errno = err; - return -1; - } - -#ifdef SO_NOSIGPIPE - int one = 1; - if (::setsockopt(new_fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) == - -1) - { - int err = errno; - ::close(new_fd); - errno = err; - return -1; - } -#endif - - return new_fd; - } -}; - -/// select accept operation. -struct select_accept_op final - : reactor_accept_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_OP_HPP diff --git a/include/boost/corosio/native/detail/select/select_scheduler.hpp b/include/boost/corosio/native/detail/select/select_scheduler.hpp index a3ac4461e..07d5d5600 100644 --- a/include/boost/corosio/native/detail/select/select_scheduler.hpp +++ b/include/boost/corosio/native/detail/select/select_scheduler.hpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include #include @@ -44,7 +44,6 @@ namespace boost::corosio::detail { struct select_op; -struct select_descriptor_state; /** POSIX scheduler using select() for I/O multiplexing. @@ -109,7 +108,7 @@ class BOOST_COROSIO_DECL select_scheduler final : public reactor_scheduler @param fd The file descriptor to register. @param desc Pointer to descriptor state for this fd. */ - void register_descriptor(int fd, select_descriptor_state* desc) const; + void register_descriptor(int fd, reactor_descriptor_state* desc) const; /** Deregister a persistently registered descriptor. @@ -136,7 +135,7 @@ class BOOST_COROSIO_DECL select_scheduler final : public reactor_scheduler int pipe_fds_[2]; // [0]=read, [1]=write // Per-fd tracking for fd_set building - mutable std::unordered_map registered_descs_; + mutable std::unordered_map registered_descs_; mutable int max_fd_ = -1; }; @@ -206,7 +205,7 @@ select_scheduler::shutdown() inline void select_scheduler::register_descriptor( - int fd, select_descriptor_state* desc) const + int fd, reactor_descriptor_state* desc) const { if (fd < 0 || fd >= FD_SETSIZE) detail::throw_system_error(make_err(EINVAL), "select: fd out of range"); @@ -317,7 +316,7 @@ select_scheduler::run_task( struct fd_entry { int fd; - select_descriptor_state* desc; + reactor_descriptor_state* desc; bool needs_write; }; fd_entry snapshot[FD_SETSIZE]; @@ -399,7 +398,7 @@ select_scheduler::run_task( for (int i = 0; i < snapshot_count; ++i) { int fd = snapshot[i].fd; - select_descriptor_state* desc = snapshot[i].desc; + reactor_descriptor_state* desc = snapshot[i].desc; std::uint32_t flags = 0; if (FD_ISSET(fd, &read_fds)) diff --git a/include/boost/corosio/native/detail/select/select_tcp_acceptor.hpp b/include/boost/corosio/native/detail/select/select_tcp_acceptor.hpp deleted file mode 100644 index 683229001..000000000 --- a/include/boost/corosio/native/detail/select/select_tcp_acceptor.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_ACCEPTOR_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_ACCEPTOR_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include -#include - -namespace boost::corosio::detail { - -class select_tcp_acceptor_service; - -/// Acceptor implementation for select backend. -class select_tcp_acceptor final - : public reactor_acceptor< - select_tcp_acceptor, - select_tcp_acceptor_service, - select_op, - select_accept_op, - select_descriptor_state> -{ - friend class select_tcp_acceptor_service; - -public: - explicit select_tcp_acceptor(select_tcp_acceptor_service& svc) noexcept; - - std::coroutine_handle<> accept( - std::coroutine_handle<>, - capy::executor_ref, - std::stop_token, - std::error_code*, - io_object::implementation**) override; - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_ACCEPTOR_HPP diff --git a/include/boost/corosio/native/detail/select/select_tcp_acceptor_service.hpp b/include/boost/corosio/native/detail/select/select_tcp_acceptor_service.hpp deleted file mode 100644 index 308b54e53..000000000 --- a/include/boost/corosio/native/detail/select/select_tcp_acceptor_service.hpp +++ /dev/null @@ -1,363 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_ACCEPTOR_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_ACCEPTOR_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/** select acceptor service implementation. - - Inherits from tcp_acceptor_service to enable runtime polymorphism. - Uses key_type = tcp_acceptor_service for service lookup. -*/ -class BOOST_COROSIO_DECL select_tcp_acceptor_service final - : public reactor_acceptor_service< - select_tcp_acceptor_service, - tcp_acceptor_service, - select_scheduler, - select_tcp_acceptor, - select_tcp_service> -{ - using base_type = reactor_acceptor_service< - select_tcp_acceptor_service, - tcp_acceptor_service, - select_scheduler, - select_tcp_acceptor, - select_tcp_service>; - friend base_type; - -public: - explicit select_tcp_acceptor_service(capy::execution_context& ctx); - ~select_tcp_acceptor_service() override; - - std::error_code open_acceptor_socket( - tcp_acceptor::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code - bind_acceptor(tcp_acceptor::implementation& impl, endpoint ep) override; - std::error_code - listen_acceptor(tcp_acceptor::implementation& impl, int backlog) override; -}; - -inline void -select_accept_op::cancel() noexcept -{ - if (acceptor_impl_) - acceptor_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_accept_op::operator()() -{ - complete_accept_op(*this); -} - -inline select_tcp_acceptor::select_tcp_acceptor( - select_tcp_acceptor_service& svc) noexcept - : reactor_acceptor(svc) -{ -} - -inline std::coroutine_handle<> -select_tcp_acceptor::accept( - std::coroutine_handle<> h, - capy::executor_ref ex, - std::stop_token token, - std::error_code* ec, - io_object::implementation** impl_out) -{ - auto& op = acc_; - op.reset(); - op.h = h; - op.ex = ex; - op.ec_out = ec; - op.impl_out = impl_out; - op.fd = fd_; - op.start(token, this); - - sockaddr_storage peer_storage{}; - socklen_t addrlen; - int accepted; - do - { - addrlen = sizeof(peer_storage); - accepted = - ::accept(fd_, reinterpret_cast(&peer_storage), &addrlen); - } - while (accepted < 0 && errno == EINTR); - - if (accepted >= 0) - { - if (accepted >= FD_SETSIZE) - { - ::close(accepted); - op.complete(EINVAL, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - int flags = ::fcntl(accepted, F_GETFL, 0); - if (flags == -1) - { - int err = errno; - ::close(accepted); - op.complete(err, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (::fcntl(accepted, F_SETFL, flags | O_NONBLOCK) == -1) - { - int err = errno; - ::close(accepted); - op.complete(err, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (::fcntl(accepted, F_SETFD, FD_CLOEXEC) == -1) - { - int err = errno; - ::close(accepted); - op.complete(err, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - -#ifdef SO_NOSIGPIPE - { - int one = 1; - ::setsockopt( - accepted, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - } -#endif - - { - std::lock_guard lock(desc_state_.mutex); - desc_state_.read_ready = false; - } - - if (svc_.scheduler().try_consume_inline_budget()) - { - auto* socket_svc = svc_.stream_service(); - if (socket_svc) - { - auto& impl = - static_cast(*socket_svc->construct()); - impl.set_socket(accepted); - - impl.desc_state_.fd = accepted; - { - std::lock_guard lock(impl.desc_state_.mutex); - impl.desc_state_.read_op = nullptr; - impl.desc_state_.write_op = nullptr; - impl.desc_state_.connect_op = nullptr; - } - socket_svc->scheduler().register_descriptor( - accepted, &impl.desc_state_); - - impl.set_endpoints( - local_endpoint_, from_sockaddr(peer_storage)); - - *ec = {}; - if (impl_out) - *impl_out = &impl; - } - else - { - ::close(accepted); - *ec = make_err(ENOENT); - if (impl_out) - *impl_out = nullptr; - } - op.cont_op.cont.h = h; - return dispatch_coro(ex, op.cont_op.cont); - } - - op.accepted_fd = accepted; - op.peer_storage = peer_storage; - op.peer_addrlen = addrlen; - op.complete(0, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); - } - - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - op.impl_ptr = shared_from_this(); - svc_.work_started(); - - std::lock_guard lock(desc_state_.mutex); - bool io_done = false; - if (desc_state_.read_ready) - { - desc_state_.read_ready = false; - op.perform_io(); - io_done = (op.errn != EAGAIN && op.errn != EWOULDBLOCK); - if (!io_done) - op.errn = 0; - } - - if (io_done || op.cancelled.load(std::memory_order_acquire)) - { - svc_.post(&op); - svc_.work_finished(); - } - else - { - desc_state_.read_op = &op; - } - return std::noop_coroutine(); - } - - op.complete(errno, 0); - op.impl_ptr = shared_from_this(); - svc_.post(&op); - return std::noop_coroutine(); -} - -inline void -select_tcp_acceptor::cancel() noexcept -{ - do_cancel(); -} - -inline void -select_tcp_acceptor::close_socket() noexcept -{ - do_close_socket(); -} - -inline select_tcp_acceptor_service::select_tcp_acceptor_service( - capy::execution_context& ctx) - : base_type(ctx) -{ - auto* svc = ctx_.find_service(); - stream_svc_ = svc ? dynamic_cast(svc) : nullptr; -} - -inline select_tcp_acceptor_service::~select_tcp_acceptor_service() {} - -inline std::error_code -select_tcp_acceptor_service::open_acceptor_socket( - tcp_acceptor::implementation& impl, int family, int type, int protocol) -{ - auto* select_impl = static_cast(&impl); - select_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - if (fd >= FD_SETSIZE) - { - ::close(fd); - return make_err(EMFILE); - } - - if (family == AF_INET6) - { - int val = 0; // dual-stack default - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); - } - -#ifdef SO_NOSIGPIPE - { - int nosig = 1; - ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &nosig, sizeof(nosig)); - } -#endif - - select_impl->fd_ = fd; - - // Set up descriptor state but do NOT register with reactor yet - // (registration happens in do_listen via reactor_acceptor base) - select_impl->desc_state_.fd = fd; - { - std::lock_guard lock(select_impl->desc_state_.mutex); - select_impl->desc_state_.read_op = nullptr; - } - - return {}; -} - -inline std::error_code -select_tcp_acceptor_service::bind_acceptor( - tcp_acceptor::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -inline std::error_code -select_tcp_acceptor_service::listen_acceptor( - tcp_acceptor::implementation& impl, int backlog) -{ - return static_cast(&impl)->do_listen(backlog); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_ACCEPTOR_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/select/select_tcp_service.hpp b/include/boost/corosio/native/detail/select/select_tcp_service.hpp deleted file mode 100644 index d384e40af..000000000 --- a/include/boost/corosio/native/detail/select/select_tcp_service.hpp +++ /dev/null @@ -1,251 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/* - Each I/O op tries the syscall speculatively; only registers with - the reactor on EAGAIN. Fd is registered once at open time and - stays registered until close. The reactor only marks ready_events_; - actual I/O happens in invoke_deferred_io(). cancel() captures - shared_from_this() into op.impl_ptr to keep the impl alive. -*/ - -namespace boost::corosio::detail { - -/** select TCP service implementation. - - Inherits from tcp_service to enable runtime polymorphism. - Uses key_type = tcp_service for service lookup. -*/ -class BOOST_COROSIO_DECL select_tcp_service final - : public reactor_socket_service< - select_tcp_service, - tcp_service, - select_scheduler, - select_tcp_socket> -{ -public: - explicit select_tcp_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_socket( - tcp_socket::implementation& impl, - int family, - int type, - int protocol) override; - - std::error_code - bind_socket(tcp_socket::implementation& impl, endpoint ep) override; -}; - -inline void -select_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_read_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_write_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_op::operator()() -{ - complete_io_op(*this); -} - -inline void -select_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline select_tcp_socket::select_tcp_socket(select_tcp_service& svc) noexcept - : reactor_stream_socket(svc) -{ -} - -inline select_tcp_socket::~select_tcp_socket() = default; - -inline std::coroutine_handle<> -select_tcp_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - auto result = do_connect(h, ex, ep, token, ec); - // Rebuild fd_sets so select() watches for writability - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline std::coroutine_handle<> -select_tcp_socket::read_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_read_some(h, ex, param, token, ec, bytes_out); -} - -inline std::coroutine_handle<> -select_tcp_socket::write_some( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param param, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - auto result = do_write_some(h, ex, param, token, ec, bytes_out); - // Rebuild fd_sets so select() watches for writability - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline void -select_tcp_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -select_tcp_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline std::error_code -select_tcp_service::open_socket( - tcp_socket::implementation& impl, int family, int type, int protocol) -{ - auto* select_impl = static_cast(&impl); - select_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - if (family == AF_INET6) - { - int one = 1; - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - } - - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - if (fd >= FD_SETSIZE) - { - ::close(fd); - return make_err(EMFILE); - } - -#ifdef SO_NOSIGPIPE - { - int one = 1; - ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - } -#endif - - select_impl->fd_ = fd; - - select_impl->desc_state_.fd = fd; - { - std::lock_guard lock(select_impl->desc_state_.mutex); - select_impl->desc_state_.read_op = nullptr; - select_impl->desc_state_.write_op = nullptr; - select_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &select_impl->desc_state_); - - return {}; -} - -inline std::error_code -select_tcp_service::bind_socket( - tcp_socket::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/select/select_tcp_socket.hpp b/include/boost/corosio/native/detail/select/select_tcp_socket.hpp deleted file mode 100644 index 8abc88c69..000000000 --- a/include/boost/corosio/native/detail/select/select_tcp_socket.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include -#include - -namespace boost::corosio::detail { - -class select_tcp_service; - -/// Stream socket implementation for select backend. -class select_tcp_socket final - : public reactor_stream_socket< - select_tcp_socket, - select_tcp_service, - select_connect_op, - select_read_op, - select_write_op, - select_descriptor_state> -{ - friend class select_tcp_service; - -public: - explicit select_tcp_socket(select_tcp_service& svc) noexcept; - ~select_tcp_socket() override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> read_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> write_some( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::error_code shutdown(tcp_socket::shutdown_type what) noexcept override - { - return do_shutdown(static_cast(what)); - } - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SOCKET_HPP diff --git a/include/boost/corosio/native/detail/select/select_traits.hpp b/include/boost/corosio/native/detail/select/select_traits.hpp new file mode 100644 index 000000000..252e226f3 --- /dev/null +++ b/include/boost/corosio/native/detail/select/select_traits.hpp @@ -0,0 +1,240 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TRAITS_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TRAITS_HPP + +#include + +#if BOOST_COROSIO_HAS_SELECT + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* select backend traits. + + Captures the platform-specific behavior of the portable select() backend: + manual fcntl for O_NONBLOCK/FD_CLOEXEC, FD_SETSIZE validation, + conditional SO_NOSIGPIPE, sendmsg(MSG_NOSIGNAL) where available, + and accept()+fcntl for accepted connections. +*/ + +namespace boost::corosio::detail { + +class select_scheduler; + +struct select_traits +{ + using scheduler_type = select_scheduler; + using desc_state_type = reactor_descriptor_state; + + static constexpr bool needs_write_notification = true; + + // No extra per-socket state or lifecycle hooks needed for select. + struct stream_socket_hook + { + std::error_code on_set_option( + int fd, int level, int optname, + void const* data, std::size_t size) noexcept + { + if (::setsockopt( + fd, level, optname, data, + static_cast(size)) != 0) + return make_err(errno); + return {}; + } + static void pre_shutdown(int) noexcept {} + static void pre_destroy(int) noexcept {} + }; + + struct write_policy + { + static ssize_t write(int fd, iovec* iovecs, int count) noexcept + { + msghdr msg{}; + msg.msg_iov = iovecs; + msg.msg_iovlen = static_cast(count); + +#ifdef MSG_NOSIGNAL + constexpr int send_flags = MSG_NOSIGNAL; +#else + constexpr int send_flags = 0; +#endif + + ssize_t n; + do + { + n = ::sendmsg(fd, &msg, send_flags); + } + while (n < 0 && errno == EINTR); + return n; + } + }; + + struct accept_policy + { + static int do_accept( + int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept + { + addrlen = sizeof(peer); + int new_fd; + do + { + new_fd = ::accept( + fd, reinterpret_cast(&peer), &addrlen); + } + while (new_fd < 0 && errno == EINTR); + + if (new_fd < 0) + return new_fd; + + if (new_fd >= FD_SETSIZE) + { + ::close(new_fd); + errno = EINVAL; + return -1; + } + + int flags = ::fcntl(new_fd, F_GETFL, 0); + if (flags == -1) + { + int err = errno; + ::close(new_fd); + errno = err; + return -1; + } + + if (::fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) == -1) + { + int err = errno; + ::close(new_fd); + errno = err; + return -1; + } + + if (::fcntl(new_fd, F_SETFD, FD_CLOEXEC) == -1) + { + int err = errno; + ::close(new_fd); + errno = err; + return -1; + } + +#ifdef SO_NOSIGPIPE + // Best-effort: SO_NOSIGPIPE is a safety net; write paths + // also use MSG_NOSIGNAL where available. Failure here + // should not prevent the accepted connection from being used. + int one = 1; + (void)::setsockopt( + new_fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); +#endif + + return new_fd; + } + }; + + // Create a plain socket (no atomic flags -- select is POSIX-portable). + static int create_socket(int family, int type, int protocol) noexcept + { + return ::socket(family, type, protocol); + } + + // Set O_NONBLOCK, FD_CLOEXEC; check FD_SETSIZE; optionally SO_NOSIGPIPE. + // Caller is responsible for closing fd on error. + static std::error_code set_fd_options(int fd) noexcept + { + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags == -1) + return make_err(errno); + if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + return make_err(errno); + if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + return make_err(errno); + + if (fd >= FD_SETSIZE) + return make_err(EMFILE); + +#ifdef SO_NOSIGPIPE + // Best-effort: SO_NOSIGPIPE is a safety net; write paths + // also use MSG_NOSIGNAL where available. Match develop's + // predominant behavior of ignoring failures here rather + // than failing socket creation. + { + int one = 1; + (void)::setsockopt( + fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); + } +#endif + + return {}; + } + + // Apply protocol-specific options after socket creation. + // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort). + static std::error_code + configure_ip_socket(int fd, int family) noexcept + { + if (family == AF_INET6) + { + int one = 1; + (void)::setsockopt( + fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + } + + return set_fd_options(fd); + } + + // Apply protocol-specific options for acceptor sockets. + // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort). + static std::error_code + configure_ip_acceptor(int fd, int family) noexcept + { + if (family == AF_INET6) + { + int val = 0; + (void)::setsockopt( + fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); + } + + return set_fd_options(fd); + } + + // Apply options for local (unix) sockets. + static std::error_code + configure_local_socket(int fd) noexcept + { + return set_fd_options(fd); + } + + // Non-mutating validation for fds adopted via assign(). Select's + // reactor cannot handle fds above FD_SETSIZE, so reject them up + // front instead of letting FD_SET clobber unrelated memory. + static std::error_code + validate_assigned_fd(int fd) noexcept + { + if (fd >= FD_SETSIZE) + return make_err(EMFILE); + return {}; + } +}; + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_HAS_SELECT + +#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TRAITS_HPP diff --git a/include/boost/corosio/native/detail/select/select_types.hpp b/include/boost/corosio/native/detail/select/select_types.hpp new file mode 100644 index 000000000..941b23969 --- /dev/null +++ b/include/boost/corosio/native/detail/select/select_types.hpp @@ -0,0 +1,258 @@ +// +// Copyright (c) 2026 Michael Vandeberg +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/corosio +// + +#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TYPES_HPP +#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TYPES_HPP + +#include + +#if BOOST_COROSIO_HAS_SELECT + +/* Named per-backend types for the select reactor. + + Each class is a final, named wrapper around the parameterized + reactor_*_impl templates. Forward-declarable from backend.hpp + so the concrete layer never pulls in platform headers. +*/ + +#include +#include +#include +#include + +namespace boost::corosio::detail { + +// Forward declarations for cross-references. +class select_tcp_socket; +class select_tcp_service; +class select_tcp_acceptor; +class select_tcp_acceptor_service; +class select_udp_socket; +class select_udp_service; +class select_local_stream_socket; +class select_local_stream_service; +class select_local_stream_acceptor; +class select_local_stream_acceptor_service; +class select_local_datagram_socket; +class select_local_datagram_service; + +// --- Stream sockets --- + +class select_tcp_socket final + : public reactor_stream_socket_impl< + select_tcp_socket, select_traits, select_tcp_service, + select_tcp_acceptor, tcp_socket::implementation, endpoint> +{ + using base_type = reactor_stream_socket_impl< + select_tcp_socket, select_traits, select_tcp_service, + select_tcp_acceptor, tcp_socket::implementation, endpoint>; + friend select_tcp_service; +public: + explicit select_tcp_socket(select_tcp_service& svc) noexcept + : base_type(svc) {} +}; + +class select_local_stream_socket final + : public reactor_stream_socket_impl< + select_local_stream_socket, select_traits, + select_local_stream_service, select_local_stream_acceptor, + local_stream_socket::implementation, corosio::local_endpoint> +{ + using base_type = reactor_stream_socket_impl< + select_local_stream_socket, select_traits, + select_local_stream_service, select_local_stream_acceptor, + local_stream_socket::implementation, corosio::local_endpoint>; + friend select_local_stream_service; +public: + explicit select_local_stream_socket(select_local_stream_service& svc) noexcept + : base_type(svc) {} + + native_handle_type release_socket() noexcept override + { + hook_ = {}; + return this->do_release_socket(); + } +}; + +// --- Datagram sockets --- + +class select_udp_socket final + : public reactor_dgram_socket_impl< + select_udp_socket, select_traits, select_udp_service, + select_tcp_acceptor, udp_socket::implementation, endpoint> +{ + using base_type = reactor_dgram_socket_impl< + select_udp_socket, select_traits, select_udp_service, + select_tcp_acceptor, udp_socket::implementation, endpoint>; + friend select_udp_service; +public: + explicit select_udp_socket(select_udp_service& svc) noexcept + : base_type(svc) {} +}; + +class select_local_datagram_socket final + : public reactor_dgram_socket_impl< + select_local_datagram_socket, select_traits, + select_local_datagram_service, select_tcp_acceptor, + local_datagram_socket::implementation, corosio::local_endpoint> +{ + using base_type = reactor_dgram_socket_impl< + select_local_datagram_socket, select_traits, + select_local_datagram_service, select_tcp_acceptor, + local_datagram_socket::implementation, corosio::local_endpoint>; + friend select_local_datagram_service; +public: + explicit select_local_datagram_socket(select_local_datagram_service& svc) noexcept + : base_type(svc) {} + + std::error_code shutdown(corosio::shutdown_type what) noexcept override + { + return this->do_shutdown(static_cast(what)); + } + + std::error_code bind(corosio::local_endpoint ep) noexcept override + { + return this->do_bind(ep); + } + + native_handle_type release_socket() noexcept override + { + return this->do_release_socket(); + } +}; + +// --- Acceptors --- + +class select_tcp_acceptor final + : public reactor_acceptor_impl< + select_tcp_acceptor, select_traits, + select_tcp_acceptor_service, select_tcp_socket, + tcp_acceptor::implementation, endpoint> +{ + using base_type = reactor_acceptor_impl< + select_tcp_acceptor, select_traits, + select_tcp_acceptor_service, select_tcp_socket, + tcp_acceptor::implementation, endpoint>; + friend select_tcp_acceptor_service; +public: + explicit select_tcp_acceptor(select_tcp_acceptor_service& svc) noexcept + : base_type(svc) {} +}; + +class select_local_stream_acceptor final + : public reactor_acceptor_impl< + select_local_stream_acceptor, select_traits, + select_local_stream_acceptor_service, + select_local_stream_socket, + local_stream_acceptor::implementation, corosio::local_endpoint> +{ + using base_type = reactor_acceptor_impl< + select_local_stream_acceptor, select_traits, + select_local_stream_acceptor_service, + select_local_stream_socket, + local_stream_acceptor::implementation, corosio::local_endpoint>; + friend select_local_stream_acceptor_service; +public: + explicit select_local_stream_acceptor( + select_local_stream_acceptor_service& svc) noexcept + : base_type(svc) {} + + native_handle_type release_socket() noexcept override + { + return this->do_release_socket(); + } +}; + +// --- Services --- + +class BOOST_COROSIO_DECL select_tcp_service final + : public reactor_tcp_service_impl< + select_tcp_service, select_traits, select_tcp_socket> +{ + using base_type = reactor_tcp_service_impl< + select_tcp_service, select_traits, select_tcp_socket>; +public: + explicit select_tcp_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL select_local_stream_service final + : public reactor_local_stream_service_impl< + select_local_stream_service, select_traits, + select_local_stream_socket> +{ + using base_type = reactor_local_stream_service_impl< + select_local_stream_service, select_traits, + select_local_stream_socket>; +public: + explicit select_local_stream_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL select_udp_service final + : public reactor_udp_service_impl< + select_udp_service, select_traits, select_udp_socket> +{ + using base_type = reactor_udp_service_impl< + select_udp_service, select_traits, select_udp_socket>; +public: + explicit select_udp_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL select_local_datagram_service final + : public reactor_local_dgram_service_impl< + select_local_datagram_service, select_traits, + select_local_datagram_socket> +{ + using base_type = reactor_local_dgram_service_impl< + select_local_datagram_service, select_traits, + select_local_datagram_socket>; +public: + explicit select_local_datagram_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL select_tcp_acceptor_service final + : public reactor_acceptor_service_impl< + select_tcp_acceptor_service, select_traits, + tcp_acceptor_service, select_tcp_acceptor, + select_tcp_service, endpoint> +{ + using base_type = reactor_acceptor_service_impl< + select_tcp_acceptor_service, select_traits, + tcp_acceptor_service, select_tcp_acceptor, + select_tcp_service, endpoint>; +public: + explicit select_tcp_acceptor_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +class BOOST_COROSIO_DECL select_local_stream_acceptor_service final + : public reactor_acceptor_service_impl< + select_local_stream_acceptor_service, select_traits, + local_stream_acceptor_service, + select_local_stream_acceptor, + select_local_stream_service, corosio::local_endpoint> +{ + using base_type = reactor_acceptor_service_impl< + select_local_stream_acceptor_service, select_traits, + local_stream_acceptor_service, + select_local_stream_acceptor, + select_local_stream_service, corosio::local_endpoint>; +public: + explicit select_local_stream_acceptor_service(capy::execution_context& ctx) + : base_type(ctx) {} +}; + +} // namespace boost::corosio::detail + +#endif // BOOST_COROSIO_HAS_SELECT + +#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TYPES_HPP diff --git a/include/boost/corosio/native/detail/select/select_udp_service.hpp b/include/boost/corosio/native/detail/select/select_udp_service.hpp deleted file mode 100644 index e615e67dc..000000000 --- a/include/boost/corosio/native/detail/select/select_udp_service.hpp +++ /dev/null @@ -1,319 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -/** select UDP service implementation. - - Inherits from udp_service to enable runtime polymorphism. - Uses key_type = udp_service for service lookup. -*/ -class BOOST_COROSIO_DECL select_udp_service final - : public reactor_socket_service< - select_udp_service, - udp_service, - select_scheduler, - select_udp_socket> -{ -public: - explicit select_udp_service(capy::execution_context& ctx) - : reactor_socket_service(ctx) - { - } - - std::error_code open_datagram_socket( - udp_socket::implementation& impl, - int family, - int type, - int protocol) override; - std::error_code - bind_datagram(udp_socket::implementation& impl, endpoint ep) override; -}; - -// Cancellation for connectionless ops - -inline void -select_send_to_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_recv_from_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Cancellation for connected-mode ops - -inline void -select_udp_connect_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_send_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -inline void -select_recv_op::cancel() noexcept -{ - if (socket_impl_) - socket_impl_->cancel_single_op(*this); - else - request_cancel(); -} - -// Completion handlers - -inline void -select_datagram_op::operator()() -{ - complete_io_op(*this); -} - -inline void -select_recv_from_op::operator()() -{ - complete_datagram_op(*this, this->source_out); -} - -inline void -select_udp_connect_op::operator()() -{ - complete_connect_op(*this); -} - -inline void -select_recv_op::operator()() -{ - complete_io_op(*this); -} - -// Socket construction/destruction - -inline select_udp_socket::select_udp_socket(select_udp_service& svc) noexcept - : reactor_datagram_socket(svc) -{ -} - -inline select_udp_socket::~select_udp_socket() = default; - -// Connectionless I/O - -inline std::coroutine_handle<> -select_udp_socket::send_to( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - endpoint dest, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - auto result = do_send_to(h, ex, buf, dest, flags, token, ec, bytes_out); - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline std::coroutine_handle<> -select_udp_socket::recv_from( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - endpoint* source, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv_from(h, ex, buf, source, flags, token, ec, bytes_out); -} - -// Connected-mode I/O - -inline std::coroutine_handle<> -select_udp_socket::connect( - std::coroutine_handle<> h, - capy::executor_ref ex, - endpoint ep, - std::stop_token token, - std::error_code* ec) -{ - auto result = do_connect(h, ex, ep, token, ec); - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline std::coroutine_handle<> -select_udp_socket::send( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - auto result = do_send(h, ex, buf, flags, token, ec, bytes_out); - if (result == std::noop_coroutine()) - svc_.scheduler().notify_reactor(); - return result; -} - -inline std::coroutine_handle<> -select_udp_socket::recv( - std::coroutine_handle<> h, - capy::executor_ref ex, - buffer_param buf, - int flags, - std::stop_token token, - std::error_code* ec, - std::size_t* bytes_out) -{ - return do_recv(h, ex, buf, flags, token, ec, bytes_out); -} - -inline endpoint -select_udp_socket::remote_endpoint() const noexcept -{ - return reactor_datagram_socket::remote_endpoint(); -} - -inline void -select_udp_socket::cancel() noexcept -{ - do_cancel(); -} - -inline void -select_udp_socket::close_socket() noexcept -{ - do_close_socket(); -} - -inline std::error_code -select_udp_service::open_datagram_socket( - udp_socket::implementation& impl, int family, int type, int protocol) -{ - auto* select_impl = static_cast(&impl); - select_impl->close_socket(); - - int fd = ::socket(family, type, protocol); - if (fd < 0) - return make_err(errno); - - if (family == AF_INET6) - { - int one = 1; - ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - } - - int flags = ::fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - int errn = errno; - ::close(fd); - return make_err(errn); - } - - if (fd >= FD_SETSIZE) - { - ::close(fd); - return make_err(EMFILE); - } - -#ifdef SO_NOSIGPIPE - { - int one = 1; - ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - } -#endif - - select_impl->fd_ = fd; - - select_impl->desc_state_.fd = fd; - { - std::lock_guard lock(select_impl->desc_state_.mutex); - select_impl->desc_state_.read_op = nullptr; - select_impl->desc_state_.write_op = nullptr; - select_impl->desc_state_.connect_op = nullptr; - } - scheduler().register_descriptor(fd, &select_impl->desc_state_); - - return {}; -} - -inline std::error_code -select_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep) -{ - return static_cast(&impl)->do_bind(ep); -} - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP diff --git a/include/boost/corosio/native/detail/select/select_udp_socket.hpp b/include/boost/corosio/native/detail/select/select_udp_socket.hpp deleted file mode 100644 index 5a7374b9e..000000000 --- a/include/boost/corosio/native/detail/select/select_udp_socket.hpp +++ /dev/null @@ -1,140 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/corosio -// - -#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SOCKET_HPP -#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SOCKET_HPP - -#include - -#if BOOST_COROSIO_HAS_SELECT - -#include -#include -#include -#include -#include - -namespace boost::corosio::detail { - -class select_udp_service; -class select_udp_socket; - -/// select datagram base operation. -struct select_datagram_op : reactor_op -{ - void operator()() override; -}; - -/// select send_to operation. -struct select_send_to_op final : reactor_send_to_op -{ - void cancel() noexcept override; -}; - -/// select recv_from operation. -struct select_recv_from_op final : reactor_recv_from_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// select connect operation for UDP. -struct select_udp_connect_op final : reactor_connect_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// select connected send operation. -struct select_send_op final : reactor_send_op -{ - void cancel() noexcept override; -}; - -/// select connected recv operation. -struct select_recv_op final : reactor_recv_op -{ - void operator()() override; - void cancel() noexcept override; -}; - -/// Datagram socket implementation for select backend. -class select_udp_socket final - : public reactor_datagram_socket< - select_udp_socket, - select_udp_service, - select_udp_connect_op, - select_send_to_op, - select_recv_from_op, - select_send_op, - select_recv_op, - select_descriptor_state> -{ - friend class select_udp_service; - -public: - explicit select_udp_socket(select_udp_service& svc) noexcept; - ~select_udp_socket() override; - - std::coroutine_handle<> send_to( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - endpoint, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv_from( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - endpoint*, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> connect( - std::coroutine_handle<>, - capy::executor_ref, - endpoint, - std::stop_token, - std::error_code*) override; - - std::coroutine_handle<> send( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - std::coroutine_handle<> recv( - std::coroutine_handle<>, - capy::executor_ref, - buffer_param, - int flags, - std::stop_token, - std::error_code*, - std::size_t*) override; - - endpoint remote_endpoint() const noexcept override; - - void cancel() noexcept override; - void close_socket() noexcept; -}; - -} // namespace boost::corosio::detail - -#endif // BOOST_COROSIO_HAS_SELECT - -#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SOCKET_HPP diff --git a/include/boost/corosio/native/native_tcp_acceptor.hpp b/include/boost/corosio/native/native_tcp_acceptor.hpp index 4ff586756..7546d56f8 100644 --- a/include/boost/corosio/native/native_tcp_acceptor.hpp +++ b/include/boost/corosio/native/native_tcp_acceptor.hpp @@ -15,15 +15,15 @@ #ifndef BOOST_COROSIO_MRDOCS #if BOOST_COROSIO_HAS_EPOLL -#include +#include #endif #if BOOST_COROSIO_HAS_SELECT -#include +#include #endif #if BOOST_COROSIO_HAS_KQUEUE -#include +#include #endif #if BOOST_COROSIO_HAS_IOCP diff --git a/include/boost/corosio/native/native_tcp_socket.hpp b/include/boost/corosio/native/native_tcp_socket.hpp index c25f7507c..03e1e2bf7 100644 --- a/include/boost/corosio/native/native_tcp_socket.hpp +++ b/include/boost/corosio/native/native_tcp_socket.hpp @@ -15,15 +15,15 @@ #ifndef BOOST_COROSIO_MRDOCS #if BOOST_COROSIO_HAS_EPOLL -#include +#include #endif #if BOOST_COROSIO_HAS_SELECT -#include +#include #endif #if BOOST_COROSIO_HAS_KQUEUE -#include +#include #endif #if BOOST_COROSIO_HAS_IOCP diff --git a/include/boost/corosio/native/native_udp_socket.hpp b/include/boost/corosio/native/native_udp_socket.hpp index 5368fd395..805b463f2 100644 --- a/include/boost/corosio/native/native_udp_socket.hpp +++ b/include/boost/corosio/native/native_udp_socket.hpp @@ -15,15 +15,15 @@ #ifndef BOOST_COROSIO_MRDOCS #if BOOST_COROSIO_HAS_EPOLL -#include +#include #endif #if BOOST_COROSIO_HAS_SELECT -#include +#include #endif #if BOOST_COROSIO_HAS_KQUEUE -#include +#include #endif #if BOOST_COROSIO_HAS_IOCP diff --git a/src/corosio/src/io_context.cpp b/src/corosio/src/io_context.cpp index 119f0e4ae..0824f16a3 100644 --- a/src/corosio/src/io_context.cpp +++ b/src/corosio/src/io_context.cpp @@ -16,33 +16,15 @@ #include #if BOOST_COROSIO_HAS_EPOLL -#include -#include -#include -#include -#include -#include -#include +#include #endif #if BOOST_COROSIO_HAS_SELECT -#include -#include -#include -#include -#include -#include -#include +#include #endif #if BOOST_COROSIO_HAS_KQUEUE -#include -#include -#include -#include -#include -#include -#include +#include #endif #if BOOST_COROSIO_HAS_IOCP