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

Generated by: LCOV version 2.3