boost/beast/experimental/core/detail/impl/timeout_service.ipp
// // Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail 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) // // Official repository: https://github.com/boostorg/beast // #ifndef BOOST_BEAST_CORE_DETAIL_IMPL_TIMEOUT_SERVICE_IPP #define BOOST_BEAST_CORE_DETAIL_IMPL_TIMEOUT_SERVICE_IPP namespace boost { namespace beast { namespace detail { //------------------------------------------------------------------------------ inline timeout_object:: timeout_object(boost::asio::io_context& ioc) : svc_(boost::asio::use_service<timeout_service>(ioc)) { } //------------------------------------------------------------------------------ inline timeout_service:: timeout_service(boost::asio::io_context& ctx) : service_base(ctx) , strand_(ctx.get_executor()) , timer_(ctx) { } inline void timeout_service:: on_work_started(timeout_object& obj) { std::lock_guard<std::mutex> lock(m_); BOOST_VERIFY(++obj.outstanding_work_ == 1); insert(obj, *fresh_); if(++count_ == 1) do_async_wait(); } inline void timeout_service:: on_work_complete(timeout_object& obj) { std::lock_guard<std::mutex> lock(m_); remove(obj); } inline void timeout_service:: on_work_stopped(timeout_object& obj) { std::lock_guard<std::mutex> lock(m_); BOOST_ASSERT(count_ > 0); BOOST_VERIFY(--obj.outstanding_work_ == 0); if(obj.list_ != nullptr) remove(obj); if(--count_ == 0) timer_.cancel(); } inline void timeout_service:: set_option(std::chrono::seconds n) { interval_ = n; } //------------------------------------------------------------------------------ // Precondition: caller holds the mutex inline void timeout_service:: insert(timeout_object& obj, list_type& list) { BOOST_ASSERT(obj.list_ == nullptr); list.push_back(&obj); // can throw obj.list_ = &list; obj.pos_ = list.size(); } // Precondition: caller holds the mutex inline void timeout_service:: remove(timeout_object& obj) { BOOST_ASSERT(obj.list_ != nullptr); BOOST_ASSERT( obj.list_ == stale_ || obj.list_ == fresh_); BOOST_ASSERT(obj.list_->size() > 0); auto& list = *obj.list_; auto const n = list.size() - 1; if(obj.pos_ != n) { auto other = list[n]; list[obj.pos_] = other; other->pos_ = obj.pos_; } obj.list_ = nullptr; list.resize(n); } inline void timeout_service:: do_async_wait() { timer_.expires_after(interval_); timer_.async_wait( boost::asio::bind_executor( strand_, [this](error_code ec) { this->on_timer(ec); })); } inline void timeout_service:: on_timer(error_code ec) { if(ec == boost::asio::error::operation_aborted) { BOOST_ASSERT(fresh_->empty()); BOOST_ASSERT(stale_->empty()); return; } { std::lock_guard<std::mutex> lock(m_); if(! stale_->empty()) { for(auto obj : *stale_) { obj->list_ = nullptr; obj->on_timeout(); } stale_->clear(); } std::swap(fresh_, stale_); } do_async_wait(); } //------------------------------------------------------------------------------ inline void timeout_service:: shutdown() noexcept { boost::asio::post( boost::asio::bind_executor( strand_, [this]() { timer_.cancel(); })); } } // detail } // beast } // boost #endif