1//
2// io_context_strand.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_IO_CONTEXT_STRAND_HPP
12#define BOOST_ASIO_IO_CONTEXT_STRAND_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_EXTENSIONS) \
21 && !defined(BOOST_ASIO_NO_TS_EXECUTORS)
22
23#include <boost/asio/async_result.hpp>
24#include <boost/asio/detail/handler_type_requirements.hpp>
25#include <boost/asio/detail/strand_service.hpp>
26#include <boost/asio/detail/wrapped_handler.hpp>
27#include <boost/asio/io_context.hpp>
28
29#include <boost/asio/detail/push_options.hpp>
30
31namespace boost {
32namespace asio {
33
34/// Provides serialised handler execution.
35/**
36 * The io_context::strand class provides the ability to post and dispatch
37 * handlers with the guarantee that none of those handlers will execute
38 * concurrently.
39 *
40 * @par Order of handler invocation
41 * Given:
42 *
43 * @li a strand object @c s
44 *
45 * @li an object @c a meeting completion handler requirements
46 *
47 * @li an object @c a1 which is an arbitrary copy of @c a made by the
48 * implementation
49 *
50 * @li an object @c b meeting completion handler requirements
51 *
52 * @li an object @c b1 which is an arbitrary copy of @c b made by the
53 * implementation
54 *
55 * if any of the following conditions are true:
56 *
57 * @li @c s.post(a) happens-before @c s.post(b)
58 *
59 * @li @c s.post(a) happens-before @c s.dispatch(b), where the latter is
60 * performed outside the strand
61 *
62 * @li @c s.dispatch(a) happens-before @c s.post(b), where the former is
63 * performed outside the strand
64 *
65 * @li @c s.dispatch(a) happens-before @c s.dispatch(b), where both are
66 * performed outside the strand
67 *
68 * then @c a() happens-before @c b()
69 *
70 * Note that in the following case:
71 * @code async_op_1(..., s.wrap(a));
72 * async_op_2(..., s.wrap(b)); @endcode
73 * the completion of the first async operation will perform @c s.dispatch(a),
74 * and the second will perform @c s.dispatch(b), but the order in which those
75 * are performed is unspecified. That is, you cannot state whether one
76 * happens-before the other. Therefore none of the above conditions are met and
77 * no ordering guarantee is made.
78 *
79 * @note The implementation makes no guarantee that handlers posted or
80 * dispatched through different @c strand objects will be invoked concurrently.
81 *
82 * @par Thread Safety
83 * @e Distinct @e objects: Safe.@n
84 * @e Shared @e objects: Safe.
85 *
86 * @par Concepts:
87 * Dispatcher.
88 */
89class io_context::strand
90{
91private:
92#if !defined(BOOST_ASIO_NO_DEPRECATED)
93 struct initiate_dispatch;
94 struct initiate_post;
95#endif // !defined(BOOST_ASIO_NO_DEPRECATED)
96
97public:
98 /// Constructor.
99 /**
100 * Constructs the strand.
101 *
102 * @param io_context The io_context object that the strand will use to
103 * dispatch handlers that are ready to be run.
104 */
105 explicit strand(boost::asio::io_context& io_context)
106 : service_(boost::asio::use_service<
107 boost::asio::detail::strand_service>(ioc&: io_context))
108 {
109 service_.construct(impl&: impl_);
110 }
111
112 /// Copy constructor.
113 /**
114 * Creates a copy such that both strand objects share the same underlying
115 * state.
116 */
117 strand(const strand& other) noexcept
118 : service_(other.service_),
119 impl_(other.impl_)
120 {
121 }
122
123 /// Destructor.
124 /**
125 * Destroys a strand.
126 *
127 * Handlers posted through the strand that have not yet been invoked will
128 * still be dispatched in a way that meets the guarantee of non-concurrency.
129 */
130 ~strand()
131 {
132 }
133
134 /// Obtain the underlying execution context.
135 boost::asio::io_context& context() const noexcept
136 {
137 return service_.get_io_context();
138 }
139
140 /// Inform the strand that it has some outstanding work to do.
141 /**
142 * The strand delegates this call to its underlying io_context.
143 */
144 void on_work_started() const noexcept
145 {
146 context().get_executor().on_work_started();
147 }
148
149 /// Inform the strand that some work is no longer outstanding.
150 /**
151 * The strand delegates this call to its underlying io_context.
152 */
153 void on_work_finished() const noexcept
154 {
155 context().get_executor().on_work_finished();
156 }
157
158 /// Request the strand to invoke the given function object.
159 /**
160 * This function is used to ask the strand to execute the given function
161 * object on its underlying io_context. The function object will be executed
162 * inside this function if the strand is not otherwise busy and if the
163 * underlying io_context's executor's @c dispatch() function is also able to
164 * execute the function before returning.
165 *
166 * @param f The function object to be called. The executor will make
167 * a copy of the handler object as required. The function signature of the
168 * function object must be: @code void function(); @endcode
169 *
170 * @param a An allocator that may be used by the executor to allocate the
171 * internal storage needed for function invocation.
172 */
173 template <typename Function, typename Allocator>
174 void dispatch(Function&& f, const Allocator& a) const
175 {
176 decay_t<Function> tmp(static_cast<Function&&>(f));
177 service_.dispatch(impl_, tmp);
178 (void)a;
179 }
180
181#if !defined(BOOST_ASIO_NO_DEPRECATED)
182 /// (Deprecated: Use boost::asio::dispatch().) Request the strand to invoke
183 /// the given handler.
184 /**
185 * This function is used to ask the strand to execute the given handler.
186 *
187 * The strand object guarantees that handlers posted or dispatched through
188 * the strand will not be executed concurrently. The handler may be executed
189 * inside this function if the guarantee can be met. If this function is
190 * called from within a handler that was posted or dispatched through the same
191 * strand, then the new handler will be executed immediately.
192 *
193 * The strand's guarantee is in addition to the guarantee provided by the
194 * underlying io_context. The io_context guarantees that the handler will only
195 * be called in a thread in which the io_context's run member function is
196 * currently being invoked.
197 *
198 * @param handler The handler to be called. The strand will make a copy of the
199 * handler object as required. The function signature of the handler must be:
200 * @code void handler(); @endcode
201 */
202 template <typename LegacyCompletionHandler>
203 auto dispatch(LegacyCompletionHandler&& handler)
204 -> decltype(
205 async_initiate<LegacyCompletionHandler, void ()>(
206 declval<initiate_dispatch>(), handler, this))
207 {
208 return async_initiate<LegacyCompletionHandler, void ()>(
209 initiate_dispatch(), handler, this);
210 }
211#endif // !defined(BOOST_ASIO_NO_DEPRECATED)
212
213 /// Request the strand to invoke the given function object.
214 /**
215 * This function is used to ask the executor to execute the given function
216 * object. The function object will never be executed inside this function.
217 * Instead, it will be scheduled to run by the underlying io_context.
218 *
219 * @param f The function object to be called. The executor will make
220 * a copy of the handler object as required. The function signature of the
221 * function object must be: @code void function(); @endcode
222 *
223 * @param a An allocator that may be used by the executor to allocate the
224 * internal storage needed for function invocation.
225 */
226 template <typename Function, typename Allocator>
227 void post(Function&& f, const Allocator& a) const
228 {
229 decay_t<Function> tmp(static_cast<Function&&>(f));
230 service_.post(impl_, tmp);
231 (void)a;
232 }
233
234#if !defined(BOOST_ASIO_NO_DEPRECATED)
235 /// (Deprecated: Use boost::asio::post().) Request the strand to invoke the
236 /// given handler and return immediately.
237 /**
238 * This function is used to ask the strand to execute the given handler, but
239 * without allowing the strand to call the handler from inside this function.
240 *
241 * The strand object guarantees that handlers posted or dispatched through
242 * the strand will not be executed concurrently. The strand's guarantee is in
243 * addition to the guarantee provided by the underlying io_context. The
244 * io_context guarantees that the handler will only be called in a thread in
245 * which the io_context's run member function is currently being invoked.
246 *
247 * @param handler The handler to be called. The strand will make a copy of the
248 * handler object as required. The function signature of the handler must be:
249 * @code void handler(); @endcode
250 */
251 template <typename LegacyCompletionHandler>
252 auto post(LegacyCompletionHandler&& handler)
253 -> decltype(
254 async_initiate<LegacyCompletionHandler, void ()>(
255 declval<initiate_post>(), handler, this))
256 {
257 return async_initiate<LegacyCompletionHandler, void ()>(
258 initiate_post(), handler, this);
259 }
260#endif // !defined(BOOST_ASIO_NO_DEPRECATED)
261
262 /// Request the strand to invoke the given function object.
263 /**
264 * This function is used to ask the executor to execute the given function
265 * object. The function object will never be executed inside this function.
266 * Instead, it will be scheduled to run by the underlying io_context.
267 *
268 * @param f The function object to be called. The executor will make
269 * a copy of the handler object as required. The function signature of the
270 * function object must be: @code void function(); @endcode
271 *
272 * @param a An allocator that may be used by the executor to allocate the
273 * internal storage needed for function invocation.
274 */
275 template <typename Function, typename Allocator>
276 void defer(Function&& f, const Allocator& a) const
277 {
278 decay_t<Function> tmp(static_cast<Function&&>(f));
279 service_.post(impl_, tmp);
280 (void)a;
281 }
282
283#if !defined(BOOST_ASIO_NO_DEPRECATED)
284 /// (Deprecated: Use boost::asio::bind_executor().) Create a new handler that
285 /// automatically dispatches the wrapped handler on the strand.
286 /**
287 * This function is used to create a new handler function object that, when
288 * invoked, will automatically pass the wrapped handler to the strand's
289 * dispatch function.
290 *
291 * @param handler The handler to be wrapped. The strand will make a copy of
292 * the handler object as required. The function signature of the handler must
293 * be: @code void handler(A1 a1, ... An an); @endcode
294 *
295 * @return A function object that, when invoked, passes the wrapped handler to
296 * the strand's dispatch function. Given a function object with the signature:
297 * @code R f(A1 a1, ... An an); @endcode
298 * If this function object is passed to the wrap function like so:
299 * @code strand.wrap(f); @endcode
300 * then the return value is a function object with the signature
301 * @code void g(A1 a1, ... An an); @endcode
302 * that, when invoked, executes code equivalent to:
303 * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode
304 */
305 template <typename Handler>
306#if defined(GENERATING_DOCUMENTATION)
307 unspecified
308#else
309 detail::wrapped_handler<strand, Handler, detail::is_continuation_if_running>
310#endif
311 wrap(Handler handler)
312 {
313 return detail::wrapped_handler<io_context::strand, Handler,
314 detail::is_continuation_if_running>(*this, handler);
315 }
316#endif // !defined(BOOST_ASIO_NO_DEPRECATED)
317
318 /// Determine whether the strand is running in the current thread.
319 /**
320 * @return @c true if the current thread is executing a handler that was
321 * submitted to the strand using post(), dispatch() or wrap(). Otherwise
322 * returns @c false.
323 */
324 bool running_in_this_thread() const noexcept
325 {
326 return service_.running_in_this_thread(impl: impl_);
327 }
328
329 /// Compare two strands for equality.
330 /**
331 * Two strands are equal if they refer to the same ordered, non-concurrent
332 * state.
333 */
334 friend bool operator==(const strand& a, const strand& b) noexcept
335 {
336 return a.impl_ == b.impl_;
337 }
338
339 /// Compare two strands for inequality.
340 /**
341 * Two strands are equal if they refer to the same ordered, non-concurrent
342 * state.
343 */
344 friend bool operator!=(const strand& a, const strand& b) noexcept
345 {
346 return a.impl_ != b.impl_;
347 }
348
349private:
350#if !defined(BOOST_ASIO_NO_DEPRECATED)
351 struct initiate_dispatch
352 {
353 template <typename LegacyCompletionHandler>
354 void operator()(LegacyCompletionHandler&& handler,
355 strand* self) const
356 {
357 // If you get an error on the following line it means that your
358 // handler does not meet the documented type requirements for a
359 // LegacyCompletionHandler.
360 BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK(
361 LegacyCompletionHandler, handler) type_check;
362
363 detail::non_const_lvalue<LegacyCompletionHandler> handler2(handler);
364 self->service_.dispatch(self->impl_, handler2.value);
365 }
366 };
367
368 struct initiate_post
369 {
370 template <typename LegacyCompletionHandler>
371 void operator()(LegacyCompletionHandler&& handler,
372 strand* self) const
373 {
374 // If you get an error on the following line it means that your
375 // handler does not meet the documented type requirements for a
376 // LegacyCompletionHandler.
377 BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK(
378 LegacyCompletionHandler, handler) type_check;
379
380 detail::non_const_lvalue<LegacyCompletionHandler> handler2(handler);
381 self->service_.post(self->impl_, handler2.value);
382 }
383 };
384#endif // !defined(BOOST_ASIO_NO_DEPRECATED)
385
386 boost::asio::detail::strand_service& service_;
387 mutable boost::asio::detail::strand_service::implementation_type impl_;
388};
389
390} // namespace asio
391} // namespace boost
392
393#include <boost/asio/detail/pop_options.hpp>
394
395#endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
396 // && !defined(BOOST_ASIO_NO_TS_EXECUTORS)
397
398#endif // BOOST_ASIO_IO_CONTEXT_STRAND_HPP
399

source code of boost/libs/asio/include/boost/asio/io_context_strand.hpp