LCOV - code coverage report
Current view: top level - corosio/native/detail/reactor - reactor_acceptor.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 93.5 % 216 202 14
Test Date: 2026-08-21 20:48:07 Functions: 96.2 % 104 100 4

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Steve Gerbino
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/corosio
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
      12                 : 
      13                 : #include <boost/corosio/tcp_acceptor.hpp>
      14                 : #include <boost/corosio/wait_type.hpp>
      15                 : #include <boost/corosio/detail/intrusive.hpp>
      16                 : #include <boost/corosio/native/detail/reactor/reactor_op_base.hpp>
      17                 : #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
      18                 : #include <boost/corosio/native/detail/make_err.hpp>
      19                 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
      20                 : 
      21                 : #include <memory>
      22                 : #include <mutex>
      23                 : #include <utility>
      24                 : 
      25                 : #include <errno.h>
      26                 : #include <netinet/in.h>
      27                 : #include <sys/socket.h>
      28                 : #include <unistd.h>
      29                 : 
      30                 : namespace boost::corosio::detail {
      31                 : 
      32                 : /** CRTP base for reactor-backed acceptor implementations.
      33                 : 
      34                 :     Provides shared data members, trivial virtual overrides, and
      35                 :     non-virtual helper methods for cancellation and close. Concrete
      36                 :     backends inherit and add `cancel()`, `close_socket()`, and
      37                 :     `accept()` overrides that delegate to the `do_*` helpers.
      38                 : 
      39                 :     @tparam Derived   The concrete acceptor type (CRTP).
      40                 :     @tparam Service   The backend's acceptor service type.
      41                 :     @tparam Op        The backend's base op type.
      42                 :     @tparam AcceptOp  The backend's accept op type.
      43                 :     @tparam WaitOp    The backend's wait op type.
      44                 :     @tparam DescState The backend's descriptor_state type.
      45                 :     @tparam ImplBase  The public vtable base
      46                 :                       (tcp_acceptor::implementation or
      47                 :                        local_stream_acceptor::implementation).
      48                 :     @tparam Endpoint  The endpoint type (endpoint or local_endpoint).
      49                 : */
      50                 : template<
      51                 :     class Derived,
      52                 :     class Service,
      53                 :     class Op,
      54                 :     class AcceptOp,
      55                 :     class WaitOp,
      56                 :     class DescState,
      57                 :     class ImplBase = tcp_acceptor::implementation,
      58                 :     class Endpoint = endpoint>
      59                 : class reactor_acceptor
      60                 :     : public ImplBase
      61                 :     , public std::enable_shared_from_this<Derived>
      62                 :     , public intrusive_list<Derived>::node
      63                 : {
      64                 :     friend Derived;
      65                 : 
      66                 : protected:
      67                 :     // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
      68 HIT         538 :     explicit reactor_acceptor(Service& svc) noexcept : svc_(svc) {}
      69                 : 
      70                 : protected:
      71                 :     Service& svc_;
      72                 :     int fd_ = -1;
      73                 :     Endpoint local_endpoint_;
      74                 : 
      75                 : public:
      76                 :     /// Pending accept operation slot.
      77                 :     AcceptOp acc_;
      78                 : 
      79                 :     /// Pending wait-for-read operation slot.
      80                 :     WaitOp wait_rd_;
      81                 : 
      82                 :     /// Pending wait-for-write operation slot.
      83                 :     WaitOp wait_wr_;
      84                 : 
      85                 :     /// Pending wait-for-error operation slot.
      86                 :     WaitOp wait_er_;
      87                 : 
      88                 :     /// Per-descriptor state for persistent reactor registration.
      89                 :     DescState desc_state_;
      90                 : 
      91             538 :     ~reactor_acceptor() override = default;
      92                 : 
      93                 :     /// Return the underlying file descriptor.
      94              50 :     native_handle_type native_handle() const noexcept override
      95                 :     {
      96              50 :         return fd_;
      97                 :     }
      98                 : 
      99                 :     /// Release and return the native handle without closing it.
     100              18 :     native_handle_type release_socket() noexcept override
     101                 :     {
     102              18 :         return do_release_socket();
     103                 :     }
     104                 : 
     105                 :     /// Return the cached local endpoint.
     106            7393 :     Endpoint local_endpoint() const noexcept override
     107                 :     {
     108            7393 :         return local_endpoint_;
     109                 :     }
     110                 : 
     111                 :     /// Return true if the acceptor has an open file descriptor.
     112           10327 :     bool is_open() const noexcept override
     113                 :     {
     114           10327 :         return fd_ >= 0;
     115                 :     }
     116                 : 
     117                 :     /// Set a socket option.
     118             406 :     std::error_code set_option(
     119                 :         int level,
     120                 :         int optname,
     121                 :         void const* data,
     122                 :         std::size_t size) noexcept override
     123                 :     {
     124             406 :         if (::setsockopt(
     125             406 :                 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
     126               4 :             return make_err(errno);
     127             402 :         return {};
     128                 :     }
     129                 : 
     130                 :     /// Get a socket option.
     131                 :     std::error_code
     132              12 :     get_option(int level, int optname, void* data, std::size_t* size)
     133                 :         const noexcept override
     134                 :     {
     135              12 :         socklen_t len = static_cast<socklen_t>(*size);
     136              12 :         if (::getsockopt(fd_, level, optname, data, &len) != 0)
     137               4 :             return make_err(errno);
     138               8 :         *size = static_cast<std::size_t>(len);
     139               8 :         return {};
     140                 :     }
     141                 : 
     142                 :     /// Cache the local endpoint.
     143             471 :     void set_local_endpoint(Endpoint ep) noexcept
     144                 :     {
     145             471 :         local_endpoint_ = std::move(ep);
     146             471 :     }
     147                 : 
     148                 :     /// Assign the fd and initialize descriptor state for the acceptor.
     149             503 :     void init_acceptor_fd(int fd) noexcept
     150                 :     {
     151             503 :         fd_ = fd;
     152             503 :         desc_state_.fd = fd;
     153                 :         {
     154             503 :             std::lock_guard lock(desc_state_.mutex);
     155             503 :             desc_state_.read_op       = nullptr;
     156             503 :             desc_state_.wait_read_op  = nullptr;
     157             503 :             desc_state_.wait_write_op = nullptr;
     158             503 :             desc_state_.wait_error_op = nullptr;
     159             503 :         }
     160             503 :     }
     161                 : 
     162                 :     /** Assign the fd, initialize descriptor state, and register with
     163                 :         the reactor.
     164                 : 
     165                 :         Adoption skips `do_listen`, so the registration it performs
     166                 :         has to happen here instead.
     167                 : 
     168                 :         @param fd The already-listening descriptor to adopt.
     169                 : 
     170                 :         @return The error if the reactor rejects the descriptor, in
     171                 :         which case the implementation is left closed and the caller
     172                 :         retains ownership of @a fd; otherwise a default constructed
     173                 :         error code.
     174                 :     */
     175              16 :     std::error_code init_and_register(int fd) noexcept
     176                 :     {
     177              16 :         init_acceptor_fd(fd);
     178              16 :         if (auto ec = svc_.scheduler().register_descriptor(fd, &desc_state_))
     179                 :         {
     180 MIS           0 :             fd_                           = -1;
     181               0 :             desc_state_.fd                = -1;
     182               0 :             desc_state_.registered_events = 0;
     183               0 :             return ec;
     184                 :         }
     185 HIT          16 :         return {};
     186                 :     }
     187                 : 
     188                 :     /// Return a reference to the owning service.
     189            7020 :     Service& service() noexcept
     190                 :     {
     191            7020 :         return svc_;
     192                 :     }
     193                 : 
     194              17 :     void cancel() noexcept override { do_cancel(); }
     195                 : 
     196                 :     /// Close the acceptor (non-virtual, called by the service).
     197            2060 :     void close_socket() noexcept { do_close_socket(); }
     198                 : 
     199              31 :     std::coroutine_handle<> wait(
     200                 :         std::coroutine_handle<> h,
     201                 :         capy::executor_ref ex,
     202                 :         wait_type w,
     203                 :         std::stop_token token,
     204                 :         std::error_code* ec) override
     205                 :     {
     206              31 :         return do_wait(h, ex, w, token, ec);
     207                 :     }
     208                 : 
     209                 :     /** Wait for readiness on the listen socket.
     210                 : 
     211                 :         For `wait_type::read`, completion signals that an incoming
     212                 :         connection is pending and a subsequent accept will succeed
     213                 :         without blocking; a connection already queued when the wait
     214                 :         begins completes it immediately via an initiation probe.
     215                 : 
     216                 :         `wait_type::write` fails with `operation_not_supported` on
     217                 :         every backend: writability carries no meaning for a
     218                 :         listening socket.
     219                 :     */
     220                 :     std::coroutine_handle<> do_wait(
     221                 :         std::coroutine_handle<>,
     222                 :         capy::executor_ref,
     223                 :         wait_type,
     224                 :         std::stop_token const&,
     225                 :         std::error_code*);
     226                 : 
     227                 :     /** Cancel a single pending operation.
     228                 : 
     229                 :         Claims the operation from the read_op descriptor slot
     230                 :         under the mutex and posts it to the scheduler as cancelled.
     231                 : 
     232                 :         @param op The operation to cancel.
     233                 :     */
     234                 :     void cancel_single_op(Op& op) noexcept;
     235                 : 
     236                 :     /** Cancel the pending accept operation. */
     237                 :     void do_cancel() noexcept;
     238                 : 
     239                 :     /** Close the acceptor and cancel pending operations.
     240                 : 
     241                 :         Invoked by the derived class's close_socket(). The
     242                 :         derived class may add backend-specific cleanup after
     243                 :         calling this method.
     244                 :     */
     245                 :     void do_close_socket() noexcept;
     246                 : 
     247                 :     /** Release the acceptor without closing the fd. */
     248                 :     native_handle_type do_release_socket() noexcept;
     249                 : 
     250                 :     /** Bind the acceptor socket to an endpoint.
     251                 : 
     252                 :         Caches the resolved local endpoint (including ephemeral
     253                 :         port) after a successful bind.
     254                 : 
     255                 :         @param ep The endpoint to bind to.
     256                 :         @return The error code from bind(), or success.
     257                 :     */
     258                 :     std::error_code do_bind(Endpoint const& ep);
     259                 : 
     260                 :     /** Start listening on the acceptor socket.
     261                 : 
     262                 :         Registers the file descriptor with the reactor after
     263                 :         a successful listen() call.
     264                 : 
     265                 :         @param backlog The listen backlog.
     266                 :         @return The error code from listen() or from reactor
     267                 :         registration, or success.
     268                 :     */
     269                 :     std::error_code do_listen(int backlog);
     270                 : };
     271                 : 
     272                 : template<
     273                 :     class Derived,
     274                 :     class Service,
     275                 :     class Op,
     276                 :     class AcceptOp,
     277                 :     class WaitOp,
     278                 :     class DescState,
     279                 :     class ImplBase,
     280                 :     class Endpoint>
     281                 : void
     282             101 : reactor_acceptor<Derived, Service, Op, AcceptOp, WaitOp, DescState, ImplBase, Endpoint>::
     283                 :     cancel_single_op(Op& op) noexcept
     284                 : {
     285             101 :     auto self = this->weak_from_this().lock();
     286             101 :     if (!self)
     287 MIS           0 :         return;
     288                 : 
     289 HIT         101 :     op.request_cancel();
     290                 : 
     291             101 :     reactor_op_base* claimed = nullptr;
     292                 :     {
     293             101 :         std::lock_guard lock(desc_state_.mutex);
     294             909 :         auto try_claim = [&](reactor_op_base*& slot) {
     295             404 :             if (!claimed && slot == &op)
     296              40 :                 claimed = std::exchange(slot, nullptr);
     297                 :         };
     298             101 :         try_claim(desc_state_.read_op);
     299             101 :         try_claim(desc_state_.wait_read_op);
     300             101 :         try_claim(desc_state_.wait_write_op);
     301             101 :         try_claim(desc_state_.wait_error_op);
     302             101 :     }
     303             101 :     if (claimed)
     304                 :     {
     305              40 :         op.impl_ptr = self;
     306              40 :         svc_.post(&op);
     307              40 :         svc_.work_finished();
     308                 :     }
     309             101 : }
     310                 : 
     311                 : template<
     312                 :     class Derived,
     313                 :     class Service,
     314                 :     class Op,
     315                 :     class AcceptOp,
     316                 :     class WaitOp,
     317                 :     class DescState,
     318                 :     class ImplBase,
     319                 :     class Endpoint>
     320                 : void
     321              17 : reactor_acceptor<Derived, Service, Op, AcceptOp, WaitOp, DescState, ImplBase, Endpoint>::
     322                 :     do_cancel() noexcept
     323                 : {
     324              17 :     cancel_single_op(acc_);
     325              17 :     cancel_single_op(wait_rd_);
     326              17 :     cancel_single_op(wait_wr_);
     327              17 :     cancel_single_op(wait_er_);
     328              17 : }
     329                 : 
     330                 : template<
     331                 :     class Derived,
     332                 :     class Service,
     333                 :     class Op,
     334                 :     class AcceptOp,
     335                 :     class WaitOp,
     336                 :     class DescState,
     337                 :     class ImplBase,
     338                 :     class Endpoint>
     339                 : void
     340            2060 : reactor_acceptor<Derived, Service, Op, AcceptOp, WaitOp, DescState, ImplBase, Endpoint>::
     341                 :     do_close_socket() noexcept
     342                 : {
     343            2060 :     auto self = this->weak_from_this().lock();
     344            2060 :     if (self)
     345                 :     {
     346            2060 :         acc_.request_cancel();
     347            2060 :         wait_rd_.request_cancel();
     348            2060 :         wait_wr_.request_cancel();
     349            2060 :         wait_er_.request_cancel();
     350                 : 
     351            2060 :         reactor_op_base* claimed_acc = nullptr;
     352            2060 :         reactor_op_base* claimed_wr  = nullptr;
     353            2060 :         reactor_op_base* claimed_ww  = nullptr;
     354            2060 :         reactor_op_base* claimed_we  = nullptr;
     355                 :         {
     356            2060 :             std::lock_guard lock(desc_state_.mutex);
     357            2060 :             claimed_acc = std::exchange(desc_state_.read_op, nullptr);
     358            2060 :             claimed_wr  = std::exchange(desc_state_.wait_read_op, nullptr);
     359            2060 :             claimed_ww  = std::exchange(desc_state_.wait_write_op, nullptr);
     360            2060 :             claimed_we  = std::exchange(desc_state_.wait_error_op, nullptr);
     361            2060 :             desc_state_.read_ready  = false;
     362            2060 :             desc_state_.write_ready = false;
     363                 : 
     364            2060 :             if (desc_state_.is_enqueued_.load(std::memory_order_acquire))
     365 MIS           0 :                 desc_state_.impl_ref_ = self;
     366 HIT        2060 :         }
     367                 : 
     368           18540 :         auto repost = [&](reactor_op_base* claimed, reactor_op_base& op) {
     369            8240 :             if (claimed)
     370                 :             {
     371               8 :                 op.impl_ptr = self;
     372               8 :                 svc_.post(&op);
     373               8 :                 svc_.work_finished();
     374                 :             }
     375                 :         };
     376            2060 :         repost(claimed_acc, acc_);
     377            2060 :         repost(claimed_wr, wait_rd_);
     378            2060 :         repost(claimed_ww, wait_wr_);
     379            2060 :         repost(claimed_we, wait_er_);
     380                 :     }
     381                 : 
     382            2060 :     if (fd_ >= 0)
     383                 :     {
     384             485 :         if (desc_state_.registered_events != 0)
     385             420 :             svc_.scheduler().deregister_descriptor(fd_);
     386             485 :         ::close(fd_);
     387             485 :         fd_ = -1;
     388                 :     }
     389                 : 
     390            2060 :     desc_state_.fd                = -1;
     391            2060 :     desc_state_.registered_events = 0;
     392                 : 
     393            2060 :     local_endpoint_ = Endpoint{};
     394            2060 : }
     395                 : 
     396                 : template<
     397                 :     class Derived,
     398                 :     class Service,
     399                 :     class Op,
     400                 :     class AcceptOp,
     401                 :     class WaitOp,
     402                 :     class DescState,
     403                 :     class ImplBase,
     404                 :     class Endpoint>
     405                 : native_handle_type
     406              18 : reactor_acceptor<Derived, Service, Op, AcceptOp, WaitOp, DescState, ImplBase, Endpoint>::
     407                 :     do_release_socket() noexcept
     408                 : {
     409              18 :     auto self = this->weak_from_this().lock();
     410              18 :     if (self)
     411                 :     {
     412              18 :         acc_.request_cancel();
     413              18 :         wait_rd_.request_cancel();
     414              18 :         wait_wr_.request_cancel();
     415              18 :         wait_er_.request_cancel();
     416                 : 
     417              18 :         reactor_op_base* claimed_acc = nullptr;
     418              18 :         reactor_op_base* claimed_wr  = nullptr;
     419              18 :         reactor_op_base* claimed_ww  = nullptr;
     420              18 :         reactor_op_base* claimed_we  = nullptr;
     421                 :         {
     422              18 :             std::lock_guard lock(desc_state_.mutex);
     423              18 :             claimed_acc = std::exchange(desc_state_.read_op, nullptr);
     424              18 :             claimed_wr  = std::exchange(desc_state_.wait_read_op, nullptr);
     425              18 :             claimed_ww  = std::exchange(desc_state_.wait_write_op, nullptr);
     426              18 :             claimed_we  = std::exchange(desc_state_.wait_error_op, nullptr);
     427              18 :             desc_state_.read_ready  = false;
     428              18 :             desc_state_.write_ready = false;
     429                 : 
     430              18 :             if (desc_state_.is_enqueued_.load(std::memory_order_acquire))
     431 MIS           0 :                 desc_state_.impl_ref_ = self;
     432 HIT          18 :         }
     433                 : 
     434             162 :         auto repost = [&](reactor_op_base* claimed, reactor_op_base& op) {
     435              72 :             if (claimed)
     436                 :             {
     437 MIS           0 :                 op.impl_ptr = self;
     438               0 :                 svc_.post(&op);
     439               0 :                 svc_.work_finished();
     440                 :             }
     441                 :         };
     442 HIT          18 :         repost(claimed_acc, acc_);
     443              18 :         repost(claimed_wr, wait_rd_);
     444              18 :         repost(claimed_ww, wait_wr_);
     445              18 :         repost(claimed_we, wait_er_);
     446                 :     }
     447                 : 
     448              18 :     native_handle_type released = fd_;
     449                 : 
     450              18 :     if (fd_ >= 0)
     451                 :     {
     452              18 :         if (desc_state_.registered_events != 0)
     453              18 :             svc_.scheduler().deregister_descriptor(fd_);
     454              18 :         fd_ = -1;
     455                 :     }
     456                 : 
     457              18 :     desc_state_.fd                = -1;
     458              18 :     desc_state_.registered_events = 0;
     459                 : 
     460              18 :     local_endpoint_ = Endpoint{};
     461                 : 
     462              36 :     return released;
     463              18 : }
     464                 : 
     465                 : template<
     466                 :     class Derived,
     467                 :     class Service,
     468                 :     class Op,
     469                 :     class AcceptOp,
     470                 :     class WaitOp,
     471                 :     class DescState,
     472                 :     class ImplBase,
     473                 :     class Endpoint>
     474                 : std::error_code
     475             469 : reactor_acceptor<Derived, Service, Op, AcceptOp, WaitOp, DescState, ImplBase, Endpoint>::
     476                 :     do_bind(Endpoint const& ep)
     477                 : {
     478             469 :     sockaddr_storage storage{};
     479             469 :     socklen_t addrlen = to_sockaddr(ep, storage);
     480             469 :     if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) < 0)
     481              14 :         return make_err(errno);
     482                 : 
     483                 :     // Cache local endpoint (resolves ephemeral port / path)
     484             455 :     sockaddr_storage local{};
     485             455 :     socklen_t local_len = sizeof(local);
     486             455 :     if (::getsockname(fd_, reinterpret_cast<sockaddr*>(&local), &local_len) ==
     487                 :         0)
     488             455 :         set_local_endpoint(from_sockaddr_as(local, local_len, Endpoint{}));
     489                 : 
     490             455 :     return {};
     491                 : }
     492                 : 
     493                 : template<
     494                 :     class Derived,
     495                 :     class Service,
     496                 :     class Op,
     497                 :     class AcceptOp,
     498                 :     class WaitOp,
     499                 :     class DescState,
     500                 :     class ImplBase,
     501                 :     class Endpoint>
     502                 : std::error_code
     503             424 : reactor_acceptor<Derived, Service, Op, AcceptOp, WaitOp, DescState, ImplBase, Endpoint>::
     504                 :     do_listen(int backlog)
     505                 : {
     506             424 :     if (::listen(fd_, backlog) < 0)
     507 MIS           0 :         return make_err(errno);
     508                 : 
     509                 :     // A re-listen only changes the backlog; the descriptor is already
     510                 :     // registered and re-adding it would fail on epoll.
     511 HIT         424 :     if (desc_state_.registered_events != 0)
     512               2 :         return {};
     513                 : 
     514             422 :     return svc_.scheduler().register_descriptor(fd_, &desc_state_);
     515                 : }
     516                 : 
     517                 : template<
     518                 :     class Derived,
     519                 :     class Service,
     520                 :     class Op,
     521                 :     class AcceptOp,
     522                 :     class WaitOp,
     523                 :     class DescState,
     524                 :     class ImplBase,
     525                 :     class Endpoint>
     526                 : std::coroutine_handle<>
     527              31 : reactor_acceptor<Derived, Service, Op, AcceptOp, WaitOp, DescState, ImplBase, Endpoint>::
     528                 :     do_wait(
     529                 :         std::coroutine_handle<> h,
     530                 :         capy::executor_ref ex,
     531                 :         wait_type w,
     532                 :         std::stop_token const& token,
     533                 :         std::error_code* ec)
     534                 : {
     535                 :     // Writability carries no meaning for a listening socket; some
     536                 :     // backends could only lie about it and others could never report
     537                 :     // it, so the wait fails the same way everywhere instead.
     538              31 :     if (w == wait_type::write)
     539                 :     {
     540               6 :         auto& op = wait_wr_;
     541               6 :         op.reset();
     542               6 :         op.wait_event = reactor_event_write;
     543               6 :         op.h          = h;
     544               6 :         op.ex         = ex;
     545               6 :         op.ec_out     = ec;
     546               6 :         op.fd         = this->fd_;
     547               6 :         op.start(token, static_cast<Derived*>(this));
     548               6 :         op.impl_ptr   = this->shared_from_this();
     549               6 :         op.complete(ENOTSUP, 0);
     550               6 :         svc_.post(&op);
     551               6 :         return std::noop_coroutine();
     552                 :     }
     553                 : 
     554                 :     WaitOp* op_ptr;
     555                 :     reactor_op_base** desc_slot_ptr;
     556                 :     std::uint32_t event;
     557                 : 
     558              25 :     if (w == wait_type::read)
     559                 :     {
     560              19 :         op_ptr        = &wait_rd_;
     561              19 :         desc_slot_ptr = &desc_state_.wait_read_op;
     562              19 :         event         = reactor_event_read;
     563                 :     }
     564                 :     else // wait_type::error
     565                 :     {
     566               6 :         op_ptr        = &wait_er_;
     567               6 :         desc_slot_ptr = &desc_state_.wait_error_op;
     568               6 :         event         = reactor_event_error;
     569                 :     }
     570                 : 
     571              25 :     auto& op      = *op_ptr;
     572              25 :     op.reset();
     573              25 :     op.wait_event = event;
     574              25 :     op.h          = h;
     575              25 :     op.ex         = ex;
     576              25 :     op.ec_out     = ec;
     577              25 :     op.fd         = this->fd_;
     578              25 :     op.start(token, static_cast<Derived*>(this));
     579              25 :     op.impl_ptr   = this->shared_from_this();
     580                 : 
     581                 :     // A listener's readiness can predate the wait: an adopted or
     582                 :     // shared descriptor has history the reactor never saw, and an
     583                 :     // edge already dispatched will not be re-announced. Probe before
     584                 :     // parking.
     585              25 :     int perr = 0;
     586              25 :     if (WaitOp::probe(this->fd_, event, perr))
     587                 :     {
     588               8 :         op.complete(perr, 0);
     589               8 :         svc_.post(&op);
     590               8 :         return std::noop_coroutine();
     591                 :     }
     592                 : 
     593              17 :     svc_.work_started();
     594                 : 
     595              17 :     std::lock_guard lock(desc_state_.mutex);
     596              17 :     if (op.cancelled.load(std::memory_order_acquire))
     597                 :     {
     598               2 :         svc_.post(&op);
     599               2 :         svc_.work_finished();
     600                 :     }
     601              15 :     else if (WaitOp::probe(this->fd_, event, perr))
     602                 :     {
     603                 :         // Close the probe-to-park window: an edge that landed after
     604                 :         // the first probe was consumed, so re-check under the mutex
     605                 :         // the dispatch path holds.
     606 MIS           0 :         op.complete(perr, 0);
     607               0 :         svc_.post(&op);
     608               0 :         svc_.work_finished();
     609                 :     }
     610                 :     else
     611                 :     {
     612 HIT          15 :         *desc_slot_ptr = &op;
     613                 :     }
     614              17 :     return std::noop_coroutine();
     615              17 : }
     616                 : 
     617                 : } // namespace boost::corosio::detail
     618                 : 
     619                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
        

Generated by: LCOV version 2.3