// // detail/timed_cancel_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TIMED_CANCEL_OP_HPP #define ASIO_DETAIL_TIMED_CANCEL_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/associated_cancellation_slot.hpp" #include "asio/associator.hpp" #include "asio/basic_waitable_timer.hpp" #include "asio/cancellation_signal.hpp" #include "asio/detail/atomic_count.hpp" #include "asio/detail/completion_payload.hpp" #include "asio/detail/completion_payload_handler.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/type_traits.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class timed_cancel_op_handler; template class timed_cancel_timer_handler; template class timed_cancel_op { public: using handler_type = Handler; ASIO_DEFINE_TAGGED_HANDLER_PTR( thread_info_base::timed_cancel_tag, timed_cancel_op); timed_cancel_op(Handler& handler, Timer timer, cancellation_type_t cancel_type) : ref_count_(2), handler_(static_cast(handler)), timer_(static_cast(timer)), cancellation_type_(cancel_type), cancel_proxy_(nullptr), has_payload_(false), has_pending_timer_wait_(true) { } ~timed_cancel_op() { if (has_payload_) payload_storage_.payload_.~payload_type(); } cancellation_slot get_cancellation_slot() noexcept { return cancellation_signal_.slot(); } template void start(Initiation&& initiation, Args&&... args) { using op_handler_type = timed_cancel_op_handler; op_handler_type op_handler(this); using timer_handler_type = timed_cancel_timer_handler; timer_handler_type timer_handler(this); associated_cancellation_slot_t slot = (get_associated_cancellation_slot)(handler_); if (slot.is_connected()) cancel_proxy_ = &slot.template emplace(this); timer_.async_wait(static_cast(timer_handler)); async_initiate( static_cast(initiation), static_cast(op_handler), static_cast(args)...); } template void handle_op(Message&& message) { if (cancel_proxy_) cancel_proxy_->op_ = nullptr; new (&payload_storage_.payload_) payload_type( static_cast(message)); has_payload_ = true; if (has_pending_timer_wait_) { timer_.cancel(); release(); } else { complete(); } } void handle_timer() { has_pending_timer_wait_ = false; if (has_payload_) { complete(); } else { cancellation_signal_.emit(cancellation_type_); release(); } } void release() { if (--ref_count_ == 0) { ptr p = { asio::detail::addressof(handler_), this, this }; Handler handler(static_cast(handler_)); p.h = asio::detail::addressof(handler); p.reset(); } } void complete() { if (--ref_count_ == 0) { ptr p = { asio::detail::addressof(handler_), this, this }; completion_payload_handler handler( static_cast(payload_storage_.payload_), handler_); p.h = asio::detail::addressof(handler.handler()); p.reset(); handler(); } } //private: typedef completion_payload payload_type; struct cancel_proxy { cancel_proxy(timed_cancel_op* op) : op_(op) { } void operator()(cancellation_type_t type) { if (op_) op_->cancellation_signal_.emit(type); } timed_cancel_op* op_; }; // The number of handlers that share a reference to the state. atomic_count ref_count_; // The handler to be called when the operation completes. Handler handler_; // The timer used to determine when to cancel the pending operation. Timer timer_; // The cancellation signal and type used to cancel the pending operation. cancellation_signal cancellation_signal_; cancellation_type_t cancellation_type_; // A proxy cancel handler used to allow cancellation of the timed operation. cancel_proxy* cancel_proxy_; // Arguments to be passed to the completion handler. union payload_storage { payload_storage() {} ~payload_storage() {} char dummy_; payload_type payload_; } payload_storage_; // Whether the payload storage contains a valid payload. bool has_payload_; // Whether the asynchronous wait on the timer is still pending bool has_pending_timer_wait_; }; template class timed_cancel_op_handler { public: using cancellation_slot_type = cancellation_slot; explicit timed_cancel_op_handler(Op* op) : op_(op) { } timed_cancel_op_handler(timed_cancel_op_handler&& other) noexcept : op_(other.op_) { other.op_ = nullptr; } ~timed_cancel_op_handler() { if (op_) op_->release(); } cancellation_slot_type get_cancellation_slot() const noexcept { return op_->get_cancellation_slot(); } template enable_if_t< is_constructible, int, Args2...>::value > operator()(Args2&&... args) { Op* op = op_; op_ = nullptr; typedef completion_message message_type; op->handle_op(message_type(0, static_cast(args)...)); } //protected: Op* op_; }; template class timed_cancel_op_handler : public timed_cancel_op_handler { public: using timed_cancel_op_handler::timed_cancel_op_handler; using timed_cancel_op_handler::operator(); template enable_if_t< is_constructible, int, Args2...>::value > operator()(Args2&&... args) { Op* op = this->op_; this->op_ = nullptr; typedef completion_message message_type; op->handle_op(message_type(0, static_cast(args)...)); } }; template class timed_cancel_timer_handler { public: using cancellation_slot_type = cancellation_slot; explicit timed_cancel_timer_handler(Op* op) : op_(op) { } timed_cancel_timer_handler(timed_cancel_timer_handler&& other) noexcept : op_(other.op_) { other.op_ = nullptr; } ~timed_cancel_timer_handler() { if (op_) op_->release(); } cancellation_slot_type get_cancellation_slot() const noexcept { return cancellation_slot_type(); } void operator()(const asio::error_code&) { Op* op = op_; op_ = nullptr; op->handle_timer(); } //private: Op* op_; }; } // namespace detail template