1 //
2 // executor_work_guard.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
21
22 #include <boost/asio/associated_executor.hpp>
23 #include <boost/asio/detail/type_traits.hpp>
24 #include <boost/asio/execution.hpp>
25 #include <boost/asio/is_executor.hpp>
26
27 #include <boost/asio/detail/push_options.hpp>
28
29 namespace boost {
30 namespace asio {
31
32 #if !defined(BOOST_ASIO_EXECUTOR_WORK_GUARD_DECL)
33 #define BOOST_ASIO_EXECUTOR_WORK_GUARD_DECL
34
35 template <typename Executor, typename = void, typename = void>
36 class executor_work_guard;
37
38 #endif // !defined(BOOST_ASIO_EXECUTOR_WORK_GUARD_DECL)
39
40 /// An object of type @c executor_work_guard controls ownership of executor work
41 /// within a scope.
42 #if defined(GENERATING_DOCUMENTATION)
43 template <typename Executor>
44 #else // defined(GENERATING_DOCUMENTATION)
45 template <typename Executor, typename, typename>
46 #endif // defined(GENERATING_DOCUMENTATION)
47 class executor_work_guard
48 {
49 public:
50 /// The underlying executor type.
51 typedef Executor executor_type;
52
53 /// Constructs a @c executor_work_guard object for the specified executor.
54 /**
55 * Stores a copy of @c e and calls <tt>on_work_started()</tt> on it.
56 */
executor_work_guard(const executor_type & e)57 explicit executor_work_guard(const executor_type& e) BOOST_ASIO_NOEXCEPT
58 : executor_(e),
59 owns_(true)
60 {
61 executor_.on_work_started();
62 }
63
64 /// Copy constructor.
executor_work_guard(const executor_work_guard & other)65 executor_work_guard(const executor_work_guard& other) BOOST_ASIO_NOEXCEPT
66 : executor_(other.executor_),
67 owns_(other.owns_)
68 {
69 if (owns_)
70 executor_.on_work_started();
71 }
72
73 #if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
74 /// Move constructor.
executor_work_guard(executor_work_guard && other)75 executor_work_guard(executor_work_guard&& other) BOOST_ASIO_NOEXCEPT
76 : executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)),
77 owns_(other.owns_)
78 {
79 other.owns_ = false;
80 }
81 #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
82
83 /// Destructor.
84 /**
85 * Unless the object has already been reset, or is in a moved-from state,
86 * calls <tt>on_work_finished()</tt> on the stored executor.
87 */
~executor_work_guard()88 ~executor_work_guard()
89 {
90 if (owns_)
91 executor_.on_work_finished();
92 }
93
94 /// Obtain the associated executor.
get_executor() const95 executor_type get_executor() const BOOST_ASIO_NOEXCEPT
96 {
97 return executor_;
98 }
99
100 /// Whether the executor_work_guard object owns some outstanding work.
owns_work() const101 bool owns_work() const BOOST_ASIO_NOEXCEPT
102 {
103 return owns_;
104 }
105
106 /// Indicate that the work is no longer outstanding.
107 /**
108 * Unless the object has already been reset, or is in a moved-from state,
109 * calls <tt>on_work_finished()</tt> on the stored executor.
110 */
reset()111 void reset() BOOST_ASIO_NOEXCEPT
112 {
113 if (owns_)
114 {
115 executor_.on_work_finished();
116 owns_ = false;
117 }
118 }
119
120 private:
121 // Disallow assignment.
122 executor_work_guard& operator=(const executor_work_guard&);
123
124 executor_type executor_;
125 bool owns_;
126 };
127
128 #if !defined(GENERATING_DOCUMENTATION)
129
130 template <typename Executor>
131 class executor_work_guard<Executor,
132 typename enable_if<
133 !is_executor<Executor>::value
134 >::type,
135 typename enable_if<
136 execution::is_executor<Executor>::value
137 >::type>
138 {
139 public:
140 typedef Executor executor_type;
141
executor_work_guard(const executor_type & e)142 explicit executor_work_guard(const executor_type& e) BOOST_ASIO_NOEXCEPT
143 : executor_(e),
144 owns_(true)
145 {
146 new (&work_) work_type(boost::asio::prefer(executor_,
147 execution::outstanding_work.tracked));
148 }
149
executor_work_guard(const executor_work_guard & other)150 executor_work_guard(const executor_work_guard& other) BOOST_ASIO_NOEXCEPT
151 : executor_(other.executor_),
152 owns_(other.owns_)
153 {
154 if (owns_)
155 {
156 new (&work_) work_type(boost::asio::prefer(executor_,
157 execution::outstanding_work.tracked));
158 }
159 }
160
161 #if defined(BOOST_ASIO_HAS_MOVE)
executor_work_guard(executor_work_guard && other)162 executor_work_guard(executor_work_guard&& other) BOOST_ASIO_NOEXCEPT
163 : executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)),
164 owns_(other.owns_)
165 {
166 if (owns_)
167 {
168 new (&work_) work_type(
169 BOOST_ASIO_MOVE_CAST(work_type)(
170 *static_cast<work_type*>(
171 static_cast<void*>(&other.work_))));
172 other.owns_ = false;
173 }
174 }
175 #endif // defined(BOOST_ASIO_HAS_MOVE)
176
~executor_work_guard()177 ~executor_work_guard()
178 {
179 if (owns_)
180 static_cast<work_type*>(static_cast<void*>(&work_))->~work_type();
181 }
182
get_executor() const183 executor_type get_executor() const BOOST_ASIO_NOEXCEPT
184 {
185 return executor_;
186 }
187
owns_work() const188 bool owns_work() const BOOST_ASIO_NOEXCEPT
189 {
190 return owns_;
191 }
192
reset()193 void reset() BOOST_ASIO_NOEXCEPT
194 {
195 if (owns_)
196 {
197 static_cast<work_type*>(static_cast<void*>(&work_))->~work_type();
198 owns_ = false;
199 }
200 }
201
202 private:
203 // Disallow assignment.
204 executor_work_guard& operator=(const executor_work_guard&);
205
206 typedef typename decay<
207 typename prefer_result<
208 const executor_type&,
209 execution::outstanding_work_t::tracked_t
210 >::type
211 >::type work_type;
212
213 executor_type executor_;
214 typename aligned_storage<sizeof(work_type),
215 alignment_of<work_type>::value>::type work_;
216 bool owns_;
217 };
218
219 #endif // !defined(GENERATING_DOCUMENTATION)
220
221 /// Create an @ref executor_work_guard object.
222 template <typename Executor>
make_work_guard(const Executor & ex,typename constraint<is_executor<Executor>::value||execution::is_executor<Executor>::value>::type=0)223 inline executor_work_guard<Executor> make_work_guard(const Executor& ex,
224 typename constraint<
225 is_executor<Executor>::value || execution::is_executor<Executor>::value
226 >::type = 0)
227 {
228 return executor_work_guard<Executor>(ex);
229 }
230
231 /// Create an @ref executor_work_guard object.
232 template <typename ExecutionContext>
233 inline executor_work_guard<typename ExecutionContext::executor_type>
make_work_guard(ExecutionContext & ctx,typename constraint<is_convertible<ExecutionContext &,execution_context &>::value>::type=0)234 make_work_guard(ExecutionContext& ctx,
235 typename constraint<
236 is_convertible<ExecutionContext&, execution_context&>::value
237 >::type = 0)
238 {
239 return executor_work_guard<typename ExecutionContext::executor_type>(
240 ctx.get_executor());
241 }
242
243 /// Create an @ref executor_work_guard object.
244 template <typename T>
245 inline executor_work_guard<typename associated_executor<T>::type>
make_work_guard(const T & t,typename constraint<!is_executor<T>::value>::type=0,typename constraint<!execution::is_executor<T>::value>::type=0,typename constraint<!is_convertible<T &,execution_context &>::value>::type=0)246 make_work_guard(const T& t,
247 typename constraint<
248 !is_executor<T>::value
249 >::type = 0,
250 typename constraint<
251 !execution::is_executor<T>::value
252 >::type = 0,
253 typename constraint<
254 !is_convertible<T&, execution_context&>::value
255 >::type = 0)
256 {
257 return executor_work_guard<typename associated_executor<T>::type>(
258 associated_executor<T>::get(t));
259 }
260
261 /// Create an @ref executor_work_guard object.
262 template <typename T, typename Executor>
263 inline executor_work_guard<typename associated_executor<T, Executor>::type>
make_work_guard(const T & t,const Executor & ex,typename constraint<is_executor<Executor>::value||execution::is_executor<Executor>::value>::type=0)264 make_work_guard(const T& t, const Executor& ex,
265 typename constraint<
266 is_executor<Executor>::value || execution::is_executor<Executor>::value
267 >::type = 0)
268 {
269 return executor_work_guard<typename associated_executor<T, Executor>::type>(
270 associated_executor<T, Executor>::get(t, ex));
271 }
272
273 /// Create an @ref executor_work_guard object.
274 template <typename T, typename ExecutionContext>
275 inline executor_work_guard<typename associated_executor<T,
276 typename ExecutionContext::executor_type>::type>
make_work_guard(const T & t,ExecutionContext & ctx,typename constraint<!is_executor<T>::value>::type=0,typename constraint<!execution::is_executor<T>::value>::type=0,typename constraint<!is_convertible<T &,execution_context &>::value>::type=0,typename constraint<is_convertible<ExecutionContext &,execution_context &>::value>::type=0)277 make_work_guard(const T& t, ExecutionContext& ctx,
278 typename constraint<
279 !is_executor<T>::value
280 >::type = 0,
281 typename constraint<
282 !execution::is_executor<T>::value
283 >::type = 0,
284 typename constraint<
285 !is_convertible<T&, execution_context&>::value
286 >::type = 0,
287 typename constraint<
288 is_convertible<ExecutionContext&, execution_context&>::value
289 >::type = 0)
290 {
291 return executor_work_guard<typename associated_executor<T,
292 typename ExecutionContext::executor_type>::type>(
293 associated_executor<T, typename ExecutionContext::executor_type>::get(
294 t, ctx.get_executor()));
295 }
296
297 } // namespace asio
298 } // namespace boost
299
300 #include <boost/asio/detail/pop_options.hpp>
301
302 #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
303
304 #endif // BOOST_ASIO_EXECUTOR_WORK_GUARD_HPP
305