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