1 | // |
2 | // impl/io_context.hpp |
3 | // ~~~~~~~~~~~~~~~~~~~ |
4 | // |
5 | // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
6 | // |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
9 | // |
10 | |
11 | #ifndef BOOST_ASIO_IMPL_IO_CONTEXT_HPP |
12 | #define BOOST_ASIO_IMPL_IO_CONTEXT_HPP |
13 | |
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
15 | # pragma once |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
17 | |
18 | #include <boost/asio/detail/completion_handler.hpp> |
19 | #include <boost/asio/detail/executor_op.hpp> |
20 | #include <boost/asio/detail/fenced_block.hpp> |
21 | #include <boost/asio/detail/handler_type_requirements.hpp> |
22 | #include <boost/asio/detail/non_const_lvalue.hpp> |
23 | #include <boost/asio/detail/service_registry.hpp> |
24 | #include <boost/asio/detail/throw_error.hpp> |
25 | #include <boost/asio/detail/type_traits.hpp> |
26 | |
27 | #include <boost/asio/detail/push_options.hpp> |
28 | |
29 | namespace boost { |
30 | namespace asio { |
31 | |
32 | #if !defined(GENERATING_DOCUMENTATION) |
33 | |
34 | template <typename Service> |
35 | inline Service& use_service(io_context& ioc) |
36 | { |
37 | // Check that Service meets the necessary type requirements. |
38 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
39 | (void)static_cast<const execution_context::id*>(&Service::id); |
40 | |
41 | return ioc.service_registry_->template use_service<Service>(ioc); |
42 | } |
43 | |
44 | template <> |
45 | inline detail::io_context_impl& use_service<detail::io_context_impl>( |
46 | io_context& ioc) |
47 | { |
48 | return ioc.impl_; |
49 | } |
50 | |
51 | #endif // !defined(GENERATING_DOCUMENTATION) |
52 | |
53 | inline io_context::executor_type |
54 | io_context::get_executor() noexcept |
55 | { |
56 | return executor_type(*this); |
57 | } |
58 | |
59 | template <typename Rep, typename Period> |
60 | std::size_t io_context::run_for( |
61 | const chrono::duration<Rep, Period>& rel_time) |
62 | { |
63 | return this->run_until(chrono::steady_clock::now() + rel_time); |
64 | } |
65 | |
66 | template <typename Clock, typename Duration> |
67 | std::size_t io_context::run_until( |
68 | const chrono::time_point<Clock, Duration>& abs_time) |
69 | { |
70 | std::size_t n = 0; |
71 | while (this->run_one_until(abs_time)) |
72 | if (n != (std::numeric_limits<std::size_t>::max)()) |
73 | ++n; |
74 | return n; |
75 | } |
76 | |
77 | template <typename Rep, typename Period> |
78 | std::size_t io_context::run_one_for( |
79 | const chrono::duration<Rep, Period>& rel_time) |
80 | { |
81 | return this->run_one_until(chrono::steady_clock::now() + rel_time); |
82 | } |
83 | |
84 | template <typename Clock, typename Duration> |
85 | std::size_t io_context::run_one_until( |
86 | const chrono::time_point<Clock, Duration>& abs_time) |
87 | { |
88 | typename Clock::time_point now = Clock::now(); |
89 | while (now < abs_time) |
90 | { |
91 | typename Clock::duration rel_time = abs_time - now; |
92 | if (rel_time > chrono::seconds(1)) |
93 | rel_time = chrono::seconds(1); |
94 | |
95 | boost::system::error_code ec; |
96 | std::size_t s = impl_.wait_one( |
97 | usec: static_cast<long>(chrono::duration_cast< |
98 | chrono::microseconds>(rel_time).count()), ec); |
99 | boost::asio::detail::throw_error(err: ec); |
100 | |
101 | if (s || impl_.stopped()) |
102 | return s; |
103 | |
104 | now = Clock::now(); |
105 | } |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | #if !defined(BOOST_ASIO_NO_DEPRECATED) |
111 | |
112 | inline void io_context::reset() |
113 | { |
114 | restart(); |
115 | } |
116 | |
117 | struct io_context::initiate_dispatch |
118 | { |
119 | template <typename LegacyCompletionHandler> |
120 | void operator()(LegacyCompletionHandler&& handler, |
121 | io_context* self) const |
122 | { |
123 | // If you get an error on the following line it means that your handler does |
124 | // not meet the documented type requirements for a LegacyCompletionHandler. |
125 | BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK( |
126 | LegacyCompletionHandler, handler) type_check; |
127 | |
128 | detail::non_const_lvalue<LegacyCompletionHandler> handler2(handler); |
129 | if (self->impl_.can_dispatch()) |
130 | { |
131 | detail::fenced_block b(detail::fenced_block::full); |
132 | static_cast<decay_t<LegacyCompletionHandler>&&>(handler2.value)(); |
133 | } |
134 | else |
135 | { |
136 | // Allocate and construct an operation to wrap the handler. |
137 | typedef detail::completion_handler< |
138 | decay_t<LegacyCompletionHandler>, executor_type> op; |
139 | typename op::ptr p = { detail::addressof(handler2.value), |
140 | op::ptr::allocate(handler2.value), 0 }; |
141 | p.p = new (p.v) op(handler2.value, self->get_executor()); |
142 | |
143 | BOOST_ASIO_HANDLER_CREATION((*self, *p.p, |
144 | "io_context" , self, 0, "dispatch" )); |
145 | |
146 | self->impl_.do_dispatch(op: p.p); |
147 | p.v = p.p = 0; |
148 | } |
149 | } |
150 | }; |
151 | |
152 | template <typename LegacyCompletionHandler> |
153 | auto io_context::dispatch(LegacyCompletionHandler&& handler) |
154 | -> decltype( |
155 | async_initiate<LegacyCompletionHandler, void ()>( |
156 | declval<initiate_dispatch>(), handler, this)) |
157 | { |
158 | return async_initiate<LegacyCompletionHandler, void ()>( |
159 | initiate_dispatch(), handler, this); |
160 | } |
161 | |
162 | struct io_context::initiate_post |
163 | { |
164 | template <typename LegacyCompletionHandler> |
165 | void operator()(LegacyCompletionHandler&& handler, |
166 | io_context* self) const |
167 | { |
168 | // If you get an error on the following line it means that your handler does |
169 | // not meet the documented type requirements for a LegacyCompletionHandler. |
170 | BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK( |
171 | LegacyCompletionHandler, handler) type_check; |
172 | |
173 | detail::non_const_lvalue<LegacyCompletionHandler> handler2(handler); |
174 | |
175 | bool is_continuation = |
176 | boost_asio_handler_cont_helpers::is_continuation(handler2.value); |
177 | |
178 | // Allocate and construct an operation to wrap the handler. |
179 | typedef detail::completion_handler< |
180 | decay_t<LegacyCompletionHandler>, executor_type> op; |
181 | typename op::ptr p = { detail::addressof(handler2.value), |
182 | op::ptr::allocate(handler2.value), 0 }; |
183 | p.p = new (p.v) op(handler2.value, self->get_executor()); |
184 | |
185 | BOOST_ASIO_HANDLER_CREATION((*self, *p.p, |
186 | "io_context" , self, 0, "post" )); |
187 | |
188 | self->impl_.post_immediate_completion(op: p.p, is_continuation); |
189 | p.v = p.p = 0; |
190 | } |
191 | }; |
192 | |
193 | template <typename LegacyCompletionHandler> |
194 | auto io_context::post(LegacyCompletionHandler&& handler) |
195 | -> decltype( |
196 | async_initiate<LegacyCompletionHandler, void ()>( |
197 | declval<initiate_post>(), handler, this)) |
198 | { |
199 | return async_initiate<LegacyCompletionHandler, void ()>( |
200 | initiate_post(), handler, this); |
201 | } |
202 | |
203 | template <typename Handler> |
204 | #if defined(GENERATING_DOCUMENTATION) |
205 | unspecified |
206 | #else |
207 | inline detail::wrapped_handler<io_context&, Handler> |
208 | #endif |
209 | io_context::wrap(Handler handler) |
210 | { |
211 | return detail::wrapped_handler<io_context&, Handler>(*this, handler); |
212 | } |
213 | |
214 | #endif // !defined(BOOST_ASIO_NO_DEPRECATED) |
215 | |
216 | template <typename Allocator, uintptr_t Bits> |
217 | io_context::basic_executor_type<Allocator, Bits>& |
218 | io_context::basic_executor_type<Allocator, Bits>::operator=( |
219 | const basic_executor_type& other) noexcept |
220 | { |
221 | if (this != &other) |
222 | { |
223 | static_cast<Allocator&>(*this) = static_cast<const Allocator&>(other); |
224 | io_context* old_io_context = context_ptr(); |
225 | target_ = other.target_; |
226 | if (Bits & outstanding_work_tracked) |
227 | { |
228 | if (context_ptr()) |
229 | context_ptr()->impl_.work_started(); |
230 | if (old_io_context) |
231 | old_io_context->impl_.work_finished(); |
232 | } |
233 | } |
234 | return *this; |
235 | } |
236 | |
237 | template <typename Allocator, uintptr_t Bits> |
238 | io_context::basic_executor_type<Allocator, Bits>& |
239 | io_context::basic_executor_type<Allocator, Bits>::operator=( |
240 | basic_executor_type&& other) noexcept |
241 | { |
242 | if (this != &other) |
243 | { |
244 | static_cast<Allocator&>(*this) = static_cast<Allocator&&>(other); |
245 | io_context* old_io_context = context_ptr(); |
246 | target_ = other.target_; |
247 | if (Bits & outstanding_work_tracked) |
248 | { |
249 | other.target_ = 0; |
250 | if (old_io_context) |
251 | old_io_context->impl_.work_finished(); |
252 | } |
253 | } |
254 | return *this; |
255 | } |
256 | |
257 | template <typename Allocator, uintptr_t Bits> |
258 | inline bool io_context::basic_executor_type<Allocator, |
259 | Bits>::running_in_this_thread() const noexcept |
260 | { |
261 | return context_ptr()->impl_.can_dispatch(); |
262 | } |
263 | |
264 | template <typename Allocator, uintptr_t Bits> |
265 | template <typename Function> |
266 | void io_context::basic_executor_type<Allocator, Bits>::execute( |
267 | Function&& f) const |
268 | { |
269 | typedef decay_t<Function> function_type; |
270 | |
271 | // Invoke immediately if the blocking.possibly property is enabled and we are |
272 | // already inside the thread pool. |
273 | if ((bits() & blocking_never) == 0 && context_ptr()->impl_.can_dispatch()) |
274 | { |
275 | // Make a local, non-const copy of the function. |
276 | function_type tmp(static_cast<Function&&>(f)); |
277 | |
278 | #if !defined(BOOST_ASIO_NO_EXCEPTIONS) |
279 | try |
280 | { |
281 | #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) |
282 | detail::fenced_block b(detail::fenced_block::full); |
283 | static_cast<function_type&&>(tmp)(); |
284 | return; |
285 | #if !defined(BOOST_ASIO_NO_EXCEPTIONS) |
286 | } |
287 | catch (...) |
288 | { |
289 | context_ptr()->impl_.capture_current_exception(); |
290 | return; |
291 | } |
292 | #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) |
293 | } |
294 | |
295 | // Allocate and construct an operation to wrap the function. |
296 | typedef detail::executor_op<function_type, Allocator, detail::operation> op; |
297 | typename op::ptr p = { |
298 | detail::addressof(static_cast<const Allocator&>(*this)), |
299 | op::ptr::allocate(static_cast<const Allocator&>(*this)), 0 }; |
300 | p.p = new (p.v) op(static_cast<Function&&>(f), |
301 | static_cast<const Allocator&>(*this)); |
302 | |
303 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p, |
304 | "io_context" , context_ptr(), 0, "execute" )); |
305 | |
306 | context_ptr()->impl_.post_immediate_completion(p.p, |
307 | (bits() & relationship_continuation) != 0); |
308 | p.v = p.p = 0; |
309 | } |
310 | |
311 | #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
312 | template <typename Allocator, uintptr_t Bits> |
313 | inline io_context& io_context::basic_executor_type< |
314 | Allocator, Bits>::context() const noexcept |
315 | { |
316 | return *context_ptr(); |
317 | } |
318 | |
319 | template <typename Allocator, uintptr_t Bits> |
320 | inline void io_context::basic_executor_type<Allocator, |
321 | Bits>::on_work_started() const noexcept |
322 | { |
323 | context_ptr()->impl_.work_started(); |
324 | } |
325 | |
326 | template <typename Allocator, uintptr_t Bits> |
327 | inline void io_context::basic_executor_type<Allocator, |
328 | Bits>::on_work_finished() const noexcept |
329 | { |
330 | context_ptr()->impl_.work_finished(); |
331 | } |
332 | |
333 | template <typename Allocator, uintptr_t Bits> |
334 | template <typename Function, typename OtherAllocator> |
335 | void io_context::basic_executor_type<Allocator, Bits>::dispatch( |
336 | Function&& f, const OtherAllocator& a) const |
337 | { |
338 | typedef decay_t<Function> function_type; |
339 | |
340 | // Invoke immediately if we are already inside the thread pool. |
341 | if (context_ptr()->impl_.can_dispatch()) |
342 | { |
343 | // Make a local, non-const copy of the function. |
344 | function_type tmp(static_cast<Function&&>(f)); |
345 | |
346 | detail::fenced_block b(detail::fenced_block::full); |
347 | static_cast<function_type&&>(tmp)(); |
348 | return; |
349 | } |
350 | |
351 | // Allocate and construct an operation to wrap the function. |
352 | typedef detail::executor_op<function_type, |
353 | OtherAllocator, detail::operation> op; |
354 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
355 | p.p = new (p.v) op(static_cast<Function&&>(f), a); |
356 | |
357 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p, |
358 | "io_context" , context_ptr(), 0, "dispatch" )); |
359 | |
360 | context_ptr()->impl_.post_immediate_completion(p.p, false); |
361 | p.v = p.p = 0; |
362 | } |
363 | |
364 | template <typename Allocator, uintptr_t Bits> |
365 | template <typename Function, typename OtherAllocator> |
366 | void io_context::basic_executor_type<Allocator, Bits>::post( |
367 | Function&& f, const OtherAllocator& a) const |
368 | { |
369 | // Allocate and construct an operation to wrap the function. |
370 | typedef detail::executor_op<decay_t<Function>, |
371 | OtherAllocator, detail::operation> op; |
372 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
373 | p.p = new (p.v) op(static_cast<Function&&>(f), a); |
374 | |
375 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p, |
376 | "io_context" , context_ptr(), 0, "post" )); |
377 | |
378 | context_ptr()->impl_.post_immediate_completion(p.p, false); |
379 | p.v = p.p = 0; |
380 | } |
381 | |
382 | template <typename Allocator, uintptr_t Bits> |
383 | template <typename Function, typename OtherAllocator> |
384 | void io_context::basic_executor_type<Allocator, Bits>::defer( |
385 | Function&& f, const OtherAllocator& a) const |
386 | { |
387 | // Allocate and construct an operation to wrap the function. |
388 | typedef detail::executor_op<decay_t<Function>, |
389 | OtherAllocator, detail::operation> op; |
390 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
391 | p.p = new (p.v) op(static_cast<Function&&>(f), a); |
392 | |
393 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p, |
394 | "io_context" , context_ptr(), 0, "defer" )); |
395 | |
396 | context_ptr()->impl_.post_immediate_completion(p.p, true); |
397 | p.v = p.p = 0; |
398 | } |
399 | #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
400 | |
401 | #if !defined(BOOST_ASIO_NO_DEPRECATED) |
402 | inline io_context::work::work(boost::asio::io_context& io_context) |
403 | : io_context_impl_(io_context.impl_) |
404 | { |
405 | io_context_impl_.work_started(); |
406 | } |
407 | |
408 | inline io_context::work::work(const work& other) |
409 | : io_context_impl_(other.io_context_impl_) |
410 | { |
411 | io_context_impl_.work_started(); |
412 | } |
413 | |
414 | inline io_context::work::~work() |
415 | { |
416 | io_context_impl_.work_finished(); |
417 | } |
418 | |
419 | inline boost::asio::io_context& io_context::work::get_io_context() |
420 | { |
421 | return static_cast<boost::asio::io_context&>(io_context_impl_.context()); |
422 | } |
423 | #endif // !defined(BOOST_ASIO_NO_DEPRECATED) |
424 | |
425 | inline boost::asio::io_context& io_context::service::get_io_context() |
426 | { |
427 | return static_cast<boost::asio::io_context&>(context()); |
428 | } |
429 | |
430 | } // namespace asio |
431 | } // namespace boost |
432 | |
433 | #include <boost/asio/detail/pop_options.hpp> |
434 | |
435 | #endif // BOOST_ASIO_IMPL_IO_CONTEXT_HPP |
436 | |