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: 98.2 % 220 216 4
Test Date: 2026-09-09 02:31:18 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         760 :     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             760 :     ~reactor_acceptor() override = default;
      92                 : 
      93                 :     /// Return the underlying file descriptor.
      94              59 :     native_handle_type native_handle() const noexcept override
      95                 :     {
      96              59 :         return fd_;
      97                 :     }
      98                 : 
      99                 :     /// Release and return the native handle without closing it.
     100              22 :     native_handle_type release_socket() noexcept override
     101                 :     {
     102              22 :         return do_release_socket();
     103                 :     }
     104                 : 
     105                 :     /// Return the cached local endpoint.
     106            5046 :     Endpoint local_endpoint() const noexcept override
     107                 :     {
     108            5046 :         return local_endpoint_;
     109                 :     }
     110                 : 
     111                 :     /// Return true if the acceptor has an open file descriptor.
     112            9253 :     bool is_open() const noexcept override
     113                 :     {
     114            9253 :         return fd_ >= 0;
     115                 :     }
     116                 : 
     117                 :     /// Set a socket option.
     118             599 :     std::error_code set_option(
     119                 :         int level,
     120                 :         int optname,
     121                 :         void const* data,
     122                 :         std::size_t size) noexcept override
     123                 :     {
     124             599 :         if (::setsockopt(
     125             599 :                 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
     126              10 :             return make_err(errno);
     127             589 :         return {};
     128                 :     }
     129                 : 
     130                 :     /// Get a socket option.
     131                 :     std::error_code
     132              25 :     get_option(int level, int optname, void* data, std::size_t* size)
     133                 :         const noexcept override
     134                 :     {
     135              25 :         socklen_t len = static_cast<socklen_t>(*size);
     136              25 :         if (::getsockopt(fd_, level, optname, data, &len) != 0)
     137              10 :             return make_err(errno);
     138              15 :         *size = static_cast<std::size_t>(len);
     139              15 :         return {};
     140                 :     }
     141                 : 
     142                 :     /// Cache the local endpoint.
     143             673 :     void set_local_endpoint(Endpoint ep) noexcept
     144                 :     {
     145             673 :         local_endpoint_ = std::move(ep);
     146             673 :     }
     147                 : 
     148                 :     /// Assign the fd and initialize descriptor state for the acceptor.
     149             714 :     void init_acceptor_fd(int fd) noexcept
     150                 :     {
     151             714 :         fd_            = fd;
     152             714 :         desc_state_.fd = fd;
     153                 :         {
     154             714 :             std::lock_guard lock(desc_state_.mutex);
     155             714 :             desc_state_.read_op       = nullptr;
     156             714 :             desc_state_.wait_read_op  = nullptr;
     157             714 :             desc_state_.wait_write_op = nullptr;
     158             714 :             desc_state_.wait_error_op = nullptr;
     159             714 :         }
     160             714 :     }
     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              18 :     std::error_code init_and_register(int fd) noexcept
     176                 :     {
     177              18 :         init_acceptor_fd(fd);
     178              18 :         if (auto ec = svc_.scheduler().register_descriptor(fd, &desc_state_))
     179                 :         {
     180               1 :             fd_                           = -1;
     181               1 :             desc_state_.fd                = -1;
     182               1 :             desc_state_.registered_events = 0;
     183               1 :             return ec;
     184                 :         }
     185              17 :         return {};
     186                 :     }
     187                 : 
     188                 :     /// Return a reference to the owning service.
     189            4500 :     Service& service() noexcept
     190                 :     {
     191            4500 :         return svc_;
     192                 :     }
     193                 : 
     194              19 :     void cancel() noexcept override
     195                 :     {
     196              19 :         do_cancel();
     197              19 :     }
     198                 : 
     199                 :     /// Close the acceptor (non-virtual, called by the service).
     200            2924 :     void close_socket() noexcept
     201                 :     {
     202            2924 :         do_close_socket();
     203            2924 :     }
     204                 : 
     205              44 :     std::coroutine_handle<> wait(
     206                 :         std::coroutine_handle<> h,
     207                 :         capy::executor_ref ex,
     208                 :         wait_type w,
     209                 :         std::stop_token token,
     210                 :         std::error_code* ec) override
     211                 :     {
     212              44 :         return do_wait(h, ex, w, token, ec);
     213                 :     }
     214                 : 
     215                 :     /** Wait for readiness on the listen socket.
     216                 : 
     217                 :         For `wait_type::read`, completion signals that an incoming
     218                 :         connection is pending and a subsequent accept will succeed
     219                 :         without blocking; a connection already queued when the wait
     220                 :         begins completes it immediately via an initiation probe.
     221                 : 
     222                 :         `wait_type::write` fails with `operation_not_supported` on
     223                 :         every backend: writability carries no meaning for a
     224                 :         listening socket.
     225                 :     */
     226                 :     std::coroutine_handle<> do_wait(
     227                 :         std::coroutine_handle<>,
     228                 :         capy::executor_ref,
     229                 :         wait_type,
     230                 :         std::stop_token const&,
     231                 :         std::error_code*);
     232                 : 
     233                 :     /** Cancel a single pending operation.
     234                 : 
     235                 :         Claims the operation from the read_op descriptor slot
     236                 :         under the mutex and posts it to the scheduler as cancelled.
     237                 : 
     238                 :         @param op The operation to cancel.
     239                 :     */
     240                 :     void cancel_single_op(Op& op) noexcept;
     241                 : 
     242                 :     /** Cancel the pending accept operation. */
     243                 :     void do_cancel() noexcept;
     244                 : 
     245                 :     /** Close the acceptor and cancel pending operations.
     246                 : 
     247                 :         Invoked by the derived class's close_socket(). The
     248                 :         derived class may add backend-specific cleanup after
     249                 :         calling this method.
     250                 :     */
     251                 :     void do_close_socket() noexcept;
     252                 : 
     253                 :     /** Release the acceptor without closing the fd. */
     254                 :     native_handle_type do_release_socket() noexcept;
     255                 : 
     256                 :     /** Bind the acceptor socket to an endpoint.
     257                 : 
     258                 :         Caches the resolved local endpoint (including ephemeral
     259                 :         port) after a successful bind.
     260                 : 
     261                 :         @param ep The endpoint to bind to.
     262                 :         @return The error code from bind(), or success.
     263                 :     */
     264                 :     std::error_code do_bind(Endpoint const& ep);
     265                 : 
     266                 :     /** Start listening on the acceptor socket.
     267                 : 
     268                 :         Registers the file descriptor with the reactor after
     269                 :         a successful listen() call.
     270                 : 
     271                 :         @param backlog The listen backlog.
     272                 :         @return The error code from listen() or from reactor
     273                 :         registration, or success.
     274                 :     */
     275                 :     std::error_code do_listen(int backlog);
     276                 : };
     277                 : 
     278                 : template<
     279                 :     class Derived,
     280                 :     class Service,
     281                 :     class Op,
     282                 :     class AcceptOp,
     283                 :     class WaitOp,
     284                 :     class DescState,
     285                 :     class ImplBase,
     286                 :     class Endpoint>
     287                 : void
     288             164 : reactor_acceptor<
     289                 :     Derived,
     290                 :     Service,
     291                 :     Op,
     292                 :     AcceptOp,
     293                 :     WaitOp,
     294                 :     DescState,
     295                 :     ImplBase,
     296                 :     Endpoint>::cancel_single_op(Op& op) noexcept
     297                 : {
     298             164 :     auto self = this->weak_from_this().lock();
     299             164 :     if (!self)
     300 MIS           0 :         return;
     301                 : 
     302 HIT         164 :     op.request_cancel();
     303                 : 
     304             164 :     reactor_op_base* claimed = nullptr;
     305                 :     {
     306             164 :         std::lock_guard lock(desc_state_.mutex);
     307            1476 :         auto try_claim = [&](reactor_op_base*& slot) {
     308             656 :             if (!claimed && slot == &op)
     309              87 :                 claimed = std::exchange(slot, nullptr);
     310                 :         };
     311             164 :         try_claim(desc_state_.read_op);
     312             164 :         try_claim(desc_state_.wait_read_op);
     313             164 :         try_claim(desc_state_.wait_write_op);
     314             164 :         try_claim(desc_state_.wait_error_op);
     315             164 :     }
     316             164 :     if (claimed)
     317                 :     {
     318              87 :         op.impl_ptr = self;
     319              87 :         svc_.post(&op);
     320              87 :         svc_.work_finished();
     321                 :     }
     322             164 : }
     323                 : 
     324                 : template<
     325                 :     class Derived,
     326                 :     class Service,
     327                 :     class Op,
     328                 :     class AcceptOp,
     329                 :     class WaitOp,
     330                 :     class DescState,
     331                 :     class ImplBase,
     332                 :     class Endpoint>
     333                 : void
     334              19 : reactor_acceptor<
     335                 :     Derived,
     336                 :     Service,
     337                 :     Op,
     338                 :     AcceptOp,
     339                 :     WaitOp,
     340                 :     DescState,
     341                 :     ImplBase,
     342                 :     Endpoint>::do_cancel() noexcept
     343                 : {
     344              19 :     cancel_single_op(acc_);
     345              19 :     cancel_single_op(wait_rd_);
     346              19 :     cancel_single_op(wait_wr_);
     347              19 :     cancel_single_op(wait_er_);
     348              19 : }
     349                 : 
     350                 : template<
     351                 :     class Derived,
     352                 :     class Service,
     353                 :     class Op,
     354                 :     class AcceptOp,
     355                 :     class WaitOp,
     356                 :     class DescState,
     357                 :     class ImplBase,
     358                 :     class Endpoint>
     359                 : void
     360            2924 : reactor_acceptor<
     361                 :     Derived,
     362                 :     Service,
     363                 :     Op,
     364                 :     AcceptOp,
     365                 :     WaitOp,
     366                 :     DescState,
     367                 :     ImplBase,
     368                 :     Endpoint>::do_close_socket() noexcept
     369                 : {
     370            2924 :     auto self = this->weak_from_this().lock();
     371            2924 :     if (self)
     372                 :     {
     373            2924 :         acc_.request_cancel();
     374            2924 :         wait_rd_.request_cancel();
     375            2924 :         wait_wr_.request_cancel();
     376            2924 :         wait_er_.request_cancel();
     377                 : 
     378            2924 :         reactor_op_base* claimed_acc = nullptr;
     379            2924 :         reactor_op_base* claimed_wr  = nullptr;
     380            2924 :         reactor_op_base* claimed_ww  = nullptr;
     381            2924 :         reactor_op_base* claimed_we  = nullptr;
     382                 :         {
     383            2924 :             std::lock_guard lock(desc_state_.mutex);
     384            2924 :             claimed_acc = std::exchange(desc_state_.read_op, nullptr);
     385            2924 :             claimed_wr  = std::exchange(desc_state_.wait_read_op, nullptr);
     386            2924 :             claimed_ww  = std::exchange(desc_state_.wait_write_op, nullptr);
     387            2924 :             claimed_we  = std::exchange(desc_state_.wait_error_op, nullptr);
     388            2924 :             desc_state_.read_ready  = false;
     389            2924 :             desc_state_.write_ready = false;
     390                 : 
     391            2924 :             if (desc_state_.is_enqueued_.load(std::memory_order_acquire))
     392              32 :                 desc_state_.impl_ref_ = self;
     393            2924 :         }
     394                 : 
     395           26316 :         auto repost = [&](reactor_op_base* claimed, reactor_op_base& op) {
     396           11696 :             if (claimed)
     397                 :             {
     398              19 :                 op.impl_ptr = self;
     399              19 :                 svc_.post(&op);
     400              19 :                 svc_.work_finished();
     401                 :             }
     402                 :         };
     403            2924 :         repost(claimed_acc, acc_);
     404            2924 :         repost(claimed_wr, wait_rd_);
     405            2924 :         repost(claimed_ww, wait_wr_);
     406            2924 :         repost(claimed_we, wait_er_);
     407                 :     }
     408                 : 
     409            2924 :     if (fd_ >= 0)
     410                 :     {
     411             691 :         if (desc_state_.registered_events != 0)
     412             608 :             svc_.scheduler().deregister_descriptor(fd_);
     413             691 :         ::close(fd_);
     414             691 :         fd_ = -1;
     415                 :     }
     416                 : 
     417            2924 :     desc_state_.fd                = -1;
     418            2924 :     desc_state_.registered_events = 0;
     419                 : 
     420            2924 :     local_endpoint_ = Endpoint{};
     421            2924 : }
     422                 : 
     423                 : template<
     424                 :     class Derived,
     425                 :     class Service,
     426                 :     class Op,
     427                 :     class AcceptOp,
     428                 :     class WaitOp,
     429                 :     class DescState,
     430                 :     class ImplBase,
     431                 :     class Endpoint>
     432                 : native_handle_type
     433              22 : reactor_acceptor<
     434                 :     Derived,
     435                 :     Service,
     436                 :     Op,
     437                 :     AcceptOp,
     438                 :     WaitOp,
     439                 :     DescState,
     440                 :     ImplBase,
     441                 :     Endpoint>::do_release_socket() noexcept
     442                 : {
     443              22 :     auto self = this->weak_from_this().lock();
     444              22 :     if (self)
     445                 :     {
     446              22 :         acc_.request_cancel();
     447              22 :         wait_rd_.request_cancel();
     448              22 :         wait_wr_.request_cancel();
     449              22 :         wait_er_.request_cancel();
     450                 : 
     451              22 :         reactor_op_base* claimed_acc = nullptr;
     452              22 :         reactor_op_base* claimed_wr  = nullptr;
     453              22 :         reactor_op_base* claimed_ww  = nullptr;
     454              22 :         reactor_op_base* claimed_we  = nullptr;
     455                 :         {
     456              22 :             std::lock_guard lock(desc_state_.mutex);
     457              22 :             claimed_acc = std::exchange(desc_state_.read_op, nullptr);
     458              22 :             claimed_wr  = std::exchange(desc_state_.wait_read_op, nullptr);
     459              22 :             claimed_ww  = std::exchange(desc_state_.wait_write_op, nullptr);
     460              22 :             claimed_we  = std::exchange(desc_state_.wait_error_op, nullptr);
     461              22 :             desc_state_.read_ready  = false;
     462              22 :             desc_state_.write_ready = false;
     463                 : 
     464              22 :             if (desc_state_.is_enqueued_.load(std::memory_order_acquire))
     465               1 :                 desc_state_.impl_ref_ = self;
     466              22 :         }
     467                 : 
     468             198 :         auto repost = [&](reactor_op_base* claimed, reactor_op_base& op) {
     469              88 :             if (claimed)
     470                 :             {
     471               5 :                 op.impl_ptr = self;
     472               5 :                 svc_.post(&op);
     473               5 :                 svc_.work_finished();
     474                 :             }
     475                 :         };
     476              22 :         repost(claimed_acc, acc_);
     477              22 :         repost(claimed_wr, wait_rd_);
     478              22 :         repost(claimed_ww, wait_wr_);
     479              22 :         repost(claimed_we, wait_er_);
     480                 :     }
     481                 : 
     482              22 :     native_handle_type released = fd_;
     483                 : 
     484              22 :     if (fd_ >= 0)
     485                 :     {
     486              22 :         if (desc_state_.registered_events != 0)
     487              22 :             svc_.scheduler().deregister_descriptor(fd_);
     488              22 :         fd_ = -1;
     489                 :     }
     490                 : 
     491              22 :     desc_state_.fd                = -1;
     492              22 :     desc_state_.registered_events = 0;
     493                 : 
     494              22 :     local_endpoint_ = Endpoint{};
     495                 : 
     496              44 :     return released;
     497              22 : }
     498                 : 
     499                 : template<
     500                 :     class Derived,
     501                 :     class Service,
     502                 :     class Op,
     503                 :     class AcceptOp,
     504                 :     class WaitOp,
     505                 :     class DescState,
     506                 :     class ImplBase,
     507                 :     class Endpoint>
     508                 : std::error_code
     509             672 : reactor_acceptor<
     510                 :     Derived,
     511                 :     Service,
     512                 :     Op,
     513                 :     AcceptOp,
     514                 :     WaitOp,
     515                 :     DescState,
     516                 :     ImplBase,
     517                 :     Endpoint>::do_bind(Endpoint const& ep)
     518                 : {
     519             672 :     sockaddr_storage storage{};
     520             672 :     socklen_t addrlen = to_sockaddr(ep, storage);
     521             672 :     if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) < 0)
     522              16 :         return make_err(errno);
     523                 : 
     524                 :     // Cache local endpoint (resolves ephemeral port / path)
     525             656 :     sockaddr_storage local{};
     526             656 :     socklen_t local_len = sizeof(local);
     527             656 :     if (::getsockname(fd_, reinterpret_cast<sockaddr*>(&local), &local_len) ==
     528                 :         0)
     529             656 :         set_local_endpoint(from_sockaddr_as(local, local_len, Endpoint{}));
     530                 : 
     531             656 :     return {};
     532                 : }
     533                 : 
     534                 : template<
     535                 :     class Derived,
     536                 :     class Service,
     537                 :     class Op,
     538                 :     class AcceptOp,
     539                 :     class WaitOp,
     540                 :     class DescState,
     541                 :     class ImplBase,
     542                 :     class Endpoint>
     543                 : std::error_code
     544             628 : reactor_acceptor<
     545                 :     Derived,
     546                 :     Service,
     547                 :     Op,
     548                 :     AcceptOp,
     549                 :     WaitOp,
     550                 :     DescState,
     551                 :     ImplBase,
     552                 :     Endpoint>::do_listen(int backlog)
     553                 : {
     554             628 :     if (::listen(fd_, backlog) < 0)
     555              12 :         return make_err(errno);
     556                 : 
     557                 :     // A re-listen only changes the backlog; the descriptor is already
     558                 :     // registered and re-adding it would fail on epoll.
     559             616 :     if (desc_state_.registered_events != 0)
     560               2 :         return {};
     561                 : 
     562             614 :     return svc_.scheduler().register_descriptor(fd_, &desc_state_);
     563                 : }
     564                 : 
     565                 : template<
     566                 :     class Derived,
     567                 :     class Service,
     568                 :     class Op,
     569                 :     class AcceptOp,
     570                 :     class WaitOp,
     571                 :     class DescState,
     572                 :     class ImplBase,
     573                 :     class Endpoint>
     574                 : std::coroutine_handle<>
     575              44 : reactor_acceptor<
     576                 :     Derived,
     577                 :     Service,
     578                 :     Op,
     579                 :     AcceptOp,
     580                 :     WaitOp,
     581                 :     DescState,
     582                 :     ImplBase,
     583                 :     Endpoint>::
     584                 :     do_wait(
     585                 :         std::coroutine_handle<> h,
     586                 :         capy::executor_ref ex,
     587                 :         wait_type w,
     588                 :         std::stop_token const& token,
     589                 :         std::error_code* ec)
     590                 : {
     591                 :     // Writability carries no meaning for a listening socket; some
     592                 :     // backends could only lie about it and others could never report
     593                 :     // it, so the wait fails the same way everywhere instead.
     594              44 :     if (w == wait_type::write)
     595                 :     {
     596               6 :         auto& op = wait_wr_;
     597               6 :         op.reset();
     598               6 :         op.wait_event = reactor_event_write;
     599               6 :         op.h          = h;
     600               6 :         op.ex         = ex;
     601               6 :         op.ec_out     = ec;
     602               6 :         op.fd         = this->fd_;
     603               6 :         op.start(token, static_cast<Derived*>(this));
     604               6 :         op.impl_ptr = this->shared_from_this();
     605               6 :         op.complete(ENOTSUP, 0);
     606               6 :         svc_.post(&op);
     607               6 :         return std::noop_coroutine();
     608                 :     }
     609                 : 
     610                 :     WaitOp* op_ptr;
     611                 :     reactor_op_base** desc_slot_ptr;
     612                 :     std::uint32_t event;
     613                 : 
     614              38 :     if (w == wait_type::read)
     615                 :     {
     616              32 :         op_ptr        = &wait_rd_;
     617              32 :         desc_slot_ptr = &desc_state_.wait_read_op;
     618              32 :         event         = reactor_event_read;
     619                 :     }
     620                 :     else // wait_type::error
     621                 :     {
     622               6 :         op_ptr        = &wait_er_;
     623               6 :         desc_slot_ptr = &desc_state_.wait_error_op;
     624               6 :         event         = reactor_event_error;
     625                 :     }
     626                 : 
     627              38 :     auto& op = *op_ptr;
     628              38 :     op.reset();
     629              38 :     op.wait_event = event;
     630              38 :     op.h          = h;
     631              38 :     op.ex         = ex;
     632              38 :     op.ec_out     = ec;
     633              38 :     op.fd         = this->fd_;
     634              38 :     op.start(token, static_cast<Derived*>(this));
     635              38 :     op.impl_ptr = this->shared_from_this();
     636                 : 
     637                 :     // A listener's readiness can predate the wait: an adopted or
     638                 :     // shared descriptor has history the reactor never saw, and an
     639                 :     // edge already dispatched will not be re-announced. Probe before
     640                 :     // parking.
     641              38 :     int perr = 0;
     642              38 :     if (WaitOp::probe(this->fd_, event, perr))
     643                 :     {
     644              11 :         op.complete(perr, 0);
     645              11 :         svc_.post(&op);
     646              11 :         return std::noop_coroutine();
     647                 :     }
     648                 : 
     649              27 :     svc_.work_started();
     650                 : 
     651              27 :     std::lock_guard lock(desc_state_.mutex);
     652              27 :     if (op.cancelled.load(std::memory_order_acquire))
     653                 :     {
     654               6 :         svc_.post(&op);
     655               6 :         svc_.work_finished();
     656                 :     }
     657              21 :     else if (WaitOp::probe(this->fd_, event, perr))
     658                 :     {
     659                 :         // Close the probe-to-park window: an edge that landed after
     660                 :         // the first probe was consumed, so re-check under the mutex
     661                 :         // the dispatch path holds.
     662 MIS           0 :         op.complete(perr, 0);
     663               0 :         svc_.post(&op);
     664               0 :         svc_.work_finished();
     665                 :     }
     666                 :     else
     667                 :     {
     668 HIT          21 :         *desc_slot_ptr = &op;
     669                 :     }
     670              27 :     return std::noop_coroutine();
     671              27 : }
     672                 : 
     673                 : } // namespace boost::corosio::detail
     674                 : 
     675                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
        

Generated by: LCOV version 2.3