1//
2// strand.hpp
3// ~~~~~~~~~~
4//
5// Copyright (c) 2003-2015 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_STRAND_HPP
12#define BOOST_ASIO_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#include <boost/asio/async_result.hpp>
20#include <boost/asio/detail/handler_type_requirements.hpp>
21#include <boost/asio/detail/strand_service.hpp>
22#include <boost/asio/detail/wrapped_handler.hpp>
23#include <boost/asio/io_service.hpp>
24
25#include <boost/asio/detail/push_options.hpp>
26
27namespace boost {
28namespace asio {
29
30/// Provides serialised handler execution.
31/**
32 * The io_service::strand class provides the ability to post and dispatch
33 * handlers with the guarantee that none of those handlers will execute
34 * concurrently.
35 *
36 * @par Order of handler invocation
37 * Given:
38 *
39 * @li a strand object @c s
40 *
41 * @li an object @c a meeting completion handler requirements
42 *
43 * @li an object @c a1 which is an arbitrary copy of @c a made by the
44 * implementation
45 *
46 * @li an object @c b meeting completion handler requirements
47 *
48 * @li an object @c b1 which is an arbitrary copy of @c b made by the
49 * implementation
50 *
51 * if any of the following conditions are true:
52 *
53 * @li @c s.post(a) happens-before @c s.post(b)
54 *
55 * @li @c s.post(a) happens-before @c s.dispatch(b), where the latter is
56 * performed outside the strand
57 *
58 * @li @c s.dispatch(a) happens-before @c s.post(b), where the former is
59 * performed outside the strand
60 *
61 * @li @c s.dispatch(a) happens-before @c s.dispatch(b), where both are
62 * performed outside the strand
63 *
64 * then @c asio_handler_invoke(a1, &a1) happens-before
65 * @c asio_handler_invoke(b1, &b1).
66 *
67 * Note that in the following case:
68 * @code async_op_1(..., s.wrap(a));
69 * async_op_2(..., s.wrap(b)); @endcode
70 * the completion of the first async operation will perform @c s.dispatch(a),
71 * and the second will perform @c s.dispatch(b), but the order in which those
72 * are performed is unspecified. That is, you cannot state whether one
73 * happens-before the other. Therefore none of the above conditions are met and
74 * no ordering guarantee is made.
75 *
76 * @note The implementation makes no guarantee that handlers posted or
77 * dispatched through different @c strand objects will be invoked concurrently.
78 *
79 * @par Thread Safety
80 * @e Distinct @e objects: Safe.@n
81 * @e Shared @e objects: Safe.
82 *
83 * @par Concepts:
84 * Dispatcher.
85 */
86class io_service::strand
87{
88public:
89 /// Constructor.
90 /**
91 * Constructs the strand.
92 *
93 * @param io_service The io_service object that the strand will use to
94 * dispatch handlers that are ready to be run.
95 */
96 explicit strand(boost::asio::io_service& io_service)
97 : service_(boost::asio::use_service<
98 boost::asio::detail::strand_service>(io_service))
99 {
100 service_.construct(impl_);
101 }
102
103 /// Destructor.
104 /**
105 * Destroys a strand.
106 *
107 * Handlers posted through the strand that have not yet been invoked will
108 * still be dispatched in a way that meets the guarantee of non-concurrency.
109 */
110 ~strand()
111 {
112 }
113
114 /// Get the io_service associated with the strand.
115 /**
116 * This function may be used to obtain the io_service object that the strand
117 * uses to dispatch handlers for asynchronous operations.
118 *
119 * @return A reference to the io_service object that the strand will use to
120 * dispatch handlers. Ownership is not transferred to the caller.
121 */
122 boost::asio::io_service& get_io_service()
123 {
124 return service_.get_io_service();
125 }
126
127 /// Request the strand to invoke the given handler.
128 /**
129 * This function is used to ask the strand to execute the given handler.
130 *
131 * The strand object guarantees that handlers posted or dispatched through
132 * the strand will not be executed concurrently. The handler may be executed
133 * inside this function if the guarantee can be met. If this function is
134 * called from within a handler that was posted or dispatched through the same
135 * strand, then the new handler will be executed immediately.
136 *
137 * The strand's guarantee is in addition to the guarantee provided by the
138 * underlying io_service. The io_service guarantees that the handler will only
139 * be called in a thread in which the io_service's run member function is
140 * currently being invoked.
141 *
142 * @param handler The handler to be called. The strand will make a copy of the
143 * handler object as required. The function signature of the handler must be:
144 * @code void handler(); @endcode
145 */
146 template <typename CompletionHandler>
147 BOOST_ASIO_INITFN_RESULT_TYPE(CompletionHandler, void ())
148 dispatch(BOOST_ASIO_MOVE_ARG(CompletionHandler) handler)
149 {
150 // If you get an error on the following line it means that your handler does
151 // not meet the documented type requirements for a CompletionHandler.
152 BOOST_ASIO_COMPLETION_HANDLER_CHECK(CompletionHandler, handler) type_check;
153
154 detail::async_result_init<
155 CompletionHandler, void ()> init(
156 BOOST_ASIO_MOVE_CAST(CompletionHandler)(handler));
157
158 service_.dispatch(impl_, init.handler);
159
160 return init.result.get();
161 }
162
163 /// Request the strand to invoke the given handler and return
164 /// immediately.
165 /**
166 * This function is used to ask the strand to execute the given handler, but
167 * without allowing the strand to call the handler from inside this function.
168 *
169 * The strand object guarantees that handlers posted or dispatched through
170 * the strand will not be executed concurrently. The strand's guarantee is in
171 * addition to the guarantee provided by the underlying io_service. The
172 * io_service guarantees that the handler will only be called in a thread in
173 * which the io_service's run member function is currently being invoked.
174 *
175 * @param handler The handler to be called. The strand will make a copy of the
176 * handler object as required. The function signature of the handler must be:
177 * @code void handler(); @endcode
178 */
179 template <typename CompletionHandler>
180 BOOST_ASIO_INITFN_RESULT_TYPE(CompletionHandler, void ())
181 post(BOOST_ASIO_MOVE_ARG(CompletionHandler) handler)
182 {
183 // If you get an error on the following line it means that your handler does
184 // not meet the documented type requirements for a CompletionHandler.
185 BOOST_ASIO_COMPLETION_HANDLER_CHECK(CompletionHandler, handler) type_check;
186
187 detail::async_result_init<
188 CompletionHandler, void ()> init(
189 BOOST_ASIO_MOVE_CAST(CompletionHandler)(handler));
190
191 service_.post(impl_, init.handler);
192
193 return init.result.get();
194 }
195
196 /// Create a new handler that automatically dispatches the wrapped handler
197 /// on the strand.
198 /**
199 * This function is used to create a new handler function object that, when
200 * invoked, will automatically pass the wrapped handler to the strand's
201 * dispatch function.
202 *
203 * @param handler The handler to be wrapped. The strand will make a copy of
204 * the handler object as required. The function signature of the handler must
205 * be: @code void handler(A1 a1, ... An an); @endcode
206 *
207 * @return A function object that, when invoked, passes the wrapped handler to
208 * the strand's dispatch function. Given a function object with the signature:
209 * @code R f(A1 a1, ... An an); @endcode
210 * If this function object is passed to the wrap function like so:
211 * @code strand.wrap(f); @endcode
212 * then the return value is a function object with the signature
213 * @code void g(A1 a1, ... An an); @endcode
214 * that, when invoked, executes code equivalent to:
215 * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode
216 */
217 template <typename Handler>
218#if defined(GENERATING_DOCUMENTATION)
219 unspecified
220#else
221 detail::wrapped_handler<strand, Handler, detail::is_continuation_if_running>
222#endif
223 wrap(Handler handler)
224 {
225 return detail::wrapped_handler<io_service::strand, Handler,
226 detail::is_continuation_if_running>(*this, handler);
227 }
228
229 /// Determine whether the strand is running in the current thread.
230 /**
231 * @return @c true if the current thread is executing a handler that was
232 * submitted to the strand using post(), dispatch() or wrap(). Otherwise
233 * returns @c false.
234 */
235 bool running_in_this_thread() const
236 {
237 return service_.running_in_this_thread(impl_);
238 }
239
240private:
241 boost::asio::detail::strand_service& service_;
242 boost::asio::detail::strand_service::implementation_type impl_;
243};
244
245/// (Deprecated: Use boost::asio::io_service::strand.) Typedef for backwards
246/// compatibility.
247typedef boost::asio::io_service::strand strand;
248
249} // namespace asio
250} // namespace boost
251
252#include <boost/asio/detail/pop_options.hpp>
253
254#endif // BOOST_ASIO_STRAND_HPP
255