src/corosio/src/io_context.cpp

46.3% Lines (38/82) 53.8% List of functions (7/13)
io_context.cpp
f(x) Functions (13)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #include <boost/corosio/io_context.hpp>
12 #include <boost/corosio/backend.hpp>
13 #include <boost/corosio/detail/thread_pool.hpp>
14
15 #include <algorithm>
16 #include <stdexcept>
17 #include <thread>
18
19 #if BOOST_COROSIO_HAS_EPOLL
20 #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
21 #endif
22
23 #if BOOST_COROSIO_HAS_SELECT
24 #include <boost/corosio/native/detail/select/select_types.hpp>
25 #endif
26
27 #if BOOST_COROSIO_HAS_KQUEUE
28 #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
29 #endif
30
31 #if BOOST_COROSIO_HAS_IOCP
32 #include <boost/corosio/native/detail/iocp/win_scheduler.hpp>
33 #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
34 #include <boost/corosio/native/detail/iocp/win_udp_service.hpp>
35 #include <boost/corosio/native/detail/iocp/win_local_stream_acceptor_service.hpp>
36 #include <boost/corosio/native/detail/iocp/win_local_dgram_service.hpp>
37 #include <boost/corosio/native/detail/iocp/win_signals.hpp>
38 #include <boost/corosio/native/detail/iocp/win_file_service.hpp>
39 #include <boost/corosio/native/detail/iocp/win_random_access_file_service.hpp>
40 #endif
41
42 namespace boost::corosio {
43
44 #if BOOST_COROSIO_HAS_EPOLL
45 detail::scheduler&
46 366x epoll_t::construct(capy::execution_context& ctx, unsigned concurrency_hint)
47 {
48 732x auto& sched = ctx.make_service<detail::epoll_scheduler>(
49 366x static_cast<int>(concurrency_hint));
50
51 366x ctx.make_service<detail::epoll_tcp_service>();
52 366x ctx.make_service<detail::epoll_tcp_acceptor_service>();
53 366x ctx.make_service<detail::epoll_udp_service>();
54 366x ctx.make_service<detail::epoll_local_stream_service>();
55 366x ctx.make_service<detail::epoll_local_stream_acceptor_service>();
56 366x ctx.make_service<detail::epoll_local_datagram_service>();
57
58 366x return sched;
59 }
60 #endif
61
62 #if BOOST_COROSIO_HAS_SELECT
63 detail::scheduler&
64 239x select_t::construct(capy::execution_context& ctx, unsigned concurrency_hint)
65 {
66 478x auto& sched = ctx.make_service<detail::select_scheduler>(
67 239x static_cast<int>(concurrency_hint));
68
69 239x ctx.make_service<detail::select_tcp_service>();
70 239x ctx.make_service<detail::select_tcp_acceptor_service>();
71 239x ctx.make_service<detail::select_udp_service>();
72 239x ctx.make_service<detail::select_local_stream_service>();
73 239x ctx.make_service<detail::select_local_stream_acceptor_service>();
74 239x ctx.make_service<detail::select_local_datagram_service>();
75
76 239x return sched;
77 }
78 #endif
79
80 #if BOOST_COROSIO_HAS_KQUEUE
81 detail::scheduler&
82 kqueue_t::construct(capy::execution_context& ctx, unsigned concurrency_hint)
83 {
84 auto& sched = ctx.make_service<detail::kqueue_scheduler>(
85 static_cast<int>(concurrency_hint));
86
87 ctx.make_service<detail::kqueue_tcp_service>();
88 ctx.make_service<detail::kqueue_tcp_acceptor_service>();
89 ctx.make_service<detail::kqueue_udp_service>();
90 ctx.make_service<detail::kqueue_local_stream_service>();
91 ctx.make_service<detail::kqueue_local_stream_acceptor_service>();
92 ctx.make_service<detail::kqueue_local_datagram_service>();
93
94 return sched;
95 }
96 #endif
97
98 #if BOOST_COROSIO_HAS_IOCP
99 detail::scheduler&
100 iocp_t::construct(capy::execution_context& ctx, unsigned concurrency_hint)
101 {
102 auto& sched = ctx.make_service<detail::win_scheduler>(
103 static_cast<int>(concurrency_hint));
104
105 auto& tcp_svc = ctx.make_service<detail::win_tcp_service>();
106 ctx.make_service<detail::win_tcp_acceptor_service>(tcp_svc);
107 ctx.make_service<detail::win_udp_service>();
108 auto& local_svc =
109 ctx.make_service<detail::win_local_stream_service>(tcp_svc);
110 ctx.make_service<detail::win_local_stream_acceptor_service>(local_svc);
111 ctx.make_service<detail::win_local_dgram_service>();
112 ctx.make_service<detail::win_signals>();
113 ctx.make_service<detail::win_file_service>();
114 ctx.make_service<detail::win_random_access_file_service>();
115
116 return sched;
117 }
118 #endif
119
120 namespace {
121
122 // Pre-create services that must exist before construct() runs.
123 void
124 pre_create_services(
125 capy::execution_context& ctx,
126 io_context_options const& opts)
127 {
128 #if BOOST_COROSIO_POSIX
129 if (opts.thread_pool_size < 1)
130 throw std::invalid_argument(
131 "thread_pool_size must be at least 1");
132 // Pre-create the shared thread pool with the configured size.
133 // This must happen before construct() because the scheduler
134 // constructor creates file and resolver services that call
135 // get_or_create_pool(), which would create a 1-thread pool.
136 if (opts.thread_pool_size != 1)
137 ctx.make_service<detail::thread_pool>(opts.thread_pool_size);
138 #endif
139
140 (void)ctx;
141 (void)opts;
142 }
143
144 // Apply runtime tuning to the scheduler after construction.
145 //
146 // Concurrency-hint heuristic for budget defaults: when the io_context is
147 // constructed with concurrency_hint > 1 AND the user has not customized
148 // the budget settings (i.e. they remain at the struct defaults), we
149 // disable the inline-completion fast path. Multi-thread workloads
150 // benefit from "always-post" because cross-thread work-stealing wins
151 // over chained dispatch on the originating thread. Single-thread (or
152 // any custom budget) keeps the user/library setting unchanged.
153 void
154 apply_scheduler_options(
155 detail::scheduler& sched,
156 io_context_options const& opts,
157 unsigned concurrency_hint)
158 {
159 #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_KQUEUE || BOOST_COROSIO_HAS_SELECT
160 // Detect "user kept the defaults" by comparing all three to the
161 // io_context_options-defined struct defaults.
162 io_context_options defaults;
163 bool budget_at_defaults =
164 opts.inline_budget_initial == defaults.inline_budget_initial &&
165 opts.inline_budget_max == defaults.inline_budget_max &&
166 opts.unassisted_budget == defaults.unassisted_budget;
167
168 unsigned init = opts.inline_budget_initial;
169 unsigned max = opts.inline_budget_max;
170 unsigned ua = opts.unassisted_budget;
171
172 if (budget_at_defaults && concurrency_hint > 1)
173 {
174 // Multi-thread default: disable budget (post-everything).
175 init = 0;
176 max = 0;
177 ua = 0;
178 }
179
180 auto& reactor =
181 static_cast<detail::reactor_scheduler&>(sched);
182 reactor.configure_reactor(
183 opts.max_events_per_poll,
184 init,
185 max,
186 ua);
187 if (opts.single_threaded)
188 reactor.configure_single_threaded(true);
189 #endif
190
191 #if BOOST_COROSIO_HAS_IOCP
192 auto& iocp_sched = static_cast<detail::win_scheduler&>(sched);
193 iocp_sched.configure_iocp(opts.gqcs_timeout_ms);
194 if (opts.single_threaded)
195 iocp_sched.configure_single_threaded(true);
196 #endif
197
198 (void)sched;
199 (void)opts;
200 }
201
202 detail::scheduler&
203 127x construct_default(capy::execution_context& ctx, unsigned concurrency_hint)
204 {
205 #if BOOST_COROSIO_HAS_IOCP
206 return iocp_t::construct(ctx, concurrency_hint);
207 #elif BOOST_COROSIO_HAS_EPOLL
208 127x return epoll_t::construct(ctx, concurrency_hint);
209 #elif BOOST_COROSIO_HAS_KQUEUE
210 return kqueue_t::construct(ctx, concurrency_hint);
211 #elif BOOST_COROSIO_HAS_SELECT
212 return select_t::construct(ctx, concurrency_hint);
213 #endif
214 }
215
216 // Tie concurrency_hint == 1 to single_threaded (asio precedent).
217 io_context_options
218 normalize_options(io_context_options opts, unsigned concurrency_hint)
219 {
220 if (concurrency_hint == 1)
221 opts.single_threaded = true;
222 return opts;
223 }
224
225 } // anonymous namespace
226
227 126x io_context::io_context()
228 126x : io_context(std::max(2u, std::thread::hardware_concurrency()))
229 {
230 126x }
231
232 127x io_context::io_context(unsigned concurrency_hint)
233 : capy::execution_context(this)
234 127x , sched_(&construct_default(*this, concurrency_hint))
235 {
236 127x if (concurrency_hint == 1)
237 1x configure_single_threaded_();
238 127x }
239
240 io_context::io_context(
241 io_context_options const& opts_in,
242 unsigned concurrency_hint)
243 : capy::execution_context(this)
244 , sched_(nullptr)
245 {
246 auto opts = normalize_options(opts_in, concurrency_hint);
247 pre_create_services(*this, opts);
248 sched_ = &construct_default(*this, concurrency_hint);
249 apply_scheduler_options(*sched_, opts, concurrency_hint);
250 }
251
252 void
253 io_context::apply_options_pre_(io_context_options const& opts)
254 {
255 pre_create_services(*this, opts);
256 }
257
258 void
259 io_context::apply_options_post_(
260 io_context_options const& opts_in,
261 unsigned concurrency_hint)
262 {
263 auto opts = normalize_options(opts_in, concurrency_hint);
264 apply_scheduler_options(*sched_, opts, concurrency_hint);
265 }
266
267 void
268 1x io_context::configure_single_threaded_()
269 {
270 #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_KQUEUE || BOOST_COROSIO_HAS_SELECT
271 1x static_cast<detail::reactor_scheduler&>(*sched_)
272 1x .configure_single_threaded(true);
273 #endif
274 #if BOOST_COROSIO_HAS_IOCP
275 static_cast<detail::win_scheduler&>(*sched_)
276 .configure_single_threaded(true);
277 #endif
278 1x }
279
280 605x io_context::~io_context()
281 {
282 605x shutdown();
283 605x destroy();
284 605x }
285
286 } // namespace boost::corosio
287