File indexing completed on 2025-04-19 08:33:50
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_MYSQL_IMPL_INTERNAL_CONNECTION_POOL_RUN_WITH_TIMEOUT_HPP
0009 #define BOOST_MYSQL_IMPL_INTERNAL_CONNECTION_POOL_RUN_WITH_TIMEOUT_HPP
0010
0011 #include <boost/mysql/client_errc.hpp>
0012 #include <boost/mysql/error_code.hpp>
0013
0014 #include <boost/asio/any_io_executor.hpp>
0015 #include <boost/asio/associated_allocator.hpp>
0016 #include <boost/asio/bind_executor.hpp>
0017 #include <boost/asio/cancellation_signal.hpp>
0018
0019 #include <chrono>
0020 #include <cstddef>
0021 #include <memory>
0022 #include <type_traits>
0023 #include <utility>
0024
0025
0026
0027
0028
0029 namespace boost {
0030 namespace mysql {
0031 namespace detail {
0032
0033
0034
0035 template <class Timer, class Handler>
0036 struct run_with_timeout_state
0037 {
0038 using this_type = run_with_timeout_state<Timer, Handler>;
0039
0040
0041 asio::cancellation_signal op_signal;
0042
0043
0044 std::size_t remaining;
0045
0046
0047 error_code final_ec;
0048
0049
0050 Handler handler;
0051
0052
0053 Timer& timer;
0054
0055 run_with_timeout_state(Handler&& handler, Timer& timer)
0056 : remaining(2), handler(std::move(handler)), timer(timer)
0057 {
0058 }
0059
0060
0061 static void complete_one_op(std::shared_ptr<this_type>&& ptr)
0062 {
0063
0064 if (ptr->remaining == 0u)
0065 {
0066
0067 auto h = std::move(ptr->handler);
0068 error_code ec = ptr->final_ec;
0069
0070
0071 ptr.reset();
0072
0073
0074 std::move(h)(ec);
0075 }
0076 }
0077
0078
0079 struct timer_handler
0080 {
0081 std::shared_ptr<this_type> st;
0082
0083 void operator()(error_code ec)
0084 {
0085
0086
0087 if (st->remaining-- == 2u)
0088 {
0089 st->final_ec = ec ? client_errc::cancelled : client_errc::timeout;
0090 st->op_signal.emit(asio::cancellation_type::terminal);
0091 }
0092
0093
0094 complete_one_op(std::move(st));
0095 }
0096 };
0097
0098
0099
0100 struct op_handler
0101 {
0102 std::shared_ptr<this_type> st;
0103
0104 void operator()(error_code ec)
0105 {
0106
0107 if (st->remaining-- == 2u)
0108 {
0109 st->final_ec = ec;
0110 st->timer.cancel();
0111 }
0112
0113
0114 complete_one_op(std::move(st));
0115 }
0116
0117
0118 using executor_type = asio::any_io_executor;
0119 executor_type get_executor() const { return st->timer.get_executor(); }
0120
0121
0122 using cancellation_slot_type = asio::cancellation_slot;
0123 cancellation_slot_type get_cancellation_slot() const noexcept { return st->op_signal.slot(); }
0124 };
0125 };
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135 template <class Op, class Timer, class Handler>
0136 void run_with_timeout(Op&& op, Timer& timer, std::chrono::steady_clock::duration timeout, Handler&& handler)
0137 {
0138 if (timeout.count() > 0)
0139 {
0140 using state_t = run_with_timeout_state<Timer, typename std::decay<Handler>::type>;
0141
0142
0143 auto alloc = asio::get_associated_allocator(handler);
0144 using alloc_t = typename std::allocator_traits<decltype(alloc)>::template rebind_alloc<state_t>;
0145 auto st = std::allocate_shared<state_t>(alloc_t(alloc), std::move(handler), timer);
0146
0147
0148 timer.expires_after(timeout);
0149 timer.async_wait(typename state_t::timer_handler{st});
0150
0151
0152 std::move(op)(typename state_t::op_handler{std::move(st)});
0153 }
0154 else
0155 {
0156 std::forward<Op>(op)(asio::bind_executor(timer.get_executor(), std::move(handler)));
0157 }
0158 }
0159
0160 }
0161 }
0162 }
0163
0164 #endif