1 | // |
2 | // executor_work_guard.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_EXECUTOR_WORK_GUARD_HPP |
12 | #define BOOST_ASIO_EXECUTOR_WORK_GUARD_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/config.hpp> |
19 | |
20 | #include <boost/asio/associated_executor.hpp> |
21 | #include <boost/asio/detail/type_traits.hpp> |
22 | #include <boost/asio/execution.hpp> |
23 | #include <boost/asio/is_executor.hpp> |
24 | |
25 | #include <boost/asio/detail/push_options.hpp> |
26 | |
27 | namespace boost { |
28 | namespace asio { |
29 | |
30 | #if !defined(BOOST_ASIO_EXECUTOR_WORK_GUARD_DECL) |
31 | #define BOOST_ASIO_EXECUTOR_WORK_GUARD_DECL |
32 | |
33 | template <typename Executor, typename = void, typename = void> |
34 | class executor_work_guard; |
35 | |
36 | #endif // !defined(BOOST_ASIO_EXECUTOR_WORK_GUARD_DECL) |
37 | |
38 | #if defined(GENERATING_DOCUMENTATION) |
39 | |
40 | /// An object of type @c executor_work_guard controls ownership of outstanding |
41 | /// executor work within a scope. |
42 | template <typename Executor> |
43 | class executor_work_guard |
44 | { |
45 | public: |
46 | /// The underlying executor type. |
47 | typedef Executor executor_type; |
48 | |
49 | /// Constructs a @c executor_work_guard object for the specified executor. |
50 | /** |
51 | * Stores a copy of @c e and calls <tt>on_work_started()</tt> on it. |
52 | */ |
53 | explicit executor_work_guard(const executor_type& e) noexcept; |
54 | |
55 | /// Copy constructor. |
56 | executor_work_guard(const executor_work_guard& other) noexcept; |
57 | |
58 | /// Move constructor. |
59 | executor_work_guard(executor_work_guard&& other) noexcept; |
60 | |
61 | /// Destructor. |
62 | /** |
63 | * Unless the object has already been reset, or is in a moved-from state, |
64 | * calls <tt>on_work_finished()</tt> on the stored executor. |
65 | */ |
66 | ~executor_work_guard(); |
67 | |
68 | /// Obtain the associated executor. |
69 | executor_type get_executor() const noexcept; |
70 | |
71 | /// Whether the executor_work_guard object owns some outstanding work. |
72 | bool owns_work() const noexcept; |
73 | |
74 | /// Indicate that the work is no longer outstanding. |
75 | /** |
76 | * Unless the object has already been reset, or is in a moved-from state, |
77 | * calls <tt>on_work_finished()</tt> on the stored executor. |
78 | */ |
79 | void reset() noexcept; |
80 | }; |
81 | |
82 | #endif // defined(GENERATING_DOCUMENTATION) |
83 | |
84 | #if !defined(GENERATING_DOCUMENTATION) |
85 | |
86 | #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
87 | |
88 | template <typename Executor> |
89 | class executor_work_guard<Executor, |
90 | enable_if_t< |
91 | is_executor<Executor>::value |
92 | >> |
93 | { |
94 | public: |
95 | typedef Executor executor_type; |
96 | |
97 | explicit executor_work_guard(const executor_type& e) noexcept |
98 | : executor_(e), |
99 | owns_(true) |
100 | { |
101 | executor_.on_work_started(); |
102 | } |
103 | |
104 | executor_work_guard(const executor_work_guard& other) noexcept |
105 | : executor_(other.executor_), |
106 | owns_(other.owns_) |
107 | { |
108 | if (owns_) |
109 | executor_.on_work_started(); |
110 | } |
111 | |
112 | executor_work_guard(executor_work_guard&& other) noexcept |
113 | : executor_(static_cast<Executor&&>(other.executor_)), |
114 | owns_(other.owns_) |
115 | { |
116 | other.owns_ = false; |
117 | } |
118 | |
119 | ~executor_work_guard() |
120 | { |
121 | if (owns_) |
122 | executor_.on_work_finished(); |
123 | } |
124 | |
125 | executor_type get_executor() const noexcept |
126 | { |
127 | return executor_; |
128 | } |
129 | |
130 | bool owns_work() const noexcept |
131 | { |
132 | return owns_; |
133 | } |
134 | |
135 | void reset() noexcept |
136 | { |
137 | if (owns_) |
138 | { |
139 | executor_.on_work_finished(); |
140 | owns_ = false; |
141 | } |
142 | } |
143 | |
144 | private: |
145 | // Disallow assignment. |
146 | executor_work_guard& operator=(const executor_work_guard&); |
147 | |
148 | executor_type executor_; |
149 | bool owns_; |
150 | }; |
151 | |
152 | #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
153 | |
154 | template <typename Executor> |
155 | class executor_work_guard<Executor, |
156 | enable_if_t< |
157 | !is_executor<Executor>::value |
158 | >, |
159 | enable_if_t< |
160 | execution::is_executor<Executor>::value |
161 | >> |
162 | { |
163 | public: |
164 | typedef Executor executor_type; |
165 | |
166 | explicit executor_work_guard(const executor_type& e) noexcept |
167 | : executor_(e), |
168 | owns_(true) |
169 | { |
170 | new (&work_) work_type(boost::asio::prefer(executor_, |
171 | execution::outstanding_work.tracked)); |
172 | } |
173 | |
174 | executor_work_guard(const executor_work_guard& other) noexcept |
175 | : executor_(other.executor_), |
176 | owns_(other.owns_) |
177 | { |
178 | if (owns_) |
179 | { |
180 | new (&work_) work_type(boost::asio::prefer(executor_, |
181 | execution::outstanding_work.tracked)); |
182 | } |
183 | } |
184 | |
185 | executor_work_guard(executor_work_guard&& other) noexcept |
186 | : executor_(static_cast<Executor&&>(other.executor_)), |
187 | owns_(other.owns_) |
188 | { |
189 | if (owns_) |
190 | { |
191 | new (&work_) work_type( |
192 | static_cast<work_type&&>( |
193 | *static_cast<work_type*>( |
194 | static_cast<void*>(&other.work_)))); |
195 | other.owns_ = false; |
196 | } |
197 | } |
198 | |
199 | ~executor_work_guard() |
200 | { |
201 | if (owns_) |
202 | static_cast<work_type*>(static_cast<void*>(&work_))->~work_type(); |
203 | } |
204 | |
205 | executor_type get_executor() const noexcept |
206 | { |
207 | return executor_; |
208 | } |
209 | |
210 | bool owns_work() const noexcept |
211 | { |
212 | return owns_; |
213 | } |
214 | |
215 | void reset() noexcept |
216 | { |
217 | if (owns_) |
218 | { |
219 | static_cast<work_type*>(static_cast<void*>(&work_))->~work_type(); |
220 | owns_ = false; |
221 | } |
222 | } |
223 | |
224 | private: |
225 | // Disallow assignment. |
226 | executor_work_guard& operator=(const executor_work_guard&); |
227 | |
228 | typedef decay_t< |
229 | prefer_result_t< |
230 | const executor_type&, |
231 | execution::outstanding_work_t::tracked_t |
232 | > |
233 | > work_type; |
234 | |
235 | executor_type executor_; |
236 | aligned_storage_t<sizeof(work_type), alignment_of<work_type>::value> work_; |
237 | bool owns_; |
238 | }; |
239 | |
240 | #endif // !defined(GENERATING_DOCUMENTATION) |
241 | |
242 | /// Create an @ref executor_work_guard object. |
243 | /** |
244 | * @param ex An executor. |
245 | * |
246 | * @returns A work guard constructed with the specified executor. |
247 | */ |
248 | template <typename Executor> |
249 | BOOST_ASIO_NODISCARD inline executor_work_guard<Executor> |
250 | make_work_guard(const Executor& ex, |
251 | constraint_t< |
252 | is_executor<Executor>::value || execution::is_executor<Executor>::value |
253 | > = 0) |
254 | { |
255 | return executor_work_guard<Executor>(ex); |
256 | } |
257 | |
258 | /// Create an @ref executor_work_guard object. |
259 | /** |
260 | * @param ctx An execution context, from which an executor will be obtained. |
261 | * |
262 | * @returns A work guard constructed with the execution context's executor, |
263 | * obtained by performing <tt>ctx.get_executor()</tt>. |
264 | */ |
265 | template <typename ExecutionContext> |
266 | BOOST_ASIO_NODISCARD inline |
267 | executor_work_guard<typename ExecutionContext::executor_type> |
268 | make_work_guard(ExecutionContext& ctx, |
269 | constraint_t< |
270 | is_convertible<ExecutionContext&, execution_context&>::value |
271 | > = 0) |
272 | { |
273 | return executor_work_guard<typename ExecutionContext::executor_type>( |
274 | ctx.get_executor()); |
275 | } |
276 | |
277 | /// Create an @ref executor_work_guard object. |
278 | /** |
279 | * @param t An arbitrary object, such as a completion handler, for which the |
280 | * associated executor will be obtained. |
281 | * |
282 | * @returns A work guard constructed with the associated executor of the object |
283 | * @c t, which is obtained as if by calling <tt>get_associated_executor(t)</tt>. |
284 | */ |
285 | template <typename T> |
286 | BOOST_ASIO_NODISCARD inline |
287 | executor_work_guard< |
288 | typename constraint_t< |
289 | !is_executor<T>::value |
290 | && !execution::is_executor<T>::value |
291 | && !is_convertible<T&, execution_context&>::value, |
292 | associated_executor<T> |
293 | >::type> |
294 | make_work_guard(const T& t) |
295 | { |
296 | return executor_work_guard<associated_executor_t<T>>( |
297 | associated_executor<T>::get(t)); |
298 | } |
299 | |
300 | /// Create an @ref executor_work_guard object. |
301 | /** |
302 | * @param t An arbitrary object, such as a completion handler, for which the |
303 | * associated executor will be obtained. |
304 | * |
305 | * @param ex An executor to be used as the candidate object when determining the |
306 | * associated executor. |
307 | * |
308 | * @returns A work guard constructed with the associated executor of the object |
309 | * @c t, which is obtained as if by calling <tt>get_associated_executor(t, |
310 | * ex)</tt>. |
311 | */ |
312 | template <typename T, typename Executor> |
313 | BOOST_ASIO_NODISCARD inline |
314 | executor_work_guard<associated_executor_t<T, Executor>> |
315 | make_work_guard(const T& t, const Executor& ex, |
316 | constraint_t< |
317 | is_executor<Executor>::value || execution::is_executor<Executor>::value |
318 | > = 0) |
319 | { |
320 | return executor_work_guard<associated_executor_t<T, Executor>>( |
321 | associated_executor<T, Executor>::get(t, ex)); |
322 | } |
323 | |
324 | /// Create an @ref executor_work_guard object. |
325 | /** |
326 | * @param t An arbitrary object, such as a completion handler, for which the |
327 | * associated executor will be obtained. |
328 | * |
329 | * @param ctx An execution context, from which an executor is obtained to use as |
330 | * the candidate object for determining the associated executor. |
331 | * |
332 | * @returns A work guard constructed with the associated executor of the object |
333 | * @c t, which is obtained as if by calling <tt>get_associated_executor(t, |
334 | * ctx.get_executor())</tt>. |
335 | */ |
336 | template <typename T, typename ExecutionContext> |
337 | BOOST_ASIO_NODISCARD inline executor_work_guard< |
338 | associated_executor_t<T, typename ExecutionContext::executor_type>> |
339 | make_work_guard(const T& t, ExecutionContext& ctx, |
340 | constraint_t< |
341 | !is_executor<T>::value |
342 | > = 0, |
343 | constraint_t< |
344 | !execution::is_executor<T>::value |
345 | > = 0, |
346 | constraint_t< |
347 | !is_convertible<T&, execution_context&>::value |
348 | > = 0, |
349 | constraint_t< |
350 | is_convertible<ExecutionContext&, execution_context&>::value |
351 | > = 0) |
352 | { |
353 | return executor_work_guard< |
354 | associated_executor_t<T, typename ExecutionContext::executor_type>>( |
355 | associated_executor<T, typename ExecutionContext::executor_type>::get( |
356 | t, ctx.get_executor())); |
357 | } |
358 | |
359 | } // namespace asio |
360 | } // namespace boost |
361 | |
362 | #include <boost/asio/detail/pop_options.hpp> |
363 | |
364 | #endif // BOOST_ASIO_EXECUTOR_WORK_GUARD_HPP |
365 | |