71.43% Lines (5/7) 66.67% Functions (2/3)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Steve Gerbino 3   // Copyright (c) 2026 Steve Gerbino
4   // Copyright (c) 2026 Michael Vandeberg 4   // Copyright (c) 2026 Michael Vandeberg
5   // 5   //
6   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 7   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8   // 8   //
9   // Official repository: https://github.com/cppalliance/corosio 9   // Official repository: https://github.com/cppalliance/corosio
10   // 10   //
11   11  
12   #ifndef BOOST_COROSIO_DETAIL_SCHEDULER_HPP 12   #ifndef BOOST_COROSIO_DETAIL_SCHEDULER_HPP
13   #define BOOST_COROSIO_DETAIL_SCHEDULER_HPP 13   #define BOOST_COROSIO_DETAIL_SCHEDULER_HPP
14   14  
15   #include <boost/corosio/detail/config.hpp> 15   #include <boost/corosio/detail/config.hpp>
16   #include <boost/corosio/detail/except.hpp> 16   #include <boost/corosio/detail/except.hpp>
17   17  
18   #include <boost/capy/continuation.hpp> 18   #include <boost/capy/continuation.hpp>
19   #include <boost/capy/ex/execution_context.hpp> 19   #include <boost/capy/ex/execution_context.hpp>
20   20  
21   #include <coroutine> 21   #include <coroutine>
22   #include <cstddef> 22   #include <cstddef>
23   #include <system_error> 23   #include <system_error>
24   24  
25   namespace boost::corosio::detail { 25   namespace boost::corosio::detail {
26   26  
27   class scheduler_op; 27   class scheduler_op;
28   28  
29   /** Define the abstract interface for the event loop scheduler. 29   /** Define the abstract interface for the event loop scheduler.
30   30  
31   Concrete backends (epoll, IOCP, kqueue, select) derive from 31   Concrete backends (epoll, IOCP, kqueue, select) derive from
32   this to implement the reactor/proactor event loop. The 32   this to implement the reactor/proactor event loop. The
33   @ref io_context delegates all scheduling operations here. 33   @ref io_context delegates all scheduling operations here.
34   34  
35   The scheduler is a registry service keyed under this abstract 35   The scheduler is a registry service keyed under this abstract
36   type, so services created on first use can locate it without 36   type, so services created on first use can locate it without
37   naming a concrete backend. 37   naming a concrete backend.
38   38  
39   @see io_context 39   @see io_context
40   */ 40   */
41   struct BOOST_COROSIO_DECL scheduler 41   struct BOOST_COROSIO_DECL scheduler
42   : capy::execution_context::service 42   : capy::execution_context::service
43   { 43   {
44   using key_type = scheduler; 44   using key_type = scheduler;
45   45  
HITCBC 46   2255 ~scheduler() override = default; 46   2322 ~scheduler() override = default;
47   47  
48   /// Post a coroutine handle for deferred execution. 48   /// Post a coroutine handle for deferred execution.
49   virtual void post(std::coroutine_handle<>) const = 0; 49   virtual void post(std::coroutine_handle<>) const = 0;
50   50  
51   /// Post a scheduler operation for deferred execution. 51   /// Post a scheduler operation for deferred execution.
52   virtual void post(scheduler_op*) const = 0; 52   virtual void post(scheduler_op*) const = 0;
53   53  
54   /// Post a continuation for deferred execution (zero-allocation). 54   /// Post a continuation for deferred execution (zero-allocation).
55   virtual void post(capy::continuation&) const = 0; 55   virtual void post(capy::continuation&) const = 0;
56   56  
57   /// Increment the outstanding work count. 57   /// Increment the outstanding work count.
58   virtual void work_started() noexcept = 0; 58   virtual void work_started() noexcept = 0;
59   59  
60   /// Decrement the outstanding work count. 60   /// Decrement the outstanding work count.
61   virtual void work_finished() noexcept = 0; 61   virtual void work_finished() noexcept = 0;
62   62  
63   /// Check if the calling thread is running the event loop. 63   /// Check if the calling thread is running the event loop.
64   virtual bool running_in_this_thread() const noexcept = 0; 64   virtual bool running_in_this_thread() const noexcept = 0;
65   65  
66   /// Signal the event loop to stop. 66   /// Signal the event loop to stop.
67   virtual void stop() = 0; 67   virtual void stop() = 0;
68   68  
69   /// Check if the event loop has been stopped. 69   /// Check if the event loop has been stopped.
70   virtual bool stopped() const noexcept = 0; 70   virtual bool stopped() const noexcept = 0;
71   71  
72   /// Reset the stopped state so `run()` can be called again. 72   /// Reset the stopped state so `run()` can be called again.
73   virtual void restart() = 0; 73   virtual void restart() = 0;
74   74  
75   /// Run the event loop, blocking until all work completes. 75   /// Run the event loop, blocking until all work completes.
76   virtual std::size_t run() = 0; 76   virtual std::size_t run() = 0;
77   77  
78   /// Run one handler, blocking until one completes. 78   /// Run one handler, blocking until one completes.
79   virtual std::size_t run_one() = 0; 79   virtual std::size_t run_one() = 0;
80   80  
81   /** Run one handler, blocking up to @p usec microseconds. 81   /** Run one handler, blocking up to @p usec microseconds.
82   82  
83   @param usec Maximum wait time in microseconds. 83   @param usec Maximum wait time in microseconds.
84   84  
85   @return The number of handlers executed (0 or 1). 85   @return The number of handlers executed (0 or 1).
86   */ 86   */
87   virtual std::size_t wait_one(long usec) = 0; 87   virtual std::size_t wait_one(long usec) = 0;
88   88  
89   /// Run all ready handlers without blocking. 89   /// Run all ready handlers without blocking.
90   virtual std::size_t poll() = 0; 90   virtual std::size_t poll() = 0;
91   91  
92   /// Run at most one ready handler without blocking. 92   /// Run at most one ready handler without blocking.
93   virtual std::size_t poll_one() = 0; 93   virtual std::size_t poll_one() = 0;
94   94  
95   /** Register the read end of the POSIX signal self-pipe. 95   /** Register the read end of the POSIX signal self-pipe.
96   96  
97   Called once (by the first signal_set to register a signal) so the 97   Called once (by the first signal_set to register a signal) so the
98   backend's event loop watches @p read_fd for readability. When the 98   backend's event loop watches @p read_fd for readability. When the
99   pipe becomes readable the backend drains it and calls 99   pipe becomes readable the backend drains it and calls
100   `posix_signal_service::deliver_signal` for each pending signal, in 100   `posix_signal_service::deliver_signal` for each pending signal, in
101   normal thread context. This keeps the C signal handler 101   normal thread context. This keeps the C signal handler
102   async-signal-safe: it only writes the signal number to the pipe. 102   async-signal-safe: it only writes the signal number to the pipe.
103   103  
104   POSIX backends override this; the default is a no-op (Windows/IOCP 104   POSIX backends override this; the default is a no-op (Windows/IOCP
105   uses synchronous C-runtime signal handling instead). 105   uses synchronous C-runtime signal handling instead).
106   106  
107   @param read_fd The read end of the global signal self-pipe. 107   @param read_fd The read end of the global signal self-pipe.
108   108  
109   @return The error code, empty on success. 109   @return The error code, empty on success.
110   */ 110   */
111   [[nodiscard]] virtual std::error_code 111   [[nodiscard]] virtual std::error_code
MISUBC 112   ✗ register_signal_reader([[maybe_unused]] int read_fd) 112   ✗ register_signal_reader([[maybe_unused]] int read_fd)
113   { 113   {
114   return {}; // LCOV_EXCL_LINE POSIX overrides; the only caller never runs on IOCP 114   return {}; // LCOV_EXCL_LINE POSIX overrides; the only caller never runs on IOCP
115   } 115   }
116   116  
117   /// Decomposed threading configuration applied via @ref configure_threading. 117   /// Decomposed threading configuration applied via @ref configure_threading.
118   struct threading_config 118   struct threading_config
119   { 119   {
120   /// Scheduler mutex/condvar enabled. Off only in the `unsafe` tier. 120   /// Scheduler mutex/condvar enabled. Off only in the `unsafe` tier.
121   bool scheduler_locking = true; 121   bool scheduler_locking = true;
122   /// Per-descriptor (reactor) or ring (uring) I/O lock enabled. 122   /// Per-descriptor (reactor) or ring (uring) I/O lock enabled.
123   /// Off in the `unsafe_io` and `unsafe` tiers. 123   /// Off in the `unsafe_io` and `unsafe` tiers.
124   bool reactor_io_locking = true; 124   bool reactor_io_locking = true;
125   /// A single run thread is guaranteed (a lockless tier): elide 125   /// A single run thread is guaranteed (a lockless tier): elide
126   /// inter-run-thread wakeups. 126   /// inter-run-thread wakeups.
127   bool one_thread = false; 127   bool one_thread = false;
128   }; 128   };
129   129  
130   /// True in the fully-lockless (`unsafe`) tier. The resolver and POSIX 130   /// True in the fully-lockless (`unsafe`) tier. The resolver and POSIX
131   /// file services gate their `operation_not_supported` result on this. 131   /// file services gate their `operation_not_supported` result on this.
132   virtual bool scheduler_locking_disabled() const noexcept = 0; 132   virtual bool scheduler_locking_disabled() const noexcept = 0;
133   133  
134   /// Apply @ref threading_config. 134   /// Apply @ref threading_config.
135   virtual void configure_threading(threading_config) noexcept = 0; 135   virtual void configure_threading(threading_config) noexcept = 0;
136   }; 136   };
137   137  
138   /** Return the scheduler registered with the context. 138   /** Return the scheduler registered with the context.
139   139  
140   @throws std::logic_error If the context has no backend installed. 140   @throws std::logic_error If the context has no backend installed.
141   */ 141   */
142   inline scheduler& 142   inline scheduler&
HITCBC 143   600 get_scheduler(capy::execution_context& ctx) 143   612 get_scheduler(capy::execution_context& ctx)
144   { 144   {
HITCBC 145   600 auto* sched = ctx.find_service<scheduler>(); 145   612 auto* sched = ctx.find_service<scheduler>();
HITCBC 146   600 if (!sched) 146   612 if (!sched)
MISUBC 147   ✗ throw_logic_error("no scheduler installed"); 147   ✗ throw_logic_error("no scheduler installed");
HITCBC 148   600 return *sched; 148   612 return *sched;
149   } 149   }
150   150  
151   } // namespace boost::corosio::detail 151   } // namespace boost::corosio::detail
152   152  
153   #endif 153   #endif