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