LCOV - code coverage report
Current view: top level - corosio/io - io_object.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 100.0 % 57 57
Test Date: 2026-10-08 18:13:32 Functions: 87.0 % 23 20 3

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : // Copyright (c) 2026 Steve Gerbino
       4                 : //
       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)
       7                 : //
       8                 : // Official repository: https://github.com/cppalliance/corosio
       9                 : //
      10                 : 
      11                 : #ifndef BOOST_COROSIO_IO_IO_OBJECT_HPP
      12                 : #define BOOST_COROSIO_IO_IO_OBJECT_HPP
      13                 : 
      14                 : #include <boost/corosio/detail/config.hpp>
      15                 : #include <boost/corosio/detail/except.hpp>
      16                 : #include <boost/capy/ex/execution_context.hpp>
      17                 : 
      18                 : #include <atomic>
      19                 : #include <cstddef>
      20                 : #include <utility>
      21                 : 
      22                 : namespace boost::corosio {
      23                 : 
      24                 : /** Owns the platform-specific handle and execution context that a derived
      25                 :     socket, timer, signal handler, or acceptor type uses to dispatch
      26                 :     operations.
      27                 : 
      28                 :     Provides common infrastructure for I/O objects that wrap kernel
      29                 :     resources (sockets, timers, signal handlers, acceptors). Derived
      30                 :     classes dispatch operations through a platform-specific vtable
      31                 :     (IOCP, epoll, kqueue, io_uring).
      32                 : 
      33                 :     @par Semantics
      34                 :     Only concrete platform I/O types should inherit from `io_object`.
      35                 :     Test mocks, decorators, and stream adapters must not inherit from
      36                 :     this class. Use concepts or templates for generic I/O algorithms.
      37                 : 
      38                 :     @par Thread Safety
      39                 :     Distinct objects: Safe.
      40                 :     Shared objects: Unsafe. All operations on a single I/O object
      41                 :     must be serialized.
      42                 : 
      43                 :     @note Intended as a protected base class. The handle member
      44                 :         `h_` is accessible to derived classes.
      45                 : 
      46                 :     @see io_stream, tcp_socket, tcp_acceptor
      47                 : */
      48                 : class BOOST_COROSIO_DECL io_object
      49                 : {
      50                 : public:
      51                 :     class handle;
      52                 : 
      53                 :     /** Derived types dispatch platform-specific I/O operations through it.
      54                 : 
      55                 :         Reference-counted: the owning service holds one reference
      56                 :         (`refs_` starts at 1); in-flight operations hold additional
      57                 :         references through `detail::object_ref`. When the count reaches
      58                 :         zero, `retire()` runs — services recycle the impl into
      59                 :         their free-list there.
      60                 :     */
      61                 :     struct implementation
      62                 :     {
      63                 :         /// Destroy the implementation; called only through @ref io_service.
      64 HIT        7179 :         virtual ~implementation() = default;
      65                 : 
      66                 :         /** Retire the implementation once the reference count reaches zero.
      67                 : 
      68                 :             Every concrete implementation's override follows the
      69                 :             same shape: recycle into the owning service's pool member
      70                 :             as the final statement. The service befriends the
      71                 :             implementation so it can reach that private member
      72                 :             directly. Any required last-rites cleanup — releasing
      73                 :             state that is only safe to tear down once idle — runs as
      74                 :             ordinary statements immediately before the recycle.
      75                 :             Whichever action runs — recycle or delete — must be the
      76                 :             final statement: nothing touches the implementation
      77                 :             after.
      78                 :         */
      79                 :         virtual void retire() noexcept = 0;
      80                 : 
      81                 :         /// In-flight + service references; starts at the service's 1.
      82                 :         std::atomic<std::size_t> refs_{1};
      83                 :     };
      84                 : 
      85                 :     /** Constructs, closes, and destroys platform implementations on
      86                 :         behalf of an I/O object. Platform backends implement this
      87                 :         interface.
      88                 :     */
      89                 :     struct BOOST_COROSIO_DECL io_service
      90                 :     {
      91                 :         /// Destroy the service; the execution context outlives it.
      92            5069 :         virtual ~io_service() = default;
      93                 : 
      94                 :         /** Construct a new implementation instance.
      95                 : 
      96                 :             May return a recycled implementation taken from the
      97                 :             service's free-list (populated by `implementation::retire`)
      98                 :             instead of allocating new storage.
      99                 :         */
     100                 :         virtual implementation* construct() = 0;
     101                 : 
     102                 :         /** Close kernel resources and release the service's reference.
     103                 : 
     104                 :             Called whenever a handle relinquishes its current
     105                 :             implementation, not only on destruction. Handle
     106                 :             destruction, move-assignment onto a handle that already
     107                 :             owns one (the replaced implementation is destroyed, the
     108                 :             incoming one is not), and `handle::reset()` all invoke
     109                 :             this. It closes the underlying descriptor and drops the
     110                 :             service's own reference. In-flight operations may still
     111                 :             hold references of their own, so the implementation is
     112                 :             not necessarily recycled or freed here. That happens in
     113                 :             `implementation::retire` whenever the reference count
     114                 :             actually reaches zero.
     115                 :         */
     116                 :         virtual void destroy(implementation* impl) = 0;
     117                 : 
     118                 :         /// Close the I/O object, releasing kernel resources without deallocating.
     119           17618 :         virtual void close([[maybe_unused]] handle& h) {}
     120                 :     };
     121                 : 
     122                 :     /** Owns a platform-specific I/O implementation and destroys it
     123                 :         when the handle goes out of scope.
     124                 :     */
     125                 :     class handle
     126                 :     {
     127                 :         capy::execution_context* ctx_ = nullptr;
     128                 :         io_service* svc_              = nullptr;
     129                 :         implementation* impl_         = nullptr;
     130                 : 
     131                 :     public:
     132                 :         /// Destroy the handle and its implementation.
     133           61770 :         ~handle()
     134                 :         {
     135           61770 :             if (impl_)
     136                 :             {
     137           30248 :                 svc_->close(*this);
     138           30248 :                 svc_->destroy(impl_);
     139                 :             }
     140           61770 :         }
     141                 : 
     142                 :         /// Construct an empty handle.
     143              10 :         handle() = default;
     144                 : 
     145                 :         /// Construct a handle bound to a context and service.
     146           30310 :         handle(capy::execution_context& ctx, io_service& svc)
     147           30310 :             : ctx_(&ctx)
     148           30310 :             , svc_(&svc)
     149           30310 :             , impl_(svc_->construct())
     150                 :         {
     151           30310 :         }
     152                 : 
     153                 :         /// Move construct from another handle.
     154           31471 :         handle(handle&& other) noexcept
     155           31471 :             : ctx_(std::exchange(other.ctx_, nullptr))
     156           31471 :             , svc_(std::exchange(other.svc_, nullptr))
     157           31471 :             , impl_(std::exchange(other.impl_, nullptr))
     158                 :         {
     159           31471 :         }
     160                 : 
     161                 :         /// Move assign from another handle.
     162              42 :         handle& operator=(handle&& other) noexcept
     163                 :         {
     164              42 :             if (this != &other)
     165                 :             {
     166              42 :                 if (impl_)
     167                 :                 {
     168              41 :                     svc_->close(*this);
     169              41 :                     svc_->destroy(impl_);
     170                 :                 }
     171              42 :                 ctx_  = std::exchange(other.ctx_, nullptr);
     172              42 :                 svc_  = std::exchange(other.svc_, nullptr);
     173              42 :                 impl_ = std::exchange(other.impl_, nullptr);
     174                 :             }
     175              42 :             return *this;
     176                 :         }
     177                 : 
     178                 :         /// Copy construction is disabled; the implementation is uniquely owned.
     179                 :         handle(handle const&) = delete;
     180                 :         /// Copy assignment is disabled; the implementation is uniquely owned.
     181                 :         handle& operator=(handle const&) = delete;
     182                 : 
     183                 :         /// Return true if the handle owns an implementation.
     184           47210 :         explicit operator bool() const noexcept
     185                 :         {
     186           47210 :             return impl_ != nullptr;
     187                 :         }
     188                 : 
     189                 :         /// Return the associated I/O service.
     190           20649 :         io_service& service() const noexcept
     191                 :         {
     192           20649 :             return *svc_;
     193                 :         }
     194                 : 
     195                 :         /// Return the platform implementation.
     196          607665 :         implementation* get() const noexcept
     197                 :         {
     198          607665 :             return impl_;
     199                 :         }
     200                 : 
     201                 :         /** Replace the implementation, destroying the old one.
     202                 : 
     203                 :             @pre The handle is already bound to a service (constructed
     204                 :                 via the service-taking constructor, not the default
     205                 :                 one) — the old implementation is destroyed through
     206                 :                 that service.
     207                 :             @pre @p p, if non-null, was constructed by this handle's
     208                 :                 own service. The service that destroys it later must
     209                 :                 be the one that knows how to close it.
     210                 : 
     211                 :             @param p The new implementation to own. May be nullptr.
     212                 :         */
     213            4866 :         void reset(implementation* p) noexcept
     214                 :         {
     215            4866 :             if (impl_)
     216                 :             {
     217            4866 :                 svc_->close(*this);
     218            4866 :                 svc_->destroy(impl_);
     219                 :             }
     220            4866 :             impl_ = p;
     221            4866 :         }
     222                 : 
     223                 :         /// Return the execution context.
     224              39 :         capy::execution_context& context() const noexcept
     225                 :         {
     226              39 :             return *ctx_;
     227                 :         }
     228                 :     };
     229                 : 
     230                 :     /// Return the execution context.
     231              39 :     capy::execution_context& context() const noexcept
     232                 :     {
     233              39 :         return h_.context();
     234                 :     }
     235                 : 
     236                 : protected:
     237                 :     /// Destroy the object; protected, so only a derived type destroys one.
     238           31061 :     virtual ~io_object() = default;
     239                 : 
     240                 :     /// Default construct for virtual base initialization.
     241              10 :     io_object() noexcept = default;
     242                 : 
     243                 :     /** Create a handle bound to a service found in the context.
     244                 : 
     245                 :         @tparam Service The service type whose `key_type` is used for
     246                 :             lookup.
     247                 :         @param ctx The execution context to search for the service.
     248                 : 
     249                 :         @return A handle owning a freshly constructed implementation.
     250                 : 
     251                 :         @throws std::logic_error if the service is not installed.
     252                 :     */
     253                 :     template<class Service>
     254           17239 :     static handle create_handle(capy::execution_context& ctx)
     255                 :     {
     256           17239 :         auto* svc = ctx.find_service<Service>();
     257           17239 :         if (!svc)
     258               4 :             detail::throw_logic_error(
     259                 :                 "io_object::create_handle: service not installed");
     260           17235 :         return handle(ctx, *svc);
     261                 :     }
     262                 : 
     263                 :     /// Construct an I/O object from a handle.
     264           30310 :     explicit io_object(handle h) noexcept : h_(std::move(h)) {}
     265                 : 
     266                 :     /// Move construct from another I/O object.
     267             762 :     io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {}
     268                 : 
     269                 :     /// Move assign from another I/O object.
     270               4 :     io_object& operator=(io_object&& other) noexcept
     271                 :     {
     272               4 :         if (this != &other)
     273               4 :             h_ = std::move(other.h_);
     274               4 :         return *this;
     275                 :     }
     276                 : 
     277                 :     /// Copy construction is disabled; the handle is uniquely owned.
     278                 :     io_object(io_object const&) = delete;
     279                 :     /// Copy assignment is disabled; the handle is uniquely owned.
     280                 :     io_object& operator=(io_object const&) = delete;
     281                 : 
     282                 :     /// The platform I/O handle owned by this object.
     283                 :     BOOST_COROSIO_MSVC_WARNING_PUSH
     284                 :     BOOST_COROSIO_MSVC_WARNING_DISABLE(4251)
     285                 :     handle h_;
     286                 :     BOOST_COROSIO_MSVC_WARNING_POP
     287                 : };
     288                 : 
     289                 : } // namespace boost::corosio
     290                 : 
     291                 : #endif
        

Generated by: LCOV version 2.3