100.00% Lines (57/57) 100.00% Functions (20/20)
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 + /** Owns the platform-specific handle and execution context that a derived
  23 + socket, timer, signal handler, or acceptor type uses to dispatch
  24 + operations.
23   25  
24   Provides common infrastructure for I/O objects that wrap kernel 26   Provides common infrastructure for I/O objects that wrap kernel
25   resources (sockets, timers, signal handlers, acceptors). Derived 27   resources (sockets, timers, signal handlers, acceptors). Derived
26   classes dispatch operations through a platform-specific vtable 28   classes dispatch operations through a platform-specific vtable
27   (IOCP, epoll, kqueue, io_uring). 29   (IOCP, epoll, kqueue, io_uring).
28   30  
29   @par Semantics 31   @par Semantics
30   Only concrete platform I/O types should inherit from `io_object`. 32   Only concrete platform I/O types should inherit from `io_object`.
31   Test mocks, decorators, and stream adapters must not inherit from 33   Test mocks, decorators, and stream adapters must not inherit from
32   this class. Use concepts or templates for generic I/O algorithms. 34   this class. Use concepts or templates for generic I/O algorithms.
33   35  
34   @par Thread Safety 36   @par Thread Safety
35   Distinct objects: Safe. 37   Distinct objects: Safe.
36   Shared objects: Unsafe. All operations on a single I/O object 38   Shared objects: Unsafe. All operations on a single I/O object
37   must be serialized. 39   must be serialized.
38   40  
39   @note Intended as a protected base class. The handle member 41   @note Intended as a protected base class. The handle member
40   `h_` is accessible to derived classes. 42   `h_` is accessible to derived classes.
41   43  
42   @see io_stream, tcp_socket, tcp_acceptor 44   @see io_stream, tcp_socket, tcp_acceptor
43   */ 45   */
44   class BOOST_COROSIO_DECL io_object 46   class BOOST_COROSIO_DECL io_object
45   { 47   {
46   public: 48   public:
47   class handle; 49   class handle;
48   50  
49 - /** Base interface for platform I/O implementations. 51 + /** Derived types dispatch platform-specific I/O operations through it.
50 -  
51 - Derived classes provide platform-specific operation dispatch.  
52   */ 52   */
53   struct implementation 53   struct implementation
54   { 54   {
  55 + /// Destroy the implementation; called only through @ref io_service.
HITCBC 55   17121 virtual ~implementation() = default; 56   12470 virtual ~implementation() = default;
56   }; 57   };
57   58  
58 - /** Service interface for I/O object lifecycle management. 59 + /** Constructs, closes, and destroys platform implementations on
59 - 60 + behalf of an I/O object. Platform backends implement this
60 - Platform backends implement this interface to manage the 61 + interface.
61 - creation, closing, and destruction of I/O object  
62 - implementations.  
63   */ 62   */
64   struct BOOST_COROSIO_DECL io_service 63   struct BOOST_COROSIO_DECL io_service
65   { 64   {
  65 + /// Destroy the service; the execution context outlives it.
HITCBC 66   24651 virtual ~io_service() = default; 66   24651 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* impl) = 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 - 15159 virtual void close(handle&) {} 75 + 13496 virtual void close([[maybe_unused]] handle& h) {}
76   }; 76   };
77   77  
78 - /** RAII wrapper for I/O object implementation lifetime. 78 + /** Owns a platform-specific I/O implementation and destroys it
79 - 79 + when the handle goes out of scope.
80 - Manages ownership of the platform-specific implementation,  
81 - automatically destroying it when the handle goes out of scope.  
82   */ 80   */
83   class handle 81   class handle
84   { 82   {
85   capy::execution_context* ctx_ = nullptr; 83   capy::execution_context* ctx_ = nullptr;
86   io_service* svc_ = nullptr; 84   io_service* svc_ = nullptr;
87   implementation* impl_ = nullptr; 85   implementation* impl_ = nullptr;
88   86  
89   public: 87   public:
90   /// Destroy the handle and its implementation. 88   /// Destroy the handle and its implementation.
HITCBC 91   53340 ~handle() 89   44122 ~handle()
92   { 90   {
HITCBC 93   53340 if (impl_) 91   44122 if (impl_)
94   { 92   {
HITCBC 95   26078 svc_->close(*this); 93   21469 svc_->close(*this);
HITCBC 96   26078 svc_->destroy(impl_); 94   21469 svc_->destroy(impl_);
97   } 95   }
HITCBC 98   53340 } 96   44122 }
99   97  
100   /// Construct an empty handle. 98   /// Construct an empty handle.
HITCBC 101   10 handle() = default; 99   10 handle() = default;
102   100  
103   /// Construct a handle bound to a context and service. 101   /// Construct a handle bound to a context and service.
HITCBC 104   26140 handle(capy::execution_context& ctx, io_service& svc) 102   21531 handle(capy::execution_context& ctx, io_service& svc)
HITCBC 105   26140 : ctx_(&ctx) 103   21531 : ctx_(&ctx)
HITCBC 106   26140 , svc_(&svc) 104   21531 , svc_(&svc)
HITCBC 107   26140 , impl_(svc_->construct()) 105   21531 , impl_(svc_->construct())
108   { 106   {
HITCBC 109   26140 } 107   21531 }
110   108  
111   /// Move construct from another handle. 109   /// Move construct from another handle.
HITCBC 112   27211 handle(handle&& other) noexcept 110   22602 handle(handle&& other) noexcept
HITCBC 113   27211 : ctx_(std::exchange(other.ctx_, nullptr)) 111   22602 : ctx_(std::exchange(other.ctx_, nullptr))
HITCBC 114   27211 , svc_(std::exchange(other.svc_, nullptr)) 112   22602 , svc_(std::exchange(other.svc_, nullptr))
HITCBC 115   27211 , impl_(std::exchange(other.impl_, nullptr)) 113   22602 , impl_(std::exchange(other.impl_, nullptr))
116   { 114   {
HITCBC 117   27211 } 115   22602 }
118   116  
119   /// Move assign from another handle. 117   /// Move assign from another handle.
HITCBC 120   42 handle& operator=(handle&& other) noexcept 118   42 handle& operator=(handle&& other) noexcept
121   { 119   {
HITCBC 122   42 if (this != &other) 120   42 if (this != &other)
123   { 121   {
HITCBC 124   42 if (impl_) 122   42 if (impl_)
125   { 123   {
HITCBC 126   41 svc_->close(*this); 124   41 svc_->close(*this);
HITCBC 127   41 svc_->destroy(impl_); 125   41 svc_->destroy(impl_);
128   } 126   }
HITCBC 129   42 ctx_ = std::exchange(other.ctx_, nullptr); 127   42 ctx_ = std::exchange(other.ctx_, nullptr);
HITCBC 130   42 svc_ = std::exchange(other.svc_, nullptr); 128   42 svc_ = std::exchange(other.svc_, nullptr);
HITCBC 131   42 impl_ = std::exchange(other.impl_, nullptr); 129   42 impl_ = std::exchange(other.impl_, nullptr);
132   } 130   }
HITCBC 133   42 return *this; 131   42 return *this;
134   } 132   }
135   133  
136 - handle(handle const&) = delete; 134 + /// Copy construction is disabled; the implementation is uniquely owned.
  135 + handle(handle const&) = delete;
  136 + /// Copy assignment is disabled; the implementation is uniquely owned.
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   42075 explicit operator bool() const noexcept 140   31751 explicit operator bool() const noexcept
141   { 141   {
HITCBC 142   42075 return impl_ != nullptr; 142   31751 return impl_ != nullptr;
143   } 143   }
144   144  
145   /// Return the associated I/O service. 145   /// Return the associated I/O service.
HITCBC 146   18457 io_service& service() const noexcept 146   14038 io_service& service() const noexcept
147   { 147   {
HITCBC 148   18457 return *svc_; 148   14038 return *svc_;
149   } 149   }
150   150  
151   /// Return the platform implementation. 151   /// Return the platform implementation.
HITCBC 152   531979 implementation* get() const noexcept 152   500098 implementation* get() const noexcept
153   { 153   {
HITCBC 154   531979 return impl_; 154   500098 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   4254 void reset(implementation* p) noexcept 161   2781 void reset(implementation* p) noexcept
162   { 162   {
HITCBC 163   4254 if (impl_) 163   2781 if (impl_)
164   { 164   {
HITCBC 165   4254 svc_->close(*this); 165   2781 svc_->close(*this);
HITCBC 166   4254 svc_->destroy(impl_); 166   2781 svc_->destroy(impl_);
167   } 167   }
HITCBC 168   4254 impl_ = p; 168   2781 impl_ = p;
HITCBC 169   4254 } 169   2781 }
170   170  
171   /// Return the execution context. 171   /// Return the execution context.
HITCBC 172   39 capy::execution_context& context() const noexcept 172   39 capy::execution_context& context() const noexcept
173   { 173   {
HITCBC 174   39 return *ctx_; 174   39 return *ctx_;
175   } 175   }
176   }; 176   };
177   177  
178   /// Return the execution context. 178   /// Return the execution context.
HITCBC 179   39 capy::execution_context& context() const noexcept 179   39 capy::execution_context& context() const noexcept
180   { 180   {
HITCBC 181   39 return h_.context(); 181   39 return h_.context();
182   } 182   }
183   183  
184   protected: 184   protected:
  185 + /// Destroy the object; protected, so only a derived type destroys one.
HITCBC 185   26869 virtual ~io_object() = default; 186   22260 virtual ~io_object() = default;
186   187  
187   /// Default construct for virtual base initialization. 188   /// Default construct for virtual base initialization.
HITCBC 188   10 io_object() noexcept = default; 189   10 io_object() noexcept = default;
189   190  
190   /** Create a handle bound to a service found in the context. 191   /** Create a handle bound to a service found in the context.
191   192  
192   @tparam Service The service type whose key_type is used for lookup. 193   @tparam Service The service type whose key_type is used for lookup.
193   @param ctx The execution context to search for the service. 194   @param ctx The execution context to search for the service.
194   195  
195   @return A handle owning a freshly constructed implementation. 196   @return A handle owning a freshly constructed implementation.
196   197  
197   @throws std::logic_error if the service is not installed. 198   @throws std::logic_error if the service is not installed.
198   */ 199   */
199   template<class Service> 200   template<class Service>
HITCBC 200   26144 static handle create_handle(capy::execution_context& ctx) 201   21535 static handle create_handle(capy::execution_context& ctx)
201   { 202   {
HITCBC 202   26144 auto* svc = ctx.find_service<Service>(); 203   21535 auto* svc = ctx.find_service<Service>();
HITCBC 203   26144 if (!svc) 204   21535 if (!svc)
HITCBC 204   4 detail::throw_logic_error( 205   4 detail::throw_logic_error(
205   "io_object::create_handle: service not installed"); 206   "io_object::create_handle: service not installed");
HITCBC 206   26140 return handle(ctx, *svc); 207   21531 return handle(ctx, *svc);
207   } 208   }
208   209  
209   /// Construct an I/O object from a handle. 210   /// Construct an I/O object from a handle.
HITCBC 210   26140 explicit io_object(handle h) noexcept : h_(std::move(h)) {} 211   21531 explicit io_object(handle h) noexcept : h_(std::move(h)) {}
211   212  
212   /// Move construct from another I/O object. 213   /// Move construct from another I/O object.
HITCBC 213   740 io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {} 214   740 io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {}
214   215  
215   /// Move assign from another I/O object. 216   /// Move assign from another I/O object.
HITCBC 216   4 io_object& operator=(io_object&& other) noexcept 217   4 io_object& operator=(io_object&& other) noexcept
217   { 218   {
HITCBC 218   4 if (this != &other) 219   4 if (this != &other)
HITCBC 219   4 h_ = std::move(other.h_); 220   4 h_ = std::move(other.h_);
HITCBC 220   4 return *this; 221   4 return *this;
221   } 222   }
222   223  
223 - io_object(io_object const&) = delete; 224 + /// Copy construction is disabled; the handle is uniquely owned.
  225 + io_object(io_object const&) = delete;
  226 + /// Copy assignment is disabled; the handle is uniquely owned.
224   io_object& operator=(io_object const&) = delete; 227   io_object& operator=(io_object const&) = delete;
225   228  
226   /// The platform I/O handle owned by this object. 229   /// The platform I/O handle owned by this object.
227   BOOST_COROSIO_MSVC_WARNING_PUSH 230   BOOST_COROSIO_MSVC_WARNING_PUSH
228   BOOST_COROSIO_MSVC_WARNING_DISABLE(4251) 231   BOOST_COROSIO_MSVC_WARNING_DISABLE(4251)
229   handle h_; 232   handle h_;
230   BOOST_COROSIO_MSVC_WARNING_POP 233   BOOST_COROSIO_MSVC_WARNING_POP
231   }; 234   };
232   235  
233   } // namespace boost::corosio 236   } // namespace boost::corosio
234   237  
235   #endif 238   #endif