1 // 2 // detail/handler_work.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_DETAIL_HANDLER_WORK_HPP 12 #define BOOST_ASIO_DETAIL_HANDLER_WORK_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/associated_executor.hpp> 20 #include <boost/asio/detail/handler_invoke_helpers.hpp> 21 #include <boost/asio/detail/type_traits.hpp> 22 #include <boost/asio/execution/allocator.hpp> 23 #include <boost/asio/execution/blocking.hpp> 24 #include <boost/asio/execution/execute.hpp> 25 #include <boost/asio/execution/executor.hpp> 26 #include <boost/asio/execution/outstanding_work.hpp> 27 #include <boost/asio/executor_work_guard.hpp> 28 #include <boost/asio/prefer.hpp> 29 30 #include <boost/asio/detail/push_options.hpp> 31 32 namespace boost { 33 namespace asio { 34 35 class executor; 36 class io_context; 37 38 #if !defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) 39 40 class any_io_executor; 41 42 #endif // !defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) 43 44 namespace execution { 45 46 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 47 48 template <typename...> class any_executor; 49 50 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 51 52 template <typename, typename, typename, typename, typename, 53 typename, typename, typename, typename> class any_executor; 54 55 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 56 57 } // namespace execution 58 namespace detail { 59 60 template <typename Executor, typename CandidateExecutor = void, 61 typename IoContext = io_context, 62 typename PolymorphicExecutor = executor, typename = void> 63 class handler_work_base 64 { 65 public: handler_work_base(int,int,const Executor & ex)66 explicit handler_work_base(int, int, const Executor& ex) BOOST_ASIO_NOEXCEPT 67 : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) 68 { 69 } 70 71 template <typename OtherExecutor> handler_work_base(const Executor & ex,const OtherExecutor &)72 handler_work_base(const Executor& ex, 73 const OtherExecutor&) BOOST_ASIO_NOEXCEPT 74 : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) 75 { 76 } 77 handler_work_base(const handler_work_base & other)78 handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT 79 : executor_(other.executor_) 80 { 81 } 82 83 #if defined(BOOST_ASIO_HAS_MOVE) handler_work_base(handler_work_base && other)84 handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT 85 : executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_)) 86 { 87 } 88 #endif // defined(BOOST_ASIO_HAS_MOVE) 89 owns_work() const90 bool owns_work() const BOOST_ASIO_NOEXCEPT 91 { 92 return true; 93 } 94 95 template <typename Function, typename Handler> dispatch(Function & function,Handler & handler)96 void dispatch(Function& function, Handler& handler) 97 { 98 execution::execute( 99 boost::asio::prefer(executor_, 100 execution::blocking.possibly, 101 execution::allocator((get_associated_allocator)(handler))), 102 BOOST_ASIO_MOVE_CAST(Function)(function)); 103 } 104 105 private: 106 typedef typename decay< 107 typename prefer_result<Executor, 108 execution::outstanding_work_t::tracked_t 109 >::type 110 >::type executor_type; 111 112 executor_type executor_; 113 }; 114 115 template <typename Executor, typename CandidateExecutor, 116 typename IoContext, typename PolymorphicExecutor> 117 class handler_work_base<Executor, CandidateExecutor, 118 IoContext, PolymorphicExecutor, 119 typename enable_if< 120 !execution::is_executor<Executor>::value 121 && (!is_same<Executor, PolymorphicExecutor>::value 122 || !is_same<CandidateExecutor, void>::value) 123 >::type> 124 { 125 public: handler_work_base(int,int,const Executor & ex)126 explicit handler_work_base(int, int, const Executor& ex) BOOST_ASIO_NOEXCEPT 127 : executor_(ex), 128 owns_work_(true) 129 { 130 executor_.on_work_started(); 131 } 132 handler_work_base(const Executor & ex,const Executor & candidate)133 handler_work_base(const Executor& ex, 134 const Executor& candidate) BOOST_ASIO_NOEXCEPT 135 : executor_(ex), 136 owns_work_(ex != candidate) 137 { 138 if (owns_work_) 139 executor_.on_work_started(); 140 } 141 142 template <typename OtherExecutor> handler_work_base(const Executor & ex,const OtherExecutor &)143 handler_work_base(const Executor& ex, 144 const OtherExecutor&) BOOST_ASIO_NOEXCEPT 145 : executor_(ex), 146 owns_work_(true) 147 { 148 executor_.on_work_started(); 149 } 150 handler_work_base(const handler_work_base & other)151 handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT 152 : executor_(other.executor_), 153 owns_work_(other.owns_work_) 154 { 155 if (owns_work_) 156 executor_.on_work_started(); 157 } 158 159 #if defined(BOOST_ASIO_HAS_MOVE) handler_work_base(handler_work_base && other)160 handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT 161 : executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)), 162 owns_work_(other.owns_work_) 163 { 164 other.owns_work_ = false; 165 } 166 #endif // defined(BOOST_ASIO_HAS_MOVE) 167 ~handler_work_base()168 ~handler_work_base() 169 { 170 if (owns_work_) 171 executor_.on_work_finished(); 172 } 173 owns_work() const174 bool owns_work() const BOOST_ASIO_NOEXCEPT 175 { 176 return owns_work_; 177 } 178 179 template <typename Function, typename Handler> dispatch(Function & function,Handler & handler)180 void dispatch(Function& function, Handler& handler) 181 { 182 executor_.dispatch(BOOST_ASIO_MOVE_CAST(Function)(function), 183 boost::asio::get_associated_allocator(handler)); 184 } 185 186 private: 187 Executor executor_; 188 bool owns_work_; 189 }; 190 191 template <typename Executor, typename IoContext, typename PolymorphicExecutor> 192 class handler_work_base<Executor, void, IoContext, PolymorphicExecutor, 193 typename enable_if< 194 is_same< 195 Executor, 196 typename IoContext::executor_type 197 >::value 198 >::type> 199 { 200 public: handler_work_base(int,int,const Executor &)201 explicit handler_work_base(int, int, const Executor&) 202 { 203 } 204 owns_work() const205 bool owns_work() const BOOST_ASIO_NOEXCEPT 206 { 207 return false; 208 } 209 210 template <typename Function, typename Handler> dispatch(Function & function,Handler & handler)211 void dispatch(Function& function, Handler& handler) 212 { 213 // When using a native implementation, I/O completion handlers are 214 // already dispatched according to the execution context's executor's 215 // rules. We can call the function directly. 216 boost_asio_handler_invoke_helpers::invoke(function, handler); 217 } 218 }; 219 220 template <typename Executor, typename IoContext> 221 class handler_work_base<Executor, void, IoContext, Executor> 222 { 223 public: handler_work_base(int,int,const Executor & ex)224 explicit handler_work_base(int, int, const Executor& ex) BOOST_ASIO_NOEXCEPT 225 #if !defined(BOOST_ASIO_NO_TYPEID) 226 : executor_( 227 ex.target_type() == typeid(typename IoContext::executor_type) 228 ? Executor() : ex) 229 #else // !defined(BOOST_ASIO_NO_TYPEID) 230 : executor_(ex) 231 #endif // !defined(BOOST_ASIO_NO_TYPEID) 232 { 233 if (executor_) 234 executor_.on_work_started(); 235 } 236 handler_work_base(const Executor & ex,const Executor & candidate)237 handler_work_base(const Executor& ex, 238 const Executor& candidate) BOOST_ASIO_NOEXCEPT 239 : executor_(ex != candidate ? ex : Executor()) 240 { 241 if (executor_) 242 executor_.on_work_started(); 243 } 244 245 template <typename OtherExecutor> handler_work_base(const Executor & ex,const OtherExecutor &)246 handler_work_base(const Executor& ex, 247 const OtherExecutor&) BOOST_ASIO_NOEXCEPT 248 : executor_(ex) 249 { 250 executor_.on_work_started(); 251 } 252 handler_work_base(const handler_work_base & other)253 handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT 254 : executor_(other.executor_) 255 { 256 if (executor_) 257 executor_.on_work_started(); 258 } 259 260 #if defined(BOOST_ASIO_HAS_MOVE) handler_work_base(handler_work_base && other)261 handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT 262 : executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)) 263 { 264 } 265 #endif // defined(BOOST_ASIO_HAS_MOVE) 266 ~handler_work_base()267 ~handler_work_base() 268 { 269 if (executor_) 270 executor_.on_work_finished(); 271 } 272 owns_work() const273 bool owns_work() const BOOST_ASIO_NOEXCEPT 274 { 275 return !!executor_; 276 } 277 278 template <typename Function, typename Handler> dispatch(Function & function,Handler & handler)279 void dispatch(Function& function, Handler& handler) 280 { 281 executor_.dispatch(BOOST_ASIO_MOVE_CAST(Function)(function), 282 boost::asio::get_associated_allocator(handler)); 283 } 284 285 private: 286 Executor executor_; 287 }; 288 289 template < 290 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 291 typename... SupportableProperties, 292 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 293 typename T1, typename T2, typename T3, typename T4, typename T5, 294 typename T6, typename T7, typename T8, typename T9, 295 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 296 typename IoContext, typename PolymorphicExecutor> 297 class handler_work_base< 298 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 299 execution::any_executor<SupportableProperties...>, 300 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 301 execution::any_executor<T1, T2, T3, T4, T5, T6, T7, T8, T9>, 302 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 303 void, IoContext, PolymorphicExecutor> 304 { 305 public: 306 typedef 307 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 308 execution::any_executor<SupportableProperties...> 309 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 310 execution::any_executor<T1, T2, T3, T4, T5, T6, T7, T8, T9> 311 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 312 executor_type; 313 handler_work_base(int,int,const executor_type & ex)314 explicit handler_work_base(int, int, 315 const executor_type& ex) BOOST_ASIO_NOEXCEPT 316 #if !defined(BOOST_ASIO_NO_TYPEID) 317 : executor_( 318 ex.target_type() == typeid(typename IoContext::executor_type) 319 ? executor_type() 320 : boost::asio::prefer(ex, execution::outstanding_work.tracked)) 321 #else // !defined(BOOST_ASIO_NO_TYPEID) 322 : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) 323 #endif // !defined(BOOST_ASIO_NO_TYPEID) 324 { 325 } 326 handler_work_base(const executor_type & ex,const executor_type & candidate)327 handler_work_base(const executor_type& ex, 328 const executor_type& candidate) BOOST_ASIO_NOEXCEPT 329 : executor_(ex != candidate ? ex : executor_type()) 330 { 331 } 332 333 template <typename OtherExecutor> handler_work_base(const executor_type & ex,const OtherExecutor &)334 handler_work_base(const executor_type& ex, 335 const OtherExecutor&) BOOST_ASIO_NOEXCEPT 336 : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) 337 { 338 } 339 handler_work_base(const handler_work_base & other)340 handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT 341 : executor_(other.executor_) 342 { 343 } 344 345 #if defined(BOOST_ASIO_HAS_MOVE) handler_work_base(handler_work_base && other)346 handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT 347 : executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_)) 348 { 349 } 350 #endif // defined(BOOST_ASIO_HAS_MOVE) 351 owns_work() const352 bool owns_work() const BOOST_ASIO_NOEXCEPT 353 { 354 return !!executor_; 355 } 356 357 template <typename Function, typename Handler> dispatch(Function & function,Handler &)358 void dispatch(Function& function, Handler&) 359 { 360 execution::execute( 361 boost::asio::prefer(executor_, execution::blocking.possibly), 362 BOOST_ASIO_MOVE_CAST(Function)(function)); 363 } 364 365 private: 366 executor_type executor_; 367 }; 368 369 #if !defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) 370 371 template <typename Executor, typename IoContext, typename PolymorphicExecutor> 372 class handler_work_base<Executor, void, IoContext, PolymorphicExecutor, 373 typename enable_if< 374 is_same< 375 Executor, 376 any_io_executor 377 >::value 378 >::type> 379 { 380 public: 381 typedef Executor executor_type; 382 handler_work_base(int,int,const executor_type & ex)383 explicit handler_work_base(int, int, 384 const executor_type& ex) BOOST_ASIO_NOEXCEPT 385 #if !defined(BOOST_ASIO_NO_TYPEID) 386 : executor_( 387 ex.target_type() == typeid(typename IoContext::executor_type) 388 ? executor_type() 389 : boost::asio::prefer(ex, execution::outstanding_work.tracked)) 390 #else // !defined(BOOST_ASIO_NO_TYPEID) 391 : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) 392 #endif // !defined(BOOST_ASIO_NO_TYPEID) 393 { 394 } 395 handler_work_base(const executor_type & ex,const executor_type & candidate)396 handler_work_base(const executor_type& ex, 397 const executor_type& candidate) BOOST_ASIO_NOEXCEPT 398 : executor_(ex != candidate ? ex : executor_type()) 399 { 400 } 401 402 template <typename OtherExecutor> handler_work_base(const executor_type & ex,const OtherExecutor &)403 handler_work_base(const executor_type& ex, 404 const OtherExecutor&) BOOST_ASIO_NOEXCEPT 405 : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) 406 { 407 } 408 handler_work_base(const handler_work_base & other)409 handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT 410 : executor_(other.executor_) 411 { 412 } 413 414 #if defined(BOOST_ASIO_HAS_MOVE) handler_work_base(handler_work_base && other)415 handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT 416 : executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_)) 417 { 418 } 419 #endif // defined(BOOST_ASIO_HAS_MOVE) 420 owns_work() const421 bool owns_work() const BOOST_ASIO_NOEXCEPT 422 { 423 return !!executor_; 424 } 425 426 template <typename Function, typename Handler> dispatch(Function & function,Handler &)427 void dispatch(Function& function, Handler&) 428 { 429 execution::execute( 430 boost::asio::prefer(executor_, execution::blocking.possibly), 431 BOOST_ASIO_MOVE_CAST(Function)(function)); 432 } 433 434 private: 435 executor_type executor_; 436 }; 437 438 #endif // !defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) 439 440 template <typename Handler, typename IoExecutor, typename = void> 441 class handler_work : 442 handler_work_base<IoExecutor>, 443 handler_work_base<typename associated_executor< 444 Handler, IoExecutor>::type, IoExecutor> 445 { 446 public: 447 typedef handler_work_base<IoExecutor> base1_type; 448 typedef handler_work_base<typename associated_executor< 449 Handler, IoExecutor>::type, IoExecutor> base2_type; 450 handler_work(Handler & handler,const IoExecutor & io_ex)451 handler_work(Handler& handler, const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT 452 : base1_type(0, 0, io_ex), 453 base2_type(boost::asio::get_associated_executor(handler, io_ex), io_ex) 454 { 455 } 456 457 template <typename Function> complete(Function & function,Handler & handler)458 void complete(Function& function, Handler& handler) 459 { 460 if (!base1_type::owns_work() && !base2_type::owns_work()) 461 { 462 // When using a native implementation, I/O completion handlers are 463 // already dispatched according to the execution context's executor's 464 // rules. We can call the function directly. 465 boost_asio_handler_invoke_helpers::invoke(function, handler); 466 } 467 else 468 { 469 base2_type::dispatch(function, handler); 470 } 471 } 472 }; 473 474 template <typename Handler, typename IoExecutor> 475 class handler_work< 476 Handler, IoExecutor, 477 typename enable_if< 478 is_same< 479 typename associated_executor<Handler, 480 IoExecutor>::asio_associated_executor_is_unspecialised, 481 void 482 >::value 483 >::type> : handler_work_base<IoExecutor> 484 { 485 public: 486 typedef handler_work_base<IoExecutor> base1_type; 487 handler_work(Handler &,const IoExecutor & io_ex)488 handler_work(Handler&, const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT 489 : base1_type(0, 0, io_ex) 490 { 491 } 492 493 template <typename Function> complete(Function & function,Handler & handler)494 void complete(Function& function, Handler& handler) 495 { 496 if (!base1_type::owns_work()) 497 { 498 // When using a native implementation, I/O completion handlers are 499 // already dispatched according to the execution context's executor's 500 // rules. We can call the function directly. 501 boost_asio_handler_invoke_helpers::invoke(function, handler); 502 } 503 else 504 { 505 base1_type::dispatch(function, handler); 506 } 507 } 508 }; 509 510 } // namespace detail 511 } // namespace asio 512 } // namespace boost 513 514 #include <boost/asio/detail/pop_options.hpp> 515 516 #endif // BOOST_ASIO_DETAIL_HANDLER_WORK_HPP 517