LCOV - code coverage report
Current view: top level - include/boost/corosio - basic_io_context.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 95.2 % 63 60 3
Test Date: 2026-02-16 16:21:08 Functions: 100.0 % 21 21

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

Generated by: LCOV version 2.3