]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
1 | // |
2 | // detail/impl/strand_executor_service.hpp | |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
4 | // | |
1e59de90 | 5 | // Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
b32b8144 FG |
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_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP | |
12 | #define BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP | |
13 | ||
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
15 | # pragma once | |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
17 | ||
b32b8144 FG |
18 | #include <boost/asio/detail/fenced_block.hpp> |
19 | #include <boost/asio/detail/handler_invoke_helpers.hpp> | |
20 | #include <boost/asio/detail/recycling_allocator.hpp> | |
21 | #include <boost/asio/executor_work_guard.hpp> | |
20effc67 TL |
22 | #include <boost/asio/defer.hpp> |
23 | #include <boost/asio/dispatch.hpp> | |
24 | #include <boost/asio/post.hpp> | |
b32b8144 FG |
25 | |
26 | #include <boost/asio/detail/push_options.hpp> | |
27 | ||
28 | namespace boost { | |
29 | namespace asio { | |
30 | namespace detail { | |
31 | ||
20effc67 TL |
32 | template <typename F, typename Allocator> |
33 | class strand_executor_service::allocator_binder | |
34 | { | |
35 | public: | |
36 | typedef Allocator allocator_type; | |
37 | ||
38 | allocator_binder(BOOST_ASIO_MOVE_ARG(F) f, const Allocator& a) | |
39 | : f_(BOOST_ASIO_MOVE_CAST(F)(f)), | |
40 | allocator_(a) | |
41 | { | |
42 | } | |
43 | ||
44 | allocator_binder(const allocator_binder& other) | |
45 | : f_(other.f_), | |
46 | allocator_(other.allocator_) | |
47 | { | |
48 | } | |
49 | ||
50 | #if defined(BOOST_ASIO_HAS_MOVE) | |
51 | allocator_binder(allocator_binder&& other) | |
52 | : f_(BOOST_ASIO_MOVE_CAST(F)(other.f_)), | |
53 | allocator_(BOOST_ASIO_MOVE_CAST(allocator_type)(other.allocator_)) | |
54 | { | |
55 | } | |
56 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
57 | ||
58 | allocator_type get_allocator() const BOOST_ASIO_NOEXCEPT | |
59 | { | |
60 | return allocator_; | |
61 | } | |
62 | ||
63 | void operator()() | |
64 | { | |
65 | f_(); | |
66 | } | |
67 | ||
68 | private: | |
69 | F f_; | |
70 | allocator_type allocator_; | |
71 | }; | |
72 | ||
73 | template <typename Executor> | |
74 | class strand_executor_service::invoker<Executor, | |
75 | typename enable_if< | |
76 | execution::is_executor<Executor>::value | |
77 | >::type> | |
78 | { | |
79 | public: | |
80 | invoker(const implementation_type& impl, Executor& ex) | |
81 | : impl_(impl), | |
82 | executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) | |
83 | { | |
84 | } | |
85 | ||
86 | invoker(const invoker& other) | |
87 | : impl_(other.impl_), | |
88 | executor_(other.executor_) | |
89 | { | |
90 | } | |
91 | ||
92 | #if defined(BOOST_ASIO_HAS_MOVE) | |
93 | invoker(invoker&& other) | |
94 | : impl_(BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_)), | |
95 | executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_)) | |
96 | { | |
97 | } | |
98 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
99 | ||
100 | struct on_invoker_exit | |
101 | { | |
102 | invoker* this_; | |
103 | ||
104 | ~on_invoker_exit() | |
105 | { | |
1e59de90 | 106 | if (push_waiting_to_ready(this_->impl_)) |
20effc67 TL |
107 | { |
108 | recycling_allocator<void> allocator; | |
1e59de90 | 109 | executor_type ex = this_->executor_; |
20effc67 TL |
110 | execution::execute( |
111 | boost::asio::prefer( | |
1e59de90 TL |
112 | boost::asio::require( |
113 | BOOST_ASIO_MOVE_CAST(executor_type)(ex), | |
20effc67 TL |
114 | execution::blocking.never), |
115 | execution::allocator(allocator)), | |
116 | BOOST_ASIO_MOVE_CAST(invoker)(*this_)); | |
117 | } | |
118 | } | |
119 | }; | |
120 | ||
121 | void operator()() | |
122 | { | |
20effc67 TL |
123 | // Ensure the next handler, if any, is scheduled on block exit. |
124 | on_invoker_exit on_exit = { this }; | |
125 | (void)on_exit; | |
126 | ||
1e59de90 | 127 | run_ready_handlers(impl_); |
20effc67 TL |
128 | } |
129 | ||
130 | private: | |
131 | typedef typename decay< | |
132 | typename prefer_result< | |
133 | Executor, | |
134 | execution::outstanding_work_t::tracked_t | |
135 | >::type | |
136 | >::type executor_type; | |
137 | ||
138 | implementation_type impl_; | |
139 | executor_type executor_; | |
140 | }; | |
141 | ||
142 | #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) | |
143 | ||
b32b8144 | 144 | template <typename Executor> |
20effc67 TL |
145 | class strand_executor_service::invoker<Executor, |
146 | typename enable_if< | |
147 | !execution::is_executor<Executor>::value | |
148 | >::type> | |
b32b8144 FG |
149 | { |
150 | public: | |
151 | invoker(const implementation_type& impl, Executor& ex) | |
152 | : impl_(impl), | |
153 | work_(ex) | |
154 | { | |
155 | } | |
156 | ||
157 | invoker(const invoker& other) | |
158 | : impl_(other.impl_), | |
159 | work_(other.work_) | |
160 | { | |
161 | } | |
162 | ||
163 | #if defined(BOOST_ASIO_HAS_MOVE) | |
164 | invoker(invoker&& other) | |
165 | : impl_(BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_)), | |
166 | work_(BOOST_ASIO_MOVE_CAST(executor_work_guard<Executor>)(other.work_)) | |
167 | { | |
168 | } | |
169 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
170 | ||
171 | struct on_invoker_exit | |
172 | { | |
173 | invoker* this_; | |
174 | ||
175 | ~on_invoker_exit() | |
176 | { | |
1e59de90 | 177 | if (push_waiting_to_ready(this_->impl_)) |
b32b8144 FG |
178 | { |
179 | Executor ex(this_->work_.get_executor()); | |
180 | recycling_allocator<void> allocator; | |
181 | ex.post(BOOST_ASIO_MOVE_CAST(invoker)(*this_), allocator); | |
182 | } | |
183 | } | |
184 | }; | |
185 | ||
186 | void operator()() | |
187 | { | |
b32b8144 FG |
188 | // Ensure the next handler, if any, is scheduled on block exit. |
189 | on_invoker_exit on_exit = { this }; | |
190 | (void)on_exit; | |
191 | ||
1e59de90 | 192 | run_ready_handlers(impl_); |
b32b8144 FG |
193 | } |
194 | ||
195 | private: | |
196 | implementation_type impl_; | |
197 | executor_work_guard<Executor> work_; | |
198 | }; | |
199 | ||
20effc67 TL |
200 | #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
201 | ||
202 | template <typename Executor, typename Function> | |
203 | inline void strand_executor_service::execute(const implementation_type& impl, | |
204 | Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, | |
205 | typename enable_if< | |
206 | can_query<Executor, execution::allocator_t<void> >::value | |
207 | >::type*) | |
208 | { | |
209 | return strand_executor_service::do_execute(impl, ex, | |
210 | BOOST_ASIO_MOVE_CAST(Function)(function), | |
211 | boost::asio::query(ex, execution::allocator)); | |
212 | } | |
213 | ||
214 | template <typename Executor, typename Function> | |
215 | inline void strand_executor_service::execute(const implementation_type& impl, | |
216 | Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, | |
217 | typename enable_if< | |
218 | !can_query<Executor, execution::allocator_t<void> >::value | |
219 | >::type*) | |
220 | { | |
221 | return strand_executor_service::do_execute(impl, ex, | |
222 | BOOST_ASIO_MOVE_CAST(Function)(function), | |
223 | std::allocator<void>()); | |
224 | } | |
225 | ||
226 | template <typename Executor, typename Function, typename Allocator> | |
227 | void strand_executor_service::do_execute(const implementation_type& impl, | |
228 | Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a) | |
229 | { | |
230 | typedef typename decay<Function>::type function_type; | |
231 | ||
232 | // If the executor is not never-blocking, and we are already in the strand, | |
233 | // then the function can run immediately. | |
234 | if (boost::asio::query(ex, execution::blocking) != execution::blocking.never | |
1e59de90 | 235 | && running_in_this_thread(impl)) |
20effc67 TL |
236 | { |
237 | // Make a local, non-const copy of the function. | |
238 | function_type tmp(BOOST_ASIO_MOVE_CAST(Function)(function)); | |
239 | ||
240 | fenced_block b(fenced_block::full); | |
241 | boost_asio_handler_invoke_helpers::invoke(tmp, tmp); | |
242 | return; | |
243 | } | |
244 | ||
245 | // Allocate and construct an operation to wrap the function. | |
246 | typedef executor_op<function_type, Allocator> op; | |
247 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; | |
248 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a); | |
249 | ||
250 | BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, | |
251 | "strand_executor", impl.get(), 0, "execute")); | |
252 | ||
253 | // Add the function to the strand and schedule the strand if required. | |
254 | bool first = enqueue(impl, p.p); | |
255 | p.v = p.p = 0; | |
256 | if (first) | |
257 | { | |
258 | execution::execute(ex, invoker<Executor>(impl, ex)); | |
259 | } | |
260 | } | |
261 | ||
b32b8144 FG |
262 | template <typename Executor, typename Function, typename Allocator> |
263 | void strand_executor_service::dispatch(const implementation_type& impl, | |
264 | Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a) | |
265 | { | |
266 | typedef typename decay<Function>::type function_type; | |
267 | ||
268 | // If we are already in the strand then the function can run immediately. | |
1e59de90 | 269 | if (running_in_this_thread(impl)) |
b32b8144 FG |
270 | { |
271 | // Make a local, non-const copy of the function. | |
272 | function_type tmp(BOOST_ASIO_MOVE_CAST(Function)(function)); | |
273 | ||
274 | fenced_block b(fenced_block::full); | |
275 | boost_asio_handler_invoke_helpers::invoke(tmp, tmp); | |
276 | return; | |
277 | } | |
278 | ||
279 | // Allocate and construct an operation to wrap the function. | |
280 | typedef executor_op<function_type, Allocator> op; | |
281 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; | |
282 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a); | |
283 | ||
284 | BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, | |
285 | "strand_executor", impl.get(), 0, "dispatch")); | |
286 | ||
287 | // Add the function to the strand and schedule the strand if required. | |
288 | bool first = enqueue(impl, p.p); | |
289 | p.v = p.p = 0; | |
290 | if (first) | |
20effc67 TL |
291 | { |
292 | boost::asio::dispatch(ex, | |
293 | allocator_binder<invoker<Executor>, Allocator>( | |
294 | invoker<Executor>(impl, ex), a)); | |
295 | } | |
b32b8144 FG |
296 | } |
297 | ||
298 | // Request invocation of the given function and return immediately. | |
299 | template <typename Executor, typename Function, typename Allocator> | |
300 | void strand_executor_service::post(const implementation_type& impl, | |
301 | Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a) | |
302 | { | |
303 | typedef typename decay<Function>::type function_type; | |
304 | ||
305 | // Allocate and construct an operation to wrap the function. | |
306 | typedef executor_op<function_type, Allocator> op; | |
307 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; | |
308 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a); | |
309 | ||
310 | BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, | |
311 | "strand_executor", impl.get(), 0, "post")); | |
312 | ||
313 | // Add the function to the strand and schedule the strand if required. | |
314 | bool first = enqueue(impl, p.p); | |
315 | p.v = p.p = 0; | |
316 | if (first) | |
20effc67 TL |
317 | { |
318 | boost::asio::post(ex, | |
319 | allocator_binder<invoker<Executor>, Allocator>( | |
320 | invoker<Executor>(impl, ex), a)); | |
321 | } | |
b32b8144 FG |
322 | } |
323 | ||
324 | // Request invocation of the given function and return immediately. | |
325 | template <typename Executor, typename Function, typename Allocator> | |
326 | void strand_executor_service::defer(const implementation_type& impl, | |
327 | Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a) | |
328 | { | |
329 | typedef typename decay<Function>::type function_type; | |
330 | ||
331 | // Allocate and construct an operation to wrap the function. | |
332 | typedef executor_op<function_type, Allocator> op; | |
333 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; | |
334 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a); | |
335 | ||
336 | BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, | |
337 | "strand_executor", impl.get(), 0, "defer")); | |
338 | ||
339 | // Add the function to the strand and schedule the strand if required. | |
340 | bool first = enqueue(impl, p.p); | |
341 | p.v = p.p = 0; | |
342 | if (first) | |
20effc67 TL |
343 | { |
344 | boost::asio::defer(ex, | |
345 | allocator_binder<invoker<Executor>, Allocator>( | |
346 | invoker<Executor>(impl, ex), a)); | |
347 | } | |
b32b8144 FG |
348 | } |
349 | ||
350 | } // namespace detail | |
351 | } // namespace asio | |
352 | } // namespace boost | |
353 | ||
354 | #include <boost/asio/detail/pop_options.hpp> | |
355 | ||
356 | #endif // BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP |