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 % 62 62
Test Date: 2026-10-17 04:01:32 Functions: 87.5 % 24 21 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       17668 :         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            8907 :         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           24019 :         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          105857 :         ~handle()
     134                 :         {
     135          105857 :             if (impl_)
     136                 :             {
     137           48024 :                 svc_->close(*this);
     138           48024 :                 svc_->destroy(impl_);
     139                 :             }
     140          105857 :         }
     141                 : 
     142                 :         /// Construct an empty handle.
     143              10 :         handle() = default;
     144                 : 
     145                 :         /** Construct a handle that takes over an existing implementation.
     146                 : 
     147                 :             @pre @p p, if non-null, was constructed by @p svc and is
     148                 :                 owned by no other handle.
     149                 : 
     150                 :             @param ctx The context @p svc belongs to.
     151                 :             @param svc The service that constructed @p p.
     152                 :             @param p The implementation to own.
     153                 :         */
     154               8 :         handle(
     155                 :             capy::execution_context& ctx,
     156                 :             io_service& svc,
     157                 :             implementation* p) noexcept
     158               8 :             : ctx_(&ctx)
     159               8 :             , svc_(&svc)
     160               8 :             , impl_(p)
     161                 :         {
     162               8 :         }
     163                 : 
     164                 :         /// Construct a handle bound to a context and service.
     165           48058 :         handle(capy::execution_context& ctx, io_service& svc)
     166           48058 :             : ctx_(&ctx)
     167           48058 :             , svc_(&svc)
     168           48058 :             , impl_(svc_->construct())
     169                 :         {
     170           48058 :         }
     171                 : 
     172                 :         /// Move construct from another handle.
     173           57781 :         handle(handle&& other) noexcept
     174           57781 :             : ctx_(std::exchange(other.ctx_, nullptr))
     175           57781 :             , svc_(std::exchange(other.svc_, nullptr))
     176           57781 :             , impl_(std::exchange(other.impl_, nullptr))
     177                 :         {
     178           57781 :         }
     179                 : 
     180                 :         /// Move assign from another handle.
     181              43 :         handle& operator=(handle&& other) noexcept
     182                 :         {
     183              43 :             if (this != &other)
     184                 :             {
     185              43 :                 if (impl_)
     186                 :                 {
     187              42 :                     svc_->close(*this);
     188              42 :                     svc_->destroy(impl_);
     189                 :                 }
     190              43 :                 ctx_  = std::exchange(other.ctx_, nullptr);
     191              43 :                 svc_  = std::exchange(other.svc_, nullptr);
     192              43 :                 impl_ = std::exchange(other.impl_, nullptr);
     193                 :             }
     194              43 :             return *this;
     195                 :         }
     196                 : 
     197                 :         /// Copy construction is disabled; the implementation is uniquely owned.
     198                 :         handle(handle const&) = delete;
     199                 :         /// Copy assignment is disabled; the implementation is uniquely owned.
     200                 :         handle& operator=(handle const&) = delete;
     201                 : 
     202                 :         /// Return true if the handle owns an implementation.
     203           96285 :         explicit operator bool() const noexcept
     204                 :         {
     205           96285 :             return impl_ != nullptr;
     206                 :         }
     207                 : 
     208                 :         /// Return the associated I/O service.
     209           45186 :         io_service& service() const noexcept
     210                 :         {
     211           45186 :             return *svc_;
     212                 :         }
     213                 : 
     214                 :         /// Return the platform implementation.
     215         1140330 :         implementation* get() const noexcept
     216                 :         {
     217         1140330 :             return impl_;
     218                 :         }
     219                 : 
     220                 :         /** Replace the implementation, destroying the old one.
     221                 : 
     222                 :             @pre The handle is already bound to a service (constructed
     223                 :                 via the service-taking constructor, not the default
     224                 :                 one) — the old implementation is destroyed through
     225                 :                 that service.
     226                 :             @pre @p p, if non-null, was constructed by this handle's
     227                 :                 own service. The service that destroys it later must
     228                 :                 be the one that knows how to close it.
     229                 : 
     230                 :             @param p The new implementation to own. May be nullptr.
     231                 :         */
     232            6503 :         void reset(implementation* p) noexcept
     233                 :         {
     234            6503 :             if (impl_)
     235                 :             {
     236            6503 :                 svc_->close(*this);
     237            6503 :                 svc_->destroy(impl_);
     238                 :             }
     239            6503 :             impl_ = p;
     240            6503 :         }
     241                 : 
     242                 :         /// Return the execution context.
     243              41 :         capy::execution_context& context() const noexcept
     244                 :         {
     245              41 :             return *ctx_;
     246                 :         }
     247                 :     };
     248                 : 
     249                 :     /// Return the execution context.
     250              41 :     capy::execution_context& context() const noexcept
     251                 :     {
     252              41 :         return h_.context();
     253                 :     }
     254                 : 
     255                 : protected:
     256                 :     /// Destroy the object; protected, so only a derived type destroys one.
     257           54174 :     virtual ~io_object() = default;
     258                 : 
     259                 :     /// Default construct for virtual base initialization.
     260              10 :     io_object() noexcept = default;
     261                 : 
     262                 :     /** Create a handle bound to a service found in the context.
     263                 : 
     264                 :         @tparam Service The service type whose `key_type` is used for
     265                 :             lookup.
     266                 :         @param ctx The execution context to search for the service.
     267                 : 
     268                 :         @return A handle owning a freshly constructed implementation.
     269                 : 
     270                 :         @throws std::logic_error if the service is not installed.
     271                 :     */
     272                 :     template<class Service>
     273           20405 :     static handle create_handle(capy::execution_context& ctx)
     274                 :     {
     275           20405 :         auto* svc = ctx.find_service<Service>();
     276           20405 :         if (!svc)
     277               4 :             detail::throw_logic_error(
     278                 :                 "io_object::create_handle: service not installed");
     279           20401 :         return handle(ctx, *svc);
     280                 :     }
     281                 : 
     282                 :     /// Construct an I/O object from a handle.
     283           48058 :     explicit io_object(handle h) noexcept : h_(std::move(h)) {}
     284                 : 
     285                 :     /// Move construct from another I/O object.
     286            6106 :     io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {}
     287                 : 
     288                 :     /// Move assign from another I/O object.
     289               4 :     io_object& operator=(io_object&& other) noexcept
     290                 :     {
     291               4 :         if (this != &other)
     292               4 :             h_ = std::move(other.h_);
     293               4 :         return *this;
     294                 :     }
     295                 : 
     296                 :     /// Copy construction is disabled; the handle is uniquely owned.
     297                 :     io_object(io_object const&) = delete;
     298                 :     /// Copy assignment is disabled; the handle is uniquely owned.
     299                 :     io_object& operator=(io_object const&) = delete;
     300                 : 
     301                 :     /// The platform I/O handle owned by this object.
     302                 :     BOOST_COROSIO_MSVC_WARNING_PUSH
     303                 :     BOOST_COROSIO_MSVC_WARNING_DISABLE(4251)
     304                 :     handle h_;
     305                 :     BOOST_COROSIO_MSVC_WARNING_POP
     306                 : };
     307                 : 
     308                 : } // namespace boost::corosio
     309                 : 
     310                 : #endif
        

Generated by: LCOV version 2.3