94.34% Lines (50/53)
89.47% Functions (17/19)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | // | 1 | // | |||||
| 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | |||||
| 3 | // Copyright (c) 2026 Steve Gerbino | 3 | // Copyright (c) 2026 Steve Gerbino | |||||
| 4 | // | 4 | // | |||||
| 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |||||
| 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||||
| 7 | // | 7 | // | |||||
| 8 | // Official repository: https://github.com/cppalliance/corosio | 8 | // Official repository: https://github.com/cppalliance/corosio | |||||
| 9 | // | 9 | // | |||||
| 10 | 10 | |||||||
| 11 | #ifndef BOOST_COROSIO_IO_IO_OBJECT_HPP | 11 | #ifndef BOOST_COROSIO_IO_IO_OBJECT_HPP | |||||
| 12 | #define BOOST_COROSIO_IO_IO_OBJECT_HPP | 12 | #define BOOST_COROSIO_IO_IO_OBJECT_HPP | |||||
| 13 | 13 | |||||||
| 14 | #include <boost/corosio/detail/config.hpp> | 14 | #include <boost/corosio/detail/config.hpp> | |||||
| 15 | #include <boost/corosio/detail/except.hpp> | 15 | #include <boost/corosio/detail/except.hpp> | |||||
| 16 | #include <boost/capy/ex/execution_context.hpp> | 16 | #include <boost/capy/ex/execution_context.hpp> | |||||
| 17 | 17 | |||||||
| 18 | #include <utility> | 18 | #include <utility> | |||||
| 19 | 19 | |||||||
| 20 | namespace boost::corosio { | 20 | namespace boost::corosio { | |||||
| 21 | 21 | |||||||
| 22 | /** Base class for platform I/O objects. | 22 | /** Base class for platform I/O objects. | |||||
| 23 | 23 | |||||||
| 24 | Provides common infrastructure for I/O objects that wrap kernel | 24 | Provides common infrastructure for I/O objects that wrap kernel | |||||
| 25 | resources (sockets, timers, signal handlers, acceptors). Derived | 25 | resources (sockets, timers, signal handlers, acceptors). Derived | |||||
| 26 | classes dispatch operations through a platform-specific vtable | 26 | classes dispatch operations through a platform-specific vtable | |||||
| 27 | (IOCP, epoll, kqueue, io_uring). | 27 | (IOCP, epoll, kqueue, io_uring). | |||||
| 28 | 28 | |||||||
| 29 | @par Semantics | 29 | @par Semantics | |||||
| 30 | Only concrete platform I/O types should inherit from `io_object`. | 30 | Only concrete platform I/O types should inherit from `io_object`. | |||||
| 31 | Test mocks, decorators, and stream adapters must not inherit from | 31 | Test mocks, decorators, and stream adapters must not inherit from | |||||
| 32 | this class. Use concepts or templates for generic I/O algorithms. | 32 | this class. Use concepts or templates for generic I/O algorithms. | |||||
| 33 | 33 | |||||||
| 34 | @par Thread Safety | 34 | @par Thread Safety | |||||
| 35 | Distinct objects: Safe. | 35 | Distinct objects: Safe. | |||||
| 36 | Shared objects: Unsafe. All operations on a single I/O object | 36 | Shared objects: Unsafe. All operations on a single I/O object | |||||
| 37 | must be serialized. | 37 | must be serialized. | |||||
| 38 | 38 | |||||||
| 39 | @note Intended as a protected base class. The handle member | 39 | @note Intended as a protected base class. The handle member | |||||
| 40 | `h_` is accessible to derived classes. | 40 | `h_` is accessible to derived classes. | |||||
| 41 | 41 | |||||||
| 42 | @see io_stream, tcp_socket, tcp_acceptor | 42 | @see io_stream, tcp_socket, tcp_acceptor | |||||
| 43 | */ | 43 | */ | |||||
| 44 | class BOOST_COROSIO_DECL io_object | 44 | class BOOST_COROSIO_DECL io_object | |||||
| 45 | { | 45 | { | |||||
| 46 | public: | 46 | public: | |||||
| 47 | class handle; | 47 | class handle; | |||||
| 48 | 48 | |||||||
| 49 | /** Base interface for platform I/O implementations. | 49 | /** Base interface for platform I/O implementations. | |||||
| 50 | 50 | |||||||
| 51 | Derived classes provide platform-specific operation dispatch. | 51 | Derived classes provide platform-specific operation dispatch. | |||||
| 52 | */ | 52 | */ | |||||
| 53 | struct implementation | 53 | struct implementation | |||||
| 54 | { | 54 | { | |||||
| HITCBC | 55 | 19770 | virtual ~implementation() = default; | 55 | 23402 | virtual ~implementation() = default; | ||
| 56 | }; | 56 | }; | |||||
| 57 | 57 | |||||||
| 58 | /** Service interface for I/O object lifecycle management. | 58 | /** Service interface for I/O object lifecycle management. | |||||
| 59 | 59 | |||||||
| 60 | Platform backends implement this interface to manage the | 60 | Platform backends implement this interface to manage the | |||||
| 61 | creation, closing, and destruction of I/O object | 61 | creation, closing, and destruction of I/O object | |||||
| 62 | implementations. | 62 | implementations. | |||||
| 63 | */ | 63 | */ | |||||
| 64 | struct io_service | 64 | struct io_service | |||||
| 65 | { | 65 | { | |||||
| HITCBC | 66 | 6655 | virtual ~io_service() = default; | 66 | 6655 | virtual ~io_service() = default; | ||
| 67 | 67 | |||||||
| 68 | /// Construct a new implementation instance. | 68 | /// Construct a new implementation instance. | |||||
| 69 | virtual implementation* construct() = 0; | 69 | virtual implementation* construct() = 0; | |||||
| 70 | 70 | |||||||
| 71 | /// Destroy the implementation, closing kernel resources and freeing memory. | 71 | /// Destroy the implementation, closing kernel resources and freeing memory. | |||||
| 72 | virtual void destroy(implementation*) = 0; | 72 | virtual void destroy(implementation*) = 0; | |||||
| 73 | 73 | |||||||
| 74 | /// Close the I/O object, releasing kernel resources without deallocating. | 74 | /// Close the I/O object, releasing kernel resources without deallocating. | |||||
| HITCBC | 75 | 7108 | virtual void close(handle&) {} | 75 | 8320 | virtual void close(handle&) {} | ||
| 76 | }; | 76 | }; | |||||
| 77 | 77 | |||||||
| 78 | /** RAII wrapper for I/O object implementation lifetime. | 78 | /** RAII wrapper for I/O object implementation lifetime. | |||||
| 79 | 79 | |||||||
| 80 | Manages ownership of the platform-specific implementation, | 80 | Manages ownership of the platform-specific implementation, | |||||
| 81 | automatically destroying it when the handle goes out of scope. | 81 | automatically destroying it when the handle goes out of scope. | |||||
| 82 | */ | 82 | */ | |||||
| 83 | class handle | 83 | class handle | |||||
| 84 | { | 84 | { | |||||
| 85 | capy::execution_context* ctx_ = nullptr; | 85 | capy::execution_context* ctx_ = nullptr; | |||||
| 86 | io_service* svc_ = nullptr; | 86 | io_service* svc_ = nullptr; | |||||
| 87 | implementation* impl_ = nullptr; | 87 | implementation* impl_ = nullptr; | |||||
| 88 | 88 | |||||||
| 89 | public: | 89 | public: | |||||
| 90 | /// Destroy the handle and its implementation. | 90 | /// Destroy the handle and its implementation. | |||||
| HITCBC | 91 | 47716 | ~handle() | 91 | 56194 | ~handle() | ||
| 92 | { | 92 | { | |||||
| HITCBC | 93 | 47716 | if (impl_) | 93 | 56194 | if (impl_) | ||
| 94 | { | 94 | { | |||||
| HITCBC | 95 | 20178 | svc_->close(*this); | 95 | 23811 | svc_->close(*this); | ||
| HITCBC | 96 | 20178 | svc_->destroy(impl_); | 96 | 23811 | svc_->destroy(impl_); | ||
| 97 | } | 97 | } | |||||
| HITCBC | 98 | 47716 | } | 98 | 56194 | } | ||
| 99 | 99 | |||||||
| 100 | /// Construct an empty handle. | 100 | /// Construct an empty handle. | |||||
| MISUBC | 101 | ✗ | handle() = default; | 101 | ✗ | handle() = default; | ||
| 102 | 102 | |||||||
| 103 | /// Construct a handle bound to a context and service. | 103 | /// Construct a handle bound to a context and service. | |||||
| HITCBC | 104 | 20205 | handle(capy::execution_context& ctx, io_service& svc) | 104 | 23838 | handle(capy::execution_context& ctx, io_service& svc) | ||
| HITCBC | 105 | 20205 | : ctx_(&ctx) | 105 | 23838 | : ctx_(&ctx) | ||
| HITCBC | 106 | 20205 | , svc_(&svc) | 106 | 23838 | , svc_(&svc) | ||
| HITCBC | 107 | 20205 | , impl_(svc_->construct()) | 107 | 23838 | , impl_(svc_->construct()) | ||
| 108 | { | 108 | { | |||||
| HITCBC | 109 | 20205 | } | 109 | 23838 | } | ||
| 110 | 110 | |||||||
| 111 | /// Move construct from another handle. | 111 | /// Move construct from another handle. | |||||
| HITCBC | 112 | 27513 | handle(handle&& other) noexcept | 112 | 32358 | handle(handle&& other) noexcept | ||
| HITCBC | 113 | 27513 | : ctx_(std::exchange(other.ctx_, nullptr)) | 113 | 32358 | : ctx_(std::exchange(other.ctx_, nullptr)) | ||
| HITCBC | 114 | 27513 | , svc_(std::exchange(other.svc_, nullptr)) | 114 | 32358 | , svc_(std::exchange(other.svc_, nullptr)) | ||
| HITCBC | 115 | 27513 | , impl_(std::exchange(other.impl_, nullptr)) | 115 | 32358 | , impl_(std::exchange(other.impl_, nullptr)) | ||
| 116 | { | 116 | { | |||||
| HITCBC | 117 | 27513 | } | 117 | 32358 | } | ||
| 118 | 118 | |||||||
| 119 | /// Move assign from another handle. | 119 | /// Move assign from another handle. | |||||
| HITCBC | 120 | 25 | handle& operator=(handle&& other) noexcept | 120 | 25 | handle& operator=(handle&& other) noexcept | ||
| 121 | { | 121 | { | |||||
| HITCBC | 122 | 25 | if (this != &other) | 122 | 25 | if (this != &other) | ||
| 123 | { | 123 | { | |||||
| HITCBC | 124 | 25 | if (impl_) | 124 | 25 | if (impl_) | ||
| 125 | { | 125 | { | |||||
| HITCBC | 126 | 25 | svc_->close(*this); | 126 | 25 | svc_->close(*this); | ||
| HITCBC | 127 | 25 | svc_->destroy(impl_); | 127 | 25 | svc_->destroy(impl_); | ||
| 128 | } | 128 | } | |||||
| HITCBC | 129 | 25 | ctx_ = std::exchange(other.ctx_, nullptr); | 129 | 25 | ctx_ = std::exchange(other.ctx_, nullptr); | ||
| HITCBC | 130 | 25 | svc_ = std::exchange(other.svc_, nullptr); | 130 | 25 | svc_ = std::exchange(other.svc_, nullptr); | ||
| HITCBC | 131 | 25 | impl_ = std::exchange(other.impl_, nullptr); | 131 | 25 | impl_ = std::exchange(other.impl_, nullptr); | ||
| 132 | } | 132 | } | |||||
| HITCBC | 133 | 25 | return *this; | 133 | 25 | return *this; | ||
| 134 | } | 134 | } | |||||
| 135 | 135 | |||||||
| 136 | handle(handle const&) = delete; | 136 | handle(handle const&) = delete; | |||||
| 137 | handle& operator=(handle const&) = delete; | 137 | handle& operator=(handle const&) = delete; | |||||
| 138 | 138 | |||||||
| 139 | /// Return true if the handle owns an implementation. | 139 | /// Return true if the handle owns an implementation. | |||||
| HITCBC | 140 | 47226 | explicit operator bool() const noexcept | 140 | 55700 | explicit operator bool() const noexcept | ||
| 141 | { | 141 | { | |||||
| HITCBC | 142 | 47226 | return impl_ != nullptr; | 142 | 55700 | return impl_ != nullptr; | ||
| 143 | } | 143 | } | |||||
| 144 | 144 | |||||||
| 145 | /// Return the associated I/O service. | 145 | /// Return the associated I/O service. | |||||
| HITCBC | 146 | 20122 | io_service& service() const noexcept | 146 | 23753 | io_service& service() const noexcept | ||
| 147 | { | 147 | { | |||||
| HITCBC | 148 | 20122 | return *svc_; | 148 | 23753 | return *svc_; | ||
| 149 | } | 149 | } | |||||
| 150 | 150 | |||||||
| 151 | /// Return the platform implementation. | 151 | /// Return the platform implementation. | |||||
| HITCBC | 152 | 543704 | implementation* get() const noexcept | 152 | 581937 | implementation* get() const noexcept | ||
| 153 | { | 153 | { | |||||
| HITCBC | 154 | 543704 | return impl_; | 154 | 581937 | return impl_; | ||
| 155 | } | 155 | } | |||||
| 156 | 156 | |||||||
| 157 | /** Replace the implementation, destroying the old one. | 157 | /** Replace the implementation, destroying the old one. | |||||
| 158 | 158 | |||||||
| 159 | @param p The new implementation to own. May be nullptr. | 159 | @param p The new implementation to own. May be nullptr. | |||||
| 160 | */ | 160 | */ | |||||
| HITCBC | 161 | 6303 | void reset(implementation* p) noexcept | 161 | 7514 | void reset(implementation* p) noexcept | ||
| 162 | { | 162 | { | |||||
| HITCBC | 163 | 6303 | if (impl_) | 163 | 7514 | if (impl_) | ||
| 164 | { | 164 | { | |||||
| HITCBC | 165 | 6303 | svc_->close(*this); | 165 | 7514 | svc_->close(*this); | ||
| HITCBC | 166 | 6303 | svc_->destroy(impl_); | 166 | 7514 | svc_->destroy(impl_); | ||
| 167 | } | 167 | } | |||||
| HITCBC | 168 | 6303 | impl_ = p; | 168 | 7514 | impl_ = p; | ||
| HITCBC | 169 | 6303 | } | 169 | 7514 | } | ||
| 170 | 170 | |||||||
| 171 | /// Return the execution context. | 171 | /// Return the execution context. | |||||
| HITCBC | 172 | 10 | capy::execution_context& context() const noexcept | 172 | 10 | capy::execution_context& context() const noexcept | ||
| 173 | { | 173 | { | |||||
| HITCBC | 174 | 10 | return *ctx_; | 174 | 10 | return *ctx_; | ||
| 175 | } | 175 | } | |||||
| 176 | }; | 176 | }; | |||||
| 177 | 177 | |||||||
| 178 | /// Return the execution context. | 178 | /// Return the execution context. | |||||
| HITCBC | 179 | 10 | capy::execution_context& context() const noexcept | 179 | 10 | capy::execution_context& context() const noexcept | ||
| 180 | { | 180 | { | |||||
| HITCBC | 181 | 10 | return h_.context(); | 181 | 10 | return h_.context(); | ||
| 182 | } | 182 | } | |||||
| 183 | 183 | |||||||
| 184 | protected: | 184 | protected: | |||||
| HITCBC | 185 | 20426 | virtual ~io_object() = default; | 185 | 24059 | virtual ~io_object() = default; | ||
| 186 | 186 | |||||||
| 187 | /// Default construct for virtual base initialization. | 187 | /// Default construct for virtual base initialization. | |||||
| MISUBC | 188 | ✗ | io_object() noexcept = default; | 188 | ✗ | io_object() noexcept = default; | ||
| 189 | 189 | |||||||
| 190 | /** Create a handle bound to a service found in the context. | 190 | /** Create a handle bound to a service found in the context. | |||||
| 191 | 191 | |||||||
| 192 | @tparam Service The service type whose key_type is used for lookup. | 192 | @tparam Service The service type whose key_type is used for lookup. | |||||
| 193 | @param ctx The execution context to search for the service. | 193 | @param ctx The execution context to search for the service. | |||||
| 194 | 194 | |||||||
| 195 | @return A handle owning a freshly constructed implementation. | 195 | @return A handle owning a freshly constructed implementation. | |||||
| 196 | 196 | |||||||
| 197 | @throws std::logic_error if the service is not installed. | 197 | @throws std::logic_error if the service is not installed. | |||||
| 198 | */ | 198 | */ | |||||
| 199 | template<class Service> | 199 | template<class Service> | |||||
| HITCBC | 200 | 13214 | static handle create_handle(capy::execution_context& ctx) | 200 | 15635 | static handle create_handle(capy::execution_context& ctx) | ||
| 201 | { | 201 | { | |||||
| HITCBC | 202 | 13214 | auto* svc = ctx.find_service<Service>(); | 202 | 15635 | auto* svc = ctx.find_service<Service>(); | ||
| HITCBC | 203 | 13214 | if (!svc) | 203 | 15635 | if (!svc) | ||
| MISUBC | 204 | ✗ | detail::throw_logic_error( | 204 | ✗ | detail::throw_logic_error( | ||
| 205 | "io_object::create_handle: service not installed"); | 205 | "io_object::create_handle: service not installed"); | |||||
| HITCBC | 206 | 13214 | return handle(ctx, *svc); | 206 | 15635 | return handle(ctx, *svc); | ||
| 207 | } | 207 | } | |||||
| 208 | 208 | |||||||
| 209 | /// Construct an I/O object from a handle. | 209 | /// Construct an I/O object from a handle. | |||||
| HITCBC | 210 | 20205 | explicit io_object(handle h) noexcept : h_(std::move(h)) {} | 210 | 23838 | explicit io_object(handle h) noexcept : h_(std::move(h)) {} | ||
| 211 | 211 | |||||||
| 212 | /// Move construct from another I/O object. | 212 | /// Move construct from another I/O object. | |||||
| HITCBC | 213 | 223 | io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {} | 213 | 223 | io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {} | ||
| 214 | 214 | |||||||
| 215 | /// Move assign from another I/O object. | 215 | /// Move assign from another I/O object. | |||||
| 216 | io_object& operator=(io_object&& other) noexcept | 216 | io_object& operator=(io_object&& other) noexcept | |||||
| 217 | { | 217 | { | |||||
| 218 | if (this != &other) | 218 | if (this != &other) | |||||
| 219 | h_ = std::move(other.h_); | 219 | h_ = std::move(other.h_); | |||||
| 220 | return *this; | 220 | return *this; | |||||
| 221 | } | 221 | } | |||||
| 222 | 222 | |||||||
| 223 | io_object(io_object const&) = delete; | 223 | io_object(io_object const&) = delete; | |||||
| 224 | io_object& operator=(io_object const&) = delete; | 224 | io_object& operator=(io_object const&) = delete; | |||||
| 225 | 225 | |||||||
| 226 | /// The platform I/O handle owned by this object. | 226 | /// The platform I/O handle owned by this object. | |||||
| 227 | handle h_; | 227 | handle h_; | |||||
| 228 | }; | 228 | }; | |||||
| 229 | 229 | |||||||
| 230 | } // namespace boost::corosio | 230 | } // namespace boost::corosio | |||||
| 231 | 231 | |||||||
| 232 | #endif | 232 | #endif | |||||