LCOV - code coverage report
Current view: top level - corosio - io_context.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 95.5 % 66 63 3
Test Date: 2026-02-17 21:42:07 Functions: 100.0 % 22 22

           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_CONTEXT_HPP
      13                 : #define BOOST_COROSIO_IO_CONTEXT_HPP
      14                 : 
      15                 : #include <boost/corosio/detail/config.hpp>
      16                 : #include <boost/corosio/detail/platform.hpp>
      17                 : #include <boost/corosio/detail/scheduler.hpp>
      18                 : #include <boost/capy/ex/execution_context.hpp>
      19                 : 
      20                 : #include <chrono>
      21                 : #include <coroutine>
      22                 : #include <cstddef>
      23                 : #include <limits>
      24                 : #include <thread>
      25                 : 
      26                 : namespace boost::corosio {
      27                 : 
      28                 : namespace detail {
      29                 : struct timer_service_access;
      30                 : } // namespace detail
      31                 : 
      32                 : /** An I/O context for running asynchronous operations.
      33                 : 
      34                 :     The io_context provides an execution environment for async
      35                 :     operations. It maintains a queue of pending work items and
      36                 :     processes them when `run()` is called.
      37                 : 
      38                 :     The default and unsigned constructors select the platform's
      39                 :     native backend:
      40                 :     - Windows: IOCP
      41                 :     - Linux: epoll
      42                 :     - BSD/macOS: kqueue
      43                 :     - Other POSIX: select
      44                 : 
      45                 :     The template constructor accepts a backend tag value to
      46                 :     choose a specific backend at compile time:
      47                 : 
      48                 :     @par Example
      49                 :     @code
      50                 :     io_context ioc;                   // platform default
      51                 :     io_context ioc2(corosio::epoll);  // explicit backend
      52                 :     @endcode
      53                 : 
      54                 :     @par Thread Safety
      55                 :     Distinct objects: Safe.@n
      56                 :     Shared objects: Safe, if using a concurrency hint greater
      57                 :     than 1.
      58                 : 
      59                 :     @see epoll_t, select_t, kqueue_t, iocp_t
      60                 : */
      61                 : class BOOST_COROSIO_DECL io_context : public capy::execution_context
      62                 : {
      63                 :     friend struct detail::timer_service_access;
      64                 : 
      65                 : protected:
      66                 :     detail::scheduler* sched_;
      67                 : 
      68                 : public:
      69                 :     /** The executor type for this context. */
      70                 :     class executor_type;
      71                 : 
      72                 :     /** Construct with default concurrency and platform backend. */
      73                 :     io_context();
      74                 : 
      75                 :     /** Construct with a concurrency hint and platform backend.
      76                 : 
      77                 :         @param concurrency_hint Hint for the number of threads
      78                 :             that will call `run()`.
      79                 :     */
      80                 :     explicit io_context(unsigned concurrency_hint);
      81                 : 
      82                 :     /** Construct with an explicit backend tag.
      83                 : 
      84                 :         @param backend The backend tag value selecting the I/O
      85                 :             multiplexer (e.g. `corosio::epoll`).
      86                 :         @param concurrency_hint Hint for the number of threads
      87                 :             that will call `run()`.
      88                 :     */
      89                 :     template<class Backend>
      90                 :         requires requires { Backend::construct; }
      91 HIT         270 :     explicit io_context(
      92                 :         Backend backend,
      93                 :         unsigned concurrency_hint = std::thread::hardware_concurrency())
      94                 :         : capy::execution_context(this)
      95             270 :         , sched_(nullptr)
      96                 :     {
      97                 :         (void)backend;
      98             270 :         sched_ = &Backend::construct(*this, concurrency_hint);
      99             270 :     }
     100                 : 
     101                 :     ~io_context();
     102                 : 
     103                 :     io_context(io_context const&)            = delete;
     104                 :     io_context& operator=(io_context const&) = delete;
     105                 : 
     106                 :     /** Return an executor for this context.
     107                 : 
     108                 :         The returned executor can be used to dispatch coroutines
     109                 :         and post work items to this context.
     110                 : 
     111                 :         @return An executor associated with this context.
     112                 :     */
     113                 :     executor_type get_executor() const noexcept;
     114                 : 
     115                 :     /** Signal the context to stop processing.
     116                 : 
     117                 :         This causes `run()` to return as soon as possible. Any pending
     118                 :         work items remain queued.
     119                 :     */
     120               1 :     void stop()
     121                 :     {
     122               1 :         sched_->stop();
     123               1 :     }
     124                 : 
     125                 :     /** Return whether the context has been stopped.
     126                 : 
     127                 :         @return `true` if `stop()` has been called and `restart()`
     128                 :             has not been called since.
     129                 :     */
     130              21 :     bool stopped() const noexcept
     131                 :     {
     132              21 :         return sched_->stopped();
     133                 :     }
     134                 : 
     135                 :     /** Restart the context after being stopped.
     136                 : 
     137                 :         This function must be called before `run()` can be called
     138                 :         again after `stop()` has been called.
     139                 :     */
     140              89 :     void restart()
     141                 :     {
     142              89 :         sched_->restart();
     143              89 :     }
     144                 : 
     145                 :     /** Process all pending work items.
     146                 : 
     147                 :         This function blocks until all pending work items have been
     148                 :         executed or `stop()` is called. The context is stopped
     149                 :         when there is no more outstanding work.
     150                 : 
     151                 :         @note The context must be restarted with `restart()` before
     152                 :             calling this function again after it returns.
     153                 : 
     154                 :         @return The number of handlers executed.
     155                 :     */
     156             287 :     std::size_t run()
     157                 :     {
     158             287 :         return sched_->run();
     159                 :     }
     160                 : 
     161                 :     /** Process at most one pending work item.
     162                 : 
     163                 :         This function blocks until one work item has been executed
     164                 :         or `stop()` is called. The context is stopped when there
     165                 :         is no more outstanding work.
     166                 : 
     167                 :         @note The context must be restarted with `restart()` before
     168                 :             calling this function again after it returns.
     169                 : 
     170                 :         @return The number of handlers executed (0 or 1).
     171                 :     */
     172               2 :     std::size_t run_one()
     173                 :     {
     174               2 :         return sched_->run_one();
     175                 :     }
     176                 : 
     177                 :     /** Process work items for the specified duration.
     178                 : 
     179                 :         This function blocks until work items have been executed for
     180                 :         the specified duration, or `stop()` is called. The context
     181                 :         is stopped when there is no more outstanding work.
     182                 : 
     183                 :         @note The context must be restarted with `restart()` before
     184                 :             calling this function again after it returns.
     185                 : 
     186                 :         @param rel_time The duration for which to process work.
     187                 : 
     188                 :         @return The number of handlers executed.
     189                 :     */
     190                 :     template<class Rep, class Period>
     191               8 :     std::size_t run_for(std::chrono::duration<Rep, Period> const& rel_time)
     192                 :     {
     193               8 :         return run_until(std::chrono::steady_clock::now() + rel_time);
     194                 :     }
     195                 : 
     196                 :     /** Process work items until the specified time.
     197                 : 
     198                 :         This function blocks until the specified time is reached
     199                 :         or `stop()` is called. The context is stopped when there
     200                 :         is no more outstanding work.
     201                 : 
     202                 :         @note The context must be restarted with `restart()` before
     203                 :             calling this function again after it returns.
     204                 : 
     205                 :         @param abs_time The time point until which to process work.
     206                 : 
     207                 :         @return The number of handlers executed.
     208                 :     */
     209                 :     template<class Clock, class Duration>
     210                 :     std::size_t
     211               8 :     run_until(std::chrono::time_point<Clock, Duration> const& abs_time)
     212                 :     {
     213               8 :         std::size_t n = 0;
     214              57 :         while (run_one_until(abs_time))
     215              49 :             if (n != (std::numeric_limits<std::size_t>::max)())
     216              49 :                 ++n;
     217               8 :         return n;
     218                 :     }
     219                 : 
     220                 :     /** Process at most one work item for the specified duration.
     221                 : 
     222                 :         This function blocks until one work item has been executed,
     223                 :         the specified duration has elapsed, or `stop()` is called.
     224                 :         The context is stopped when there is no more outstanding work.
     225                 : 
     226                 :         @note The context must be restarted with `restart()` before
     227                 :             calling this function again after it returns.
     228                 : 
     229                 :         @param rel_time The duration for which the call may block.
     230                 : 
     231                 :         @return The number of handlers executed (0 or 1).
     232                 :     */
     233                 :     template<class Rep, class Period>
     234               2 :     std::size_t run_one_for(std::chrono::duration<Rep, Period> const& rel_time)
     235                 :     {
     236               2 :         return run_one_until(std::chrono::steady_clock::now() + rel_time);
     237                 :     }
     238                 : 
     239                 :     /** Process at most one work item until the specified time.
     240                 : 
     241                 :         This function blocks until one work item has been executed,
     242                 :         the specified time is reached, or `stop()` is called.
     243                 :         The context is stopped when there is no more outstanding work.
     244                 : 
     245                 :         @note The context must be restarted with `restart()` before
     246                 :             calling this function again after it returns.
     247                 : 
     248                 :         @param abs_time The time point until which the call may block.
     249                 : 
     250                 :         @return The number of handlers executed (0 or 1).
     251                 :     */
     252                 :     template<class Clock, class Duration>
     253                 :     std::size_t
     254              61 :     run_one_until(std::chrono::time_point<Clock, Duration> const& abs_time)
     255                 :     {
     256              61 :         typename Clock::time_point now = Clock::now();
     257              61 :         while (now < abs_time)
     258                 :         {
     259              61 :             auto rel_time = abs_time - now;
     260              61 :             if (rel_time > std::chrono::seconds(1))
     261 MIS           0 :                 rel_time = std::chrono::seconds(1);
     262                 : 
     263 HIT          61 :             std::size_t s = sched_->wait_one(
     264                 :                 static_cast<long>(
     265              61 :                     std::chrono::duration_cast<std::chrono::microseconds>(
     266                 :                         rel_time)
     267              61 :                         .count()));
     268                 : 
     269              61 :             if (s || stopped())
     270              61 :                 return s;
     271                 : 
     272 MIS           0 :             now = Clock::now();
     273                 :         }
     274               0 :         return 0;
     275                 :     }
     276                 : 
     277                 :     /** Process all ready work items without blocking.
     278                 : 
     279                 :         This function executes all work items that are ready to run
     280                 :         without blocking for more work. The context is stopped
     281                 :         when there is no more outstanding work.
     282                 : 
     283                 :         @note The context must be restarted with `restart()` before
     284                 :             calling this function again after it returns.
     285                 : 
     286                 :         @return The number of handlers executed.
     287                 :     */
     288 HIT           2 :     std::size_t poll()
     289                 :     {
     290               2 :         return sched_->poll();
     291                 :     }
     292                 : 
     293                 :     /** Process at most one ready work item without blocking.
     294                 : 
     295                 :         This function executes at most one work item that is ready
     296                 :         to run without blocking for more work. The context is
     297                 :         stopped when there is no more outstanding work.
     298                 : 
     299                 :         @note The context must be restarted with `restart()` before
     300                 :             calling this function again after it returns.
     301                 : 
     302                 :         @return The number of handlers executed (0 or 1).
     303                 :     */
     304               4 :     std::size_t poll_one()
     305                 :     {
     306               4 :         return sched_->poll_one();
     307                 :     }
     308                 : };
     309                 : 
     310                 : /** An executor for dispatching work to an I/O context.
     311                 : 
     312                 :     The executor provides the interface for posting work items and
     313                 :     dispatching coroutines to the associated context. It satisfies
     314                 :     the `capy::Executor` concept.
     315                 : 
     316                 :     Executors are lightweight handles that can be copied and compared
     317                 :     for equality. Two executors compare equal if they refer to the
     318                 :     same context.
     319                 : 
     320                 :     @par Thread Safety
     321                 :     Distinct objects: Safe.@n
     322                 :     Shared objects: Safe.
     323                 : */
     324                 : class io_context::executor_type
     325                 : {
     326                 :     io_context* ctx_ = nullptr;
     327                 : 
     328                 : public:
     329                 :     /** Default constructor.
     330                 : 
     331                 :         Constructs an executor not associated with any context.
     332                 :     */
     333                 :     executor_type() = default;
     334                 : 
     335                 :     /** Construct an executor from a context.
     336                 : 
     337                 :         @param ctx The context to associate with this executor.
     338                 :     */
     339             364 :     explicit executor_type(io_context& ctx) noexcept : ctx_(&ctx) {}
     340                 : 
     341                 :     /** Return a reference to the associated execution context.
     342                 : 
     343                 :         @return Reference to the context.
     344                 :     */
     345            1239 :     io_context& context() const noexcept
     346                 :     {
     347            1239 :         return *ctx_;
     348                 :     }
     349                 : 
     350                 :     /** Check if the current thread is running this executor's context.
     351                 : 
     352                 :         @return `true` if `run()` is being called on this thread.
     353                 :     */
     354            1259 :     bool running_in_this_thread() const noexcept
     355                 :     {
     356            1259 :         return ctx_->sched_->running_in_this_thread();
     357                 :     }
     358                 : 
     359                 :     /** Informs the executor that work is beginning.
     360                 : 
     361                 :         Must be paired with `on_work_finished()`.
     362                 :     */
     363            1264 :     void on_work_started() const noexcept
     364                 :     {
     365            1264 :         ctx_->sched_->work_started();
     366            1264 :     }
     367                 : 
     368                 :     /** Informs the executor that work has completed.
     369                 : 
     370                 :         @par Preconditions
     371                 :         A preceding call to `on_work_started()` on an equal executor.
     372                 :     */
     373            1238 :     void on_work_finished() const noexcept
     374                 :     {
     375            1238 :         ctx_->sched_->work_finished();
     376            1238 :     }
     377                 : 
     378                 :     /** Dispatch a coroutine handle.
     379                 : 
     380                 :         Returns a handle for symmetric transfer. If called from
     381                 :         within `run()`, returns `h`. Otherwise posts the coroutine
     382                 :         for later execution and returns `std::noop_coroutine()`.
     383                 : 
     384                 :         @param h The coroutine handle to dispatch.
     385                 : 
     386                 :         @return A handle for symmetric transfer or `std::noop_coroutine()`.
     387                 :     */
     388            1257 :     std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const
     389                 :     {
     390            1257 :         if (running_in_this_thread())
     391             822 :             return h;
     392             435 :         ctx_->sched_->post(h);
     393             435 :         return std::noop_coroutine();
     394                 :     }
     395                 : 
     396                 :     /** Post a coroutine for deferred execution.
     397                 : 
     398                 :         The coroutine will be resumed during a subsequent call to
     399                 :         `run()`.
     400                 : 
     401                 :         @param h The coroutine handle to post.
     402                 :     */
     403           10362 :     void post(std::coroutine_handle<> h) const
     404                 :     {
     405           10362 :         ctx_->sched_->post(h);
     406           10362 :     }
     407                 : 
     408                 :     /** Compare two executors for equality.
     409                 : 
     410                 :         @return `true` if both executors refer to the same context.
     411                 :     */
     412               1 :     bool operator==(executor_type const& other) const noexcept
     413                 :     {
     414               1 :         return ctx_ == other.ctx_;
     415                 :     }
     416                 : 
     417                 :     /** Compare two executors for inequality.
     418                 : 
     419                 :         @return `true` if the executors refer to different contexts.
     420                 :     */
     421                 :     bool operator!=(executor_type const& other) const noexcept
     422                 :     {
     423                 :         return ctx_ != other.ctx_;
     424                 :     }
     425                 : };
     426                 : 
     427                 : inline io_context::executor_type
     428             364 : io_context::get_executor() const noexcept
     429                 : {
     430             364 :     return executor_type(const_cast<io_context&>(*this));
     431                 : }
     432                 : 
     433                 : } // namespace boost::corosio
     434                 : 
     435                 : #endif // BOOST_COROSIO_IO_CONTEXT_HPP
        

Generated by: LCOV version 2.3