mirror of
https://github.com/boostorg/beast.git
synced 2025-07-29 20:37:31 +02:00
Fixes to timeout services (experimental)
This commit is contained in:
@ -1,9 +1,8 @@
|
||||
Version 192:
|
||||
|
||||
* Use mp11::integer_sequence
|
||||
* Tidy up warnings and deprecated usage
|
||||
* http::message is not-a boost::empty_value
|
||||
* Fix link in docs
|
||||
* Fixes to timeout services (experimental)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -310,12 +310,15 @@
|
||||
<member><link linkend="beast.ref.boost__beast__http__icy_stream">http::icy_stream</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__test__fail_count">test::fail_count</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__test__stream">test::stream</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__timeout_handle">timeout_handle</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__timeout_socket">timeout_socket</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__timeout_work_guard">timeout_work_guard</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.boost__beast__async_connect">async_connect</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__set_timeout_service_options">set_timeout_service_options</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__test__connect">test::connect</link></member>
|
||||
</simplelist>
|
||||
|
@ -0,0 +1,326 @@
|
||||
//
|
||||
// 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_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_IMPL_TIMEOUT_SERVICE_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
timeout_service::
|
||||
timeout_service(boost::asio::io_context& ctx)
|
||||
: service_base(ctx)
|
||||
, thunks_(1) // element [0] reserved for "null"
|
||||
, timer_(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
timeout_handle
|
||||
timeout_service::
|
||||
make_handle()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
if(free_thunk_ != 0)
|
||||
{
|
||||
auto const n = free_thunk_;
|
||||
auto& t = thunks_[n];
|
||||
free_thunk_ = t.pos; // next in free list
|
||||
t = {};
|
||||
return timeout_handle(n, *this);
|
||||
}
|
||||
auto const n = thunks_.size();
|
||||
thunks_.emplace_back();
|
||||
return timeout_handle(n, *this);
|
||||
}
|
||||
|
||||
void
|
||||
timeout_service::
|
||||
set_option(std::chrono::seconds n)
|
||||
{
|
||||
interval_ = n;
|
||||
}
|
||||
|
||||
template<class Executor, class CancelHandler>
|
||||
void
|
||||
timeout_service::
|
||||
set_callback(
|
||||
timeout_handle h,
|
||||
Executor const& ex,
|
||||
CancelHandler&& handler)
|
||||
{
|
||||
thunks_[h.id_].callback.emplace(
|
||||
callback<
|
||||
Executor,
|
||||
typename std::decay<CancelHandler>::type>{
|
||||
h, ex,
|
||||
std::forward<CancelHandler>(handler)});
|
||||
}
|
||||
|
||||
void
|
||||
timeout_service::
|
||||
on_work_started(timeout_handle h)
|
||||
{
|
||||
BOOST_ASSERT(h.id_ != 0);
|
||||
if( [this, h]
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
auto& t = thunks_[h.id_];
|
||||
insert(t, *fresh_);
|
||||
return ++pending_ == 1;
|
||||
}())
|
||||
{
|
||||
do_async_wait();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
timeout_service::
|
||||
on_work_stopped(timeout_handle h)
|
||||
{
|
||||
BOOST_ASSERT(h.id_ != 0);
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
auto& t = thunks_[h.id_];
|
||||
if(t.list != nullptr)
|
||||
{
|
||||
BOOST_ASSERT(! t.expired);
|
||||
remove(t);
|
||||
}
|
||||
if(--pending_ == 0)
|
||||
timer_.cancel();
|
||||
BOOST_ASSERT(pending_ >= 0);
|
||||
}
|
||||
|
||||
/*
|
||||
Synchronization points
|
||||
|
||||
(A) async_op invoke
|
||||
(B) timeout handler invoke expired=true
|
||||
(C) posted handler invoked canceled=true
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
Linearized paths (for async_read)
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
1. async_read
|
||||
2. async_read complete, async_op posted
|
||||
(A) 3. async_op invoked
|
||||
- work_.try_complete() returns true
|
||||
+ expired==false
|
||||
+ thunk is removed from list
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
1. async_read
|
||||
2. async_read complete, async_op posted
|
||||
(B) 3. timeout, cancel posted
|
||||
- expired=true
|
||||
- thunk is removed from list
|
||||
(A) 4. async_op invoked
|
||||
- work_.try_complete() returns false
|
||||
+ completed=true
|
||||
- handler is saved
|
||||
(C) 5. cancel invoked
|
||||
- saved handler is invoked
|
||||
+ expired==true, canceled==false, completed==true
|
||||
+ work_.try_complete() returns true
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
The following two paths are not distinguishable:
|
||||
|
||||
1. async_read
|
||||
(B) 2. timeout, cancel posted
|
||||
- expired=true
|
||||
- thunk is removed from list
|
||||
3. async_read complete, async_op posted
|
||||
(C) 4. cancel invoked
|
||||
- socket::cancel called (what does this do?)
|
||||
- canceled=true
|
||||
(A) 5. async_op invoked
|
||||
- expired==true, canceled==true, completed==false
|
||||
- work_.try_complete() returns `true`
|
||||
|
||||
1. async_read
|
||||
(B) 2. timeout, `cancel` posted
|
||||
- expired=true
|
||||
- thunk is removed from list
|
||||
(C) 3. cancel invoked, async_read canceled
|
||||
- socket::cancel called
|
||||
- canceled=true
|
||||
(A) 4. async_op invoked, ec==operation_aborted
|
||||
- expired==true, canceled==true, completed=false
|
||||
- work_.try_complete()` returns true
|
||||
*/
|
||||
bool
|
||||
timeout_service::
|
||||
on_try_work_complete(timeout_handle h)
|
||||
{
|
||||
BOOST_ASSERT(h.id_ != 0);
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
auto& t = thunks_[h.id_];
|
||||
if(! t.expired)
|
||||
{
|
||||
// hot path: operation complete
|
||||
BOOST_ASSERT(t.list != nullptr);
|
||||
BOOST_ASSERT(! t.canceled);
|
||||
BOOST_ASSERT(! t.completed);
|
||||
remove(t);
|
||||
return true;
|
||||
}
|
||||
BOOST_ASSERT(t.list == nullptr);
|
||||
if(! t.canceled)
|
||||
{
|
||||
// happens when operation completes before
|
||||
// posted cancel handler is invoked.
|
||||
t.completed = true;
|
||||
return false;
|
||||
}
|
||||
if(t.completed)
|
||||
{
|
||||
// happens when the saved handler is
|
||||
// invoked from the posted callback
|
||||
t.expired = false;
|
||||
t.canceled = false;
|
||||
t.completed = false;
|
||||
return true;
|
||||
}
|
||||
// happens when operation_aborted is delivered
|
||||
t.expired = false;
|
||||
t.canceled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
timeout_service::
|
||||
on_cancel(timeout_handle h)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
auto& t = thunks_[h.id_];
|
||||
BOOST_ASSERT(t.expired);
|
||||
t.canceled = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
timeout_service::
|
||||
destroy(timeout_handle h)
|
||||
{
|
||||
BOOST_ASSERT(h.id_ != 0);
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
thunks_[h.id_].pos = free_thunk_;
|
||||
free_thunk_ = h.id_;
|
||||
}
|
||||
|
||||
// Precondition: caller holds the mutex
|
||||
void
|
||||
timeout_service::
|
||||
insert(
|
||||
thunk& t,
|
||||
thunk::list_type& list)
|
||||
{
|
||||
BOOST_ASSERT(t.list == nullptr);
|
||||
list.emplace_back(&t); // can throw
|
||||
t.list = &list;
|
||||
t.pos = list.size();
|
||||
}
|
||||
|
||||
// Precondition: caller holds the mutex
|
||||
void
|
||||
timeout_service::
|
||||
remove(thunk& t)
|
||||
{
|
||||
BOOST_ASSERT(t.list != nullptr);
|
||||
BOOST_ASSERT(
|
||||
t.list == stale_ ||
|
||||
t.list == fresh_);
|
||||
BOOST_ASSERT(t.list->size() > 0);
|
||||
auto& list = *t.list;
|
||||
auto const n = list.size() - 1;
|
||||
if(t.pos != n)
|
||||
{
|
||||
// move back element to t.pos
|
||||
list[t.pos] = list[n];
|
||||
list[t.pos]->pos = t.pos;
|
||||
}
|
||||
t.list = nullptr;
|
||||
list.resize(n);
|
||||
}
|
||||
|
||||
void
|
||||
timeout_service::
|
||||
do_async_wait()
|
||||
{
|
||||
timer_.expires_after(interval_);
|
||||
timer_.async_wait(
|
||||
[this](error_code ec)
|
||||
{
|
||||
this->on_timer(ec);
|
||||
});
|
||||
}
|
||||
|
||||
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::vector<thunk*> expired;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
if(! stale_->empty())
|
||||
{
|
||||
for(auto t : *stale_)
|
||||
{
|
||||
// remove from list
|
||||
t->list = nullptr;
|
||||
t->expired = true;
|
||||
}
|
||||
std::swap(expired, *stale_);
|
||||
stale_->reserve(expired.size() / 2);
|
||||
}
|
||||
std::swap(fresh_, stale_);
|
||||
}
|
||||
for(auto p : expired)
|
||||
p->callback();
|
||||
if( [this]
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
BOOST_ASSERT(pending_);
|
||||
pending_ =
|
||||
! stale_->empty() ||
|
||||
! fresh_->empty();
|
||||
return pending_;
|
||||
}())
|
||||
{
|
||||
do_async_wait();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
timeout_service::
|
||||
shutdown() noexcept
|
||||
{
|
||||
// The ExecutionContext is already in a stopped
|
||||
// state, so no synchronization is required.
|
||||
timer_.cancel();
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@ -1,181 +0,0 @@
|
||||
//
|
||||
// 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
|
109
include/boost/beast/_experimental/core/detail/saved_handler.hpp
Normal file
109
include/boost/beast/_experimental/core/detail/saved_handler.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
//
|
||||
// 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_SAVED_HANDLER_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_SAVED_HANDLER_HPP
|
||||
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
class saved_handler
|
||||
{
|
||||
struct base
|
||||
{
|
||||
virtual ~base() = default;
|
||||
virtual void operator()() = 0;
|
||||
};
|
||||
|
||||
template<class Handler>
|
||||
struct impl : base
|
||||
{
|
||||
Handler h_;
|
||||
|
||||
template<class Deduced>
|
||||
explicit
|
||||
impl(Deduced&& h)
|
||||
: h_(std::forward<Deduced>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void operator()() override
|
||||
{
|
||||
h_();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<base> p_;
|
||||
|
||||
public:
|
||||
saved_handler() = default;
|
||||
|
||||
template<class Handler>
|
||||
void
|
||||
emplace(Handler&& h)
|
||||
{
|
||||
p_.reset(new impl<typename
|
||||
std::decay<Handler>::type>(
|
||||
std::forward<Handler>(h)));
|
||||
}
|
||||
|
||||
template<class Handler,
|
||||
class T0, class... TN>
|
||||
void
|
||||
emplace(Handler&& h,
|
||||
T0&& t0, TN&... tn)
|
||||
{
|
||||
using type = decltype(
|
||||
beast::bind_front_handler(
|
||||
std::forward<Handler>(h),
|
||||
std::forward<T0>(t0),
|
||||
std::forward<TN>(tn)...));
|
||||
p_.reset(new impl<type>(
|
||||
beast::bind_front_handler(
|
||||
std::forward<Handler>(h),
|
||||
std::forward<T0>(t0),
|
||||
std::forward<TN>(tn)...)));
|
||||
}
|
||||
|
||||
bool
|
||||
empty() const noexcept
|
||||
{
|
||||
return p_.get() == nullptr;
|
||||
}
|
||||
|
||||
explicit
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return ! empty();
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
auto p = std::move(p_);
|
||||
(*p)();
|
||||
}
|
||||
|
||||
void
|
||||
reset()
|
||||
{
|
||||
p_.reset(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@ -10,115 +10,124 @@
|
||||
#ifndef BOOST_BEAST_CORE_DETAIL_TIMEOUT_SERVICE_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_TIMEOUT_SERVICE_HPP
|
||||
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/_experimental/core/detail/service_base.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/beast/_experimental/core/detail/timeout_service_base.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/asio/executor.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/io_context.hpp> // #include <boost/asio/execution_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
class timeout_handle;
|
||||
|
||||
namespace detail {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class timeout_service;
|
||||
|
||||
class timeout_object
|
||||
{
|
||||
friend class timeout_service;
|
||||
|
||||
using list_type = std::vector<timeout_object*>;
|
||||
|
||||
timeout_service& svc_;
|
||||
std::size_t pos_;
|
||||
list_type* list_ = nullptr;
|
||||
char outstanding_work_ = 0;
|
||||
|
||||
public:
|
||||
timeout_object() = delete;
|
||||
timeout_object(timeout_object&&) = delete;
|
||||
timeout_object(timeout_object const&) = delete;
|
||||
timeout_object& operator=(timeout_object&&) = delete;
|
||||
timeout_object& operator=(timeout_object const&) = delete;
|
||||
|
||||
// VFALCO should be execution_context
|
||||
explicit
|
||||
timeout_object(boost::asio::io_context& ioc);
|
||||
|
||||
timeout_service&
|
||||
service() const
|
||||
{
|
||||
return svc_;
|
||||
}
|
||||
|
||||
virtual void on_timeout() = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class timeout_service
|
||||
: public service_base<timeout_service>
|
||||
{
|
||||
template<class Executor, class Handler>
|
||||
struct callback
|
||||
{
|
||||
timeout_handle th;
|
||||
Executor ex;
|
||||
Handler h;
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
boost::asio::post(ex,
|
||||
beast::bind_front_handler(
|
||||
std::move(*this), 0));
|
||||
}
|
||||
|
||||
void
|
||||
operator()(int)
|
||||
{
|
||||
th.service().on_cancel(th);
|
||||
h();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
using key_type = timeout_service;
|
||||
|
||||
// VFALCO Should be execution_context
|
||||
BOOST_BEAST_DECL
|
||||
explicit
|
||||
timeout_service(boost::asio::io_context& ctx);
|
||||
|
||||
void
|
||||
on_work_started(timeout_object& obj);
|
||||
BOOST_BEAST_DECL
|
||||
timeout_handle
|
||||
make_handle();
|
||||
|
||||
void
|
||||
on_work_complete(timeout_object& obj);
|
||||
BOOST_BEAST_DECL
|
||||
void set_option(std::chrono::seconds n);
|
||||
|
||||
void
|
||||
on_work_stopped(timeout_object& obj);
|
||||
// Undefined if work is active
|
||||
template<class Executor, class CancelHandler>
|
||||
void set_callback(
|
||||
timeout_handle h,
|
||||
Executor const& ex,
|
||||
CancelHandler&& handler);
|
||||
|
||||
void
|
||||
set_option(std::chrono::seconds n);
|
||||
BOOST_BEAST_DECL
|
||||
void on_work_started(timeout_handle h);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void on_work_stopped(timeout_handle h);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
bool on_try_work_complete(timeout_handle h);
|
||||
|
||||
private:
|
||||
friend class timeout_object;
|
||||
friend class beast::timeout_handle;
|
||||
|
||||
using list_type = std::vector<timeout_object*>;
|
||||
BOOST_BEAST_DECL
|
||||
void destroy(timeout_handle h);
|
||||
|
||||
void insert(timeout_object& obj, list_type& list);
|
||||
void remove(timeout_object& obj);
|
||||
BOOST_BEAST_DECL
|
||||
void insert(thunk& t, thunk::list_type& list);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void remove(thunk& t);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void do_async_wait();
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void on_cancel(timeout_handle h);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void on_timer(error_code ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
virtual void shutdown() noexcept override;
|
||||
|
||||
boost::asio::strand<
|
||||
boost::asio::io_context::executor_type> strand_;
|
||||
|
||||
std::mutex m_;
|
||||
list_type list_[2];
|
||||
list_type* fresh_ = &list_[0];
|
||||
list_type* stale_ = &list_[1];
|
||||
std::size_t count_ = 0;
|
||||
thunk::list_type list_[2];
|
||||
thunk::list_type* fresh_ = &list_[0];
|
||||
thunk::list_type* stale_ = &list_[1];
|
||||
std::deque<thunk> thunks_;
|
||||
std::size_t free_thunk_ = 0;
|
||||
boost::asio::steady_timer timer_;
|
||||
std::chrono::seconds interval_{30ul};
|
||||
long pending_ = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/_experimental/core/detail/impl/timeout_service.ipp>
|
||||
#include <boost/beast/_experimental/core/detail/impl/timeout_service.hpp>
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,38 @@
|
||||
//
|
||||
// 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_TIMEOUT_SERVICE_BASE_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_TIMEOUT_SERVICE_BASE_HPP
|
||||
|
||||
#include <boost/beast/_experimental/core/detail/saved_handler.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
struct thunk
|
||||
{
|
||||
using list_type =
|
||||
std::vector<thunk*>;
|
||||
|
||||
saved_handler callback;
|
||||
list_type* list = nullptr;
|
||||
std::size_t pos = 0; // also: next in free list
|
||||
bool expired = false;
|
||||
bool canceled = false;
|
||||
bool completed = false;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@ -0,0 +1,64 @@
|
||||
//
|
||||
// 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_IMPL_TIMEOUT_SERVICE_HPP
|
||||
#define BOOST_BEAST_CORE_IMPL_TIMEOUT_SERVICE_HPP
|
||||
|
||||
#include <boost/beast/_experimental/core/detail/timeout_service.hpp>
|
||||
#include <boost/asio/execution_context.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
timeout_handle::
|
||||
timeout_handle(boost::asio::io_context& ioc)
|
||||
: timeout_handle(
|
||||
boost::asio::use_service<
|
||||
detail::timeout_service>(
|
||||
ioc).make_handle())
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
timeout_handle::
|
||||
destroy()
|
||||
{
|
||||
BOOST_ASSERT(svc_ != nullptr);
|
||||
svc_->destroy(*this);
|
||||
id_ = 0;
|
||||
svc_ = nullptr;
|
||||
}
|
||||
|
||||
template<class Executor, class CancelHandler>
|
||||
void
|
||||
timeout_handle::
|
||||
set_callback(
|
||||
Executor const& ex, CancelHandler&& handler)
|
||||
{
|
||||
svc_->set_callback(*this, ex,
|
||||
std::forward<CancelHandler>(handler));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
set_timeout_service_options(
|
||||
boost::asio::io_context& ioc,
|
||||
std::chrono::seconds interval)
|
||||
{
|
||||
boost::asio::use_service<
|
||||
detail::timeout_service>(
|
||||
ioc).set_option(interval);
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@ -1,31 +0,0 @@
|
||||
//
|
||||
// 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_IMPL_TIMEOUT_SERVICE_HPP
|
||||
#define BOOST_BEAST_CORE_IMPL_TIMEOUT_SERVICE_HPP
|
||||
|
||||
#include <boost/beast/_experimental/core/detail/timeout_service.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
inline
|
||||
void
|
||||
set_timeout_service_options(
|
||||
boost::asio::io_context& ioc,
|
||||
std::chrono::seconds interval)
|
||||
{
|
||||
boost::asio::use_service<
|
||||
detail::timeout_service>(ioc).set_option(interval);
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@ -7,11 +7,11 @@
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_IMPL_TIMOUT_SOCKET_HPP
|
||||
#define BOOST_BEAST_CORE_IMPL_TIMOUT_SOCKET_HPP
|
||||
#ifndef BOOST_BEAST_CORE_IMPL_TIMEOUT_SOCKET_HPP
|
||||
#define BOOST_BEAST_CORE_IMPL_TIMEOUT_SOCKET_HPP
|
||||
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/_experimental/core/detail/timeout_work_guard.hpp>
|
||||
#include <boost/beast/_experimental/core/timeout_work_guard.hpp>
|
||||
#include <boost/asio/executor_work_guard.hpp>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
@ -23,10 +23,6 @@ template<class Protocol, class Executor>
|
||||
template<class Handler>
|
||||
class basic_timeout_socket<Protocol, Executor>::async_op
|
||||
{
|
||||
Handler h_;
|
||||
basic_timeout_socket& s_;
|
||||
detail::timeout_work_guard work_;
|
||||
|
||||
public:
|
||||
async_op(async_op&&) = default;
|
||||
async_op(async_op const&) = delete;
|
||||
@ -40,6 +36,10 @@ public:
|
||||
: h_(std::forward<DeducedHandler>(h))
|
||||
, s_(s)
|
||||
, work_(s.rd_timer_)
|
||||
, wg0_(s_.get_executor())
|
||||
, wg1_(get_executor())
|
||||
, saved_(s_.rd_op_)
|
||||
|
||||
{
|
||||
s_.sock_.async_read_some(b, std::move(*this));
|
||||
}
|
||||
@ -53,6 +53,9 @@ public:
|
||||
: h_(std::forward<DeducedHandler>(h))
|
||||
, s_(s)
|
||||
, work_(s.wr_timer_)
|
||||
, wg0_(s_.get_executor())
|
||||
, wg1_(get_executor())
|
||||
, saved_(s_.wr_op_)
|
||||
{
|
||||
s_.sock_.async_write_some(b, std::move(*this));
|
||||
}
|
||||
@ -63,17 +66,18 @@ public:
|
||||
allocator_type
|
||||
get_allocator() const noexcept
|
||||
{
|
||||
return (boost::asio::get_associated_allocator)(h_);
|
||||
return boost::asio::get_associated_allocator(h_);
|
||||
}
|
||||
|
||||
using executor_type =
|
||||
boost::asio::associated_executor_t<Handler, decltype(
|
||||
std::declval<basic_timeout_socket<Protocol>&>().get_executor())>;
|
||||
boost::asio::associated_executor_t<Handler,
|
||||
decltype(std::declval<basic_timeout_socket<
|
||||
Protocol, Executor>&>().get_executor())>;
|
||||
|
||||
executor_type
|
||||
get_executor() const noexcept
|
||||
{
|
||||
return (boost::asio::get_associated_executor)(
|
||||
return boost::asio::get_associated_executor(
|
||||
h_, s_.get_executor());
|
||||
}
|
||||
|
||||
@ -96,66 +100,72 @@ public:
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(s_.expired_)
|
||||
if(! work_.try_complete())
|
||||
{
|
||||
BOOST_ASSERT(ec == boost::asio::error::operation_aborted);
|
||||
ec = boost::asio::error::timed_out;
|
||||
}
|
||||
else
|
||||
{
|
||||
work_.complete();
|
||||
saved_.emplace(
|
||||
std::move(*this),
|
||||
ec,
|
||||
bytes_transferred);
|
||||
return;
|
||||
}
|
||||
h_(ec, bytes_transferred);
|
||||
}
|
||||
|
||||
private:
|
||||
Handler h_;
|
||||
basic_timeout_socket& s_;
|
||||
timeout_work_guard work_;
|
||||
boost::asio::executor_work_guard<Executor> wg0_;
|
||||
boost::asio::executor_work_guard<executor_type> wg1_;
|
||||
detail::saved_handler& saved_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
timer::
|
||||
timer(basic_timeout_socket& s)
|
||||
: detail::timeout_object(s.ex_.context())
|
||||
, s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
auto
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
timer::
|
||||
operator=(timer&&)
|
||||
-> timer&
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
void
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
timer::
|
||||
on_timeout()
|
||||
{
|
||||
boost::asio::post(
|
||||
s_.ex_,
|
||||
[this]()
|
||||
{
|
||||
s_.expired_ = true;
|
||||
s_.sock_.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
template<class ExecutionContext, class>
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
basic_timeout_socket(ExecutionContext& ctx)
|
||||
: ex_(ctx.get_executor())
|
||||
, rd_timer_(*this)
|
||||
, wr_timer_(*this)
|
||||
, rd_timer_(ctx)
|
||||
, wr_timer_(ctx)
|
||||
, cn_timer_(ctx)
|
||||
, sock_(ctx)
|
||||
{
|
||||
rd_timer_.set_callback(ex_,
|
||||
[this]
|
||||
{
|
||||
if(rd_op_.empty())
|
||||
sock_.cancel();
|
||||
else
|
||||
rd_op_();
|
||||
});
|
||||
|
||||
wr_timer_.set_callback(ex_,
|
||||
[this]
|
||||
{
|
||||
if(wr_op_.empty())
|
||||
sock_.cancel();
|
||||
else
|
||||
wr_op_();
|
||||
});
|
||||
|
||||
cn_timer_.set_callback(ex_,
|
||||
[this]
|
||||
{
|
||||
if(cn_op_.empty())
|
||||
sock_.cancel();
|
||||
else
|
||||
cn_op_();
|
||||
});
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
~basic_timeout_socket()
|
||||
{
|
||||
rd_timer_.destroy();
|
||||
wr_timer_.destroy();
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
@ -200,6 +210,225 @@ async_write_some(
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class Handler>
|
||||
class connect_op
|
||||
{
|
||||
public:
|
||||
template<
|
||||
class Endpoints,
|
||||
class Condition,
|
||||
class DeducedHandler>
|
||||
connect_op(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
Endpoints const& eps,
|
||||
Condition cond,
|
||||
DeducedHandler&& h)
|
||||
: h_(std::forward<DeducedHandler>(h))
|
||||
, work_(s.cnd_timer_)
|
||||
, s_(s)
|
||||
, wg0_(s_.get_executor())
|
||||
, wg1_(get_executor())
|
||||
{
|
||||
boost::asio::async_connect(
|
||||
s_.next_layer(), eps, cond,
|
||||
std::move(*this));
|
||||
}
|
||||
|
||||
using allocator_type =
|
||||
boost::asio::associated_allocator_t<Handler>;
|
||||
|
||||
allocator_type
|
||||
get_allocator() const noexcept
|
||||
{
|
||||
return boost::asio::get_associated_allocator(h_);
|
||||
}
|
||||
|
||||
using executor_type =
|
||||
boost::asio::associated_executor_t<Handler,
|
||||
decltype(std::declval<basic_timeout_socket<
|
||||
Protocol, Executor>&>().get_executor())>;
|
||||
|
||||
executor_type
|
||||
get_executor() const noexcept
|
||||
{
|
||||
return boost::asio::get_associated_executor(
|
||||
h_, s_.get_executor());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(connect_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, connect_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(f, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
void
|
||||
operator()(error_code ec, Arg&& arg)
|
||||
{
|
||||
if(! work_.try_complete())
|
||||
{
|
||||
s_.cn_op_.emplace(
|
||||
std::move(*this),
|
||||
ec,
|
||||
std::forward<Arg>(arg));
|
||||
return;
|
||||
}
|
||||
h_(ec, std::forward<Arg>(arg));
|
||||
}
|
||||
|
||||
private:
|
||||
Handler h_;
|
||||
timeout_work_guard work_;
|
||||
basic_timeout_socket<Protocol, Executor>& s_;
|
||||
boost::asio::executor_work_guard<Executor> wg0_;
|
||||
boost::asio::executor_work_guard<executor_type> wg1_;
|
||||
};
|
||||
|
||||
struct any_endpoint
|
||||
{
|
||||
template<class Error, class Endpoint>
|
||||
bool
|
||||
operator()(Error const&, Endpoint const&) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Iterator>
|
||||
struct endpoint_range_type
|
||||
{
|
||||
using const_iterator = Iterator;
|
||||
|
||||
Iterator begin_;
|
||||
Iterator end_;
|
||||
|
||||
Iterator begin() const noexcept
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
|
||||
Iterator end() const noexcept
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Iterator>
|
||||
endpoint_range_type<Iterator>
|
||||
endpoint_range(Iterator first, Iterator last)
|
||||
{
|
||||
return endpoint_range_type<
|
||||
Iterator>(first, last);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class EndpointSequence,
|
||||
class RangeConnectHandler, class>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
|
||||
void(boost::system::error_code, typename Protocol::endpoint))
|
||||
async_connect(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
EndpointSequence const& endpoints,
|
||||
RangeConnectHandler&& handler)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
|
||||
void(boost::system::error_code, typename Protocol::endpoint));
|
||||
detail::connect_op<Protocol, Executor,
|
||||
BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler,
|
||||
void(boost::system::error_code,
|
||||
typename Protocol::endpoint))>(
|
||||
s, endpoints, detail::any_endpoint{},
|
||||
std::forward<RangeConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class EndpointSequence,
|
||||
class ConnectCondition,
|
||||
class RangeConnectHandler, class>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
|
||||
void (boost::system::error_code, typename Protocol::endpoint))
|
||||
async_connect(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
EndpointSequence const& endpoints,
|
||||
ConnectCondition connect_condition,
|
||||
RangeConnectHandler&& handler)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
|
||||
void(boost::system::error_code, typename Protocol::endpoint));
|
||||
detail::connect_op<Protocol, Executor,
|
||||
BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler,
|
||||
void(boost::system::error_code,
|
||||
typename Protocol::endpoint))>(
|
||||
s, endpoints, connect_condition,
|
||||
std::forward<RangeConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class Iterator,
|
||||
class IteratorConnectHandler, class>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
|
||||
void (boost::system::error_code, Iterator))
|
||||
async_connect(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
Iterator begin, Iterator end,
|
||||
IteratorConnectHandler&& handler)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
|
||||
void(boost::system::error_code, Iterator));
|
||||
detail::connect_op<Protocol, Executor,
|
||||
BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler,
|
||||
void(boost::system::error_code, Iterator))>(
|
||||
s, detail::endpoint_range(begin, end), detail::any_endpoint{},
|
||||
std::forward<IteratorConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class Iterator,
|
||||
class ConnectCondition,
|
||||
class IteratorConnectHandler, class>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
|
||||
void (boost::system::error_code, Iterator))
|
||||
async_connect(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
Iterator begin, Iterator end,
|
||||
ConnectCondition connect_condition,
|
||||
IteratorConnectHandler&& handler)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
|
||||
void(boost::system::error_code, Iterator));
|
||||
detail::connect_op<Protocol, Executor,
|
||||
BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler,
|
||||
void(boost::system::error_code, Iterator))>(
|
||||
s, detail::endpoint_range(begin, end), connect_condition,
|
||||
std::forward<IteratorConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
|
@ -10,13 +10,109 @@
|
||||
#ifndef BOOST_BEAST_CORE_TIMEOUT_SERVICE_HPP
|
||||
#define BOOST_BEAST_CORE_TIMEOUT_SERVICE_HPP
|
||||
|
||||
//#include <boost/asio/execution_context.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <chrono>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/asio/io_context.hpp> // #include <boost/asio/execution_context.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
#include <functional> // temporary
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
class timeout_service;
|
||||
} // detail
|
||||
|
||||
class timeout_work_guard;
|
||||
|
||||
class timeout_handle
|
||||
{
|
||||
std::size_t id_ = 0;
|
||||
detail::timeout_service* svc_ = nullptr;
|
||||
|
||||
timeout_handle(
|
||||
std::size_t id,
|
||||
detail::timeout_service& svc)
|
||||
: id_(id)
|
||||
, svc_(&svc)
|
||||
{
|
||||
}
|
||||
|
||||
detail::timeout_service&
|
||||
service() const
|
||||
{
|
||||
return *svc_;
|
||||
}
|
||||
|
||||
friend class detail::timeout_service;
|
||||
friend class timeout_work_guard;
|
||||
|
||||
public:
|
||||
timeout_handle() = default;
|
||||
timeout_handle(timeout_handle const&) = default;
|
||||
timeout_handle& operator=(timeout_handle const&) = default;
|
||||
|
||||
timeout_handle(std::nullptr_t)
|
||||
{
|
||||
}
|
||||
|
||||
timeout_handle&
|
||||
operator=(std::nullptr_t)
|
||||
{
|
||||
id_ = 0;
|
||||
svc_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// VFALCO should be execution_context
|
||||
BOOST_BEAST_DECL
|
||||
explicit
|
||||
timeout_handle(boost::asio::io_context& ioc);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
destroy();
|
||||
|
||||
template<class Executor, class CancelHandler>
|
||||
void
|
||||
set_callback(
|
||||
Executor const& ex, CancelHandler&& handler);
|
||||
|
||||
explicit
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return svc_ != nullptr;
|
||||
}
|
||||
|
||||
friend bool operator==(
|
||||
timeout_handle const& lhs,
|
||||
std::nullptr_t) noexcept
|
||||
{
|
||||
return lhs.svc_ == nullptr;
|
||||
}
|
||||
|
||||
friend bool operator==(
|
||||
std::nullptr_t,
|
||||
timeout_handle const& rhs) noexcept
|
||||
{
|
||||
return rhs.svc_ == nullptr;
|
||||
}
|
||||
|
||||
friend bool operator!=(
|
||||
timeout_handle const& lhs,
|
||||
std::nullptr_t) noexcept
|
||||
{
|
||||
return lhs.svc_ != nullptr;
|
||||
}
|
||||
|
||||
friend bool operator!=(
|
||||
std::nullptr_t,
|
||||
timeout_handle const& rhs) noexcept
|
||||
{
|
||||
return rhs.svc_ != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/** Set timeout service options in an execution context.
|
||||
|
||||
This changes the time interval for all timeouts associated
|
||||
@ -27,6 +123,7 @@ namespace beast {
|
||||
|
||||
@param interval The approximate amount of time until a timeout occurs.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
set_timeout_service_options(
|
||||
boost::asio::io_context& ctx, // VFALCO should be execution_context
|
||||
@ -35,6 +132,6 @@ set_timeout_service_options(
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/_experimental/core/impl/timeout_service.ipp>
|
||||
#include <boost/beast/_experimental/core/impl/timeout_service.hpp>
|
||||
|
||||
#endif
|
||||
|
@ -13,9 +13,11 @@
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/_experimental/core/detail/timeout_service.hpp>
|
||||
#include <boost/beast/_experimental/core/timeout_service.hpp>
|
||||
#include <boost/beast/_experimental/core/detail/saved_handler.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/basic_stream_socket.hpp>
|
||||
#include <boost/asio/connect.hpp>
|
||||
#include <boost/asio/executor.hpp>
|
||||
#include <chrono>
|
||||
|
||||
@ -30,6 +32,11 @@ class tcp;
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
template<class, class, class>
|
||||
class connect_op;
|
||||
} // detail
|
||||
|
||||
/** A socket wrapper which automatically times out on asynchronous reads.
|
||||
|
||||
This wraps a normal stream socket and implements a simple and efficient
|
||||
@ -44,22 +51,16 @@ template<
|
||||
class basic_timeout_socket
|
||||
{
|
||||
template<class> class async_op;
|
||||
|
||||
class timer : public detail::timeout_object
|
||||
{
|
||||
basic_timeout_socket& s_;
|
||||
|
||||
public:
|
||||
explicit timer(basic_timeout_socket& s);
|
||||
timer& operator=(timer&& other);
|
||||
void on_timeout() override;
|
||||
};
|
||||
template<class, class, class> friend class detail::connect_op;
|
||||
|
||||
Executor ex_; // must come first
|
||||
timer rd_timer_;
|
||||
timer wr_timer_;
|
||||
timeout_handle rd_timer_;
|
||||
timeout_handle wr_timer_;
|
||||
timeout_handle cn_timer_;
|
||||
boost::asio::basic_stream_socket<Protocol> sock_;
|
||||
bool expired_ = false;
|
||||
detail::saved_handler rd_op_;
|
||||
detail::saved_handler wr_op_;
|
||||
detail::saved_handler cn_op_;
|
||||
|
||||
public:
|
||||
/// The type of the next layer.
|
||||
@ -74,6 +75,13 @@ public:
|
||||
/// The type of the executor associated with the object.
|
||||
using executor_type = Executor;
|
||||
|
||||
/** Destructor
|
||||
|
||||
The behavior of destruction while asynchronous operations
|
||||
are pending is undefined.
|
||||
*/
|
||||
~basic_timeout_socket();
|
||||
|
||||
// VFALCO we only support default-construction
|
||||
// of the contained socket for now.
|
||||
// This constructor needs a protocol parameter.
|
||||
@ -227,11 +235,394 @@ public:
|
||||
WriteHandler&& handler);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// A TCP/IP socket wrapper which has a built-in asynchronous timeout
|
||||
using timeout_socket = basic_timeout_socket<
|
||||
boost::asio::ip::tcp,
|
||||
boost::asio::io_context::executor_type>;
|
||||
|
||||
/**
|
||||
@defgroup async_connect boost::beast::async_connect
|
||||
|
||||
@brief Asynchronously establishes a socket connection by trying each
|
||||
endpoint in a sequence, and terminating if a timeout occurs.
|
||||
*/
|
||||
/* @{ */
|
||||
/** Asynchronously establishes a socket connection by trying each endpoint in a sequence.
|
||||
|
||||
This function attempts to connect a socket to one of a sequence of
|
||||
endpoints. It does this by repeated calls to the underlying socket's
|
||||
@c async_connect member function, once for each endpoint in the sequence,
|
||||
until a connection is successfully established or a timeout occurs.
|
||||
|
||||
@param s The @ref basic_timeout_socket to be connected. If the socket
|
||||
is already open, it will be closed.
|
||||
|
||||
@param endpoints A sequence of endpoints.
|
||||
|
||||
@param handler The handler to be called when the connect operation
|
||||
completes. Ownership of the handler may be transferred. The function
|
||||
signature of the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
// Result of operation. if the sequence is empty, set to
|
||||
// boost::asio::error::not_found. Otherwise, contains the
|
||||
// error from the last connection attempt.
|
||||
error_code const& error,
|
||||
|
||||
// On success, the successfully connected endpoint.
|
||||
// Otherwise, a default-constructed endpoint.
|
||||
typename Protocol::endpoint const& endpoint
|
||||
);
|
||||
@endcode
|
||||
|
||||
Regardless of whether the asynchronous operation completes immediately or
|
||||
not, the handler will not be invoked from within this function. Invocation
|
||||
of the handler will be performed in a manner equivalent to using
|
||||
`boost::asio::io_context::post()`.
|
||||
|
||||
@par Example
|
||||
|
||||
@code
|
||||
boost::asio::tcp::resolver r(ioc);
|
||||
boost::asio::tcp::resolver::query q("host", "service");
|
||||
timeout_socket s(ioc.get_executor());
|
||||
|
||||
// ...
|
||||
|
||||
r.async_resolve(q, resolve_handler);
|
||||
|
||||
// ...
|
||||
|
||||
void resolve_handler(
|
||||
boost::system::error_code const& ec,
|
||||
tcp::resolver::results_type results)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
async_connect(s, results, connect_handler);
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
void connect_handler(
|
||||
boost::system::error_code const& ec,
|
||||
tcp::endpoint const& endpoint)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class EndpointSequence,
|
||||
class RangeConnectHandler
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
,class = typename std::enable_if<
|
||||
boost::asio::is_endpoint_sequence<
|
||||
EndpointSequence>::value>::type
|
||||
#endif
|
||||
>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
|
||||
void (boost::system::error_code, class Protocol::endpoint))
|
||||
async_connect(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
EndpointSequence const& endpoints,
|
||||
RangeConnectHandler&& handler);
|
||||
|
||||
/** Asynchronously establishes a socket connection by trying each endpoint in a sequence.
|
||||
|
||||
This function attempts to connect a socket to one of a sequence of
|
||||
endpoints. It does this by repeated calls to the underlying socket's
|
||||
@c async_connect member function, once for each endpoint in the sequence,
|
||||
until a connection is successfully established or a timeout occurs.
|
||||
|
||||
@param s The @ref basic_timeout_socket to be connected. If the socket
|
||||
is already open, it will be closed.
|
||||
|
||||
@param endpoints A sequence of endpoints.
|
||||
|
||||
@param connect_condition A function object that is called prior to each
|
||||
connection attempt. The signature of the function object must be:
|
||||
|
||||
@code
|
||||
bool connect_condition(
|
||||
boost::system::error_code const& ec,
|
||||
typename Protocol::endpoint const& next);
|
||||
@endcode
|
||||
|
||||
The @c ec parameter contains the result from the most recent connect
|
||||
operation. Before the first connection attempt, @c ec is always set to
|
||||
indicate success. The @c next parameter is the next endpoint to be tried.
|
||||
The function object should return true if the next endpoint should be tried,
|
||||
and false if it should be skipped.
|
||||
|
||||
@param handler The handler to be called when the connect operation
|
||||
completes. Ownership of the handler may be transferred. The function
|
||||
signature of the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
// Result of operation. if the sequence is empty, set to
|
||||
// boost::asio::error::not_found. Otherwise, contains the
|
||||
// error from the last connection attempt.
|
||||
error_code const& error,
|
||||
|
||||
// On success, the successfully connected endpoint.
|
||||
// Otherwise, a default-constructed endpoint.
|
||||
typename Protocol::endpoint const& endpoint
|
||||
);
|
||||
@endcode
|
||||
|
||||
Regardless of whether the asynchronous operation completes immediately or
|
||||
not, the handler will not be invoked from within this function. Invocation
|
||||
of the handler will be performed in a manner equivalent to using
|
||||
`boost::asio::io_context::post()`.
|
||||
|
||||
@par Example
|
||||
|
||||
The following connect condition function object can be used to output
|
||||
information about the individual connection attempts:
|
||||
|
||||
@code
|
||||
struct my_connect_condition
|
||||
{
|
||||
bool operator()(
|
||||
boost::system::error_code const& ec,
|
||||
boost::asio::ip::tcp::endpoint const& next)
|
||||
{
|
||||
if (ec) std::cout << "Error: " << ec.message() << std::endl;
|
||||
std::cout << "Trying: " << next << std::endl;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
It would be used with the @ref boost::beast::async_connect
|
||||
function as follows:
|
||||
|
||||
@code
|
||||
boost::asio::tcp::resolver r(ioc);
|
||||
boost::asio::tcp::resolver::query q("host", "service");
|
||||
timeout_socket s(ioc.get_executor());
|
||||
|
||||
// ...
|
||||
|
||||
r.async_resolve(q, resolve_handler);
|
||||
|
||||
// ...
|
||||
|
||||
void resolve_handler(
|
||||
boost::system::error_code const& ec,
|
||||
tcp::resolver::results_type results)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
async_connect(s, results, my_connect_condition{}, connect_handler);
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
void connect_handler(
|
||||
boost::system::error_code const& ec,
|
||||
tcp::endpoint const& endpoint)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class EndpointSequence,
|
||||
class ConnectCondition,
|
||||
class RangeConnectHandler
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
,class = typename std::enable_if<
|
||||
boost::asio::is_endpoint_sequence<
|
||||
EndpointSequence>::value>::type
|
||||
#endif
|
||||
>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
|
||||
void (boost::system::error_code, class Protocol::endpoint))
|
||||
async_connect(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
EndpointSequence const& endpoints,
|
||||
ConnectCondition connect_condition,
|
||||
RangeConnectHandler&& handler);
|
||||
|
||||
/** Asynchronously establishes a socket connection by trying each endpoint in a sequence.
|
||||
|
||||
This function attempts to connect a socket to one of a sequence of
|
||||
endpoints. It does this by repeated calls to the underlying socket's
|
||||
@c async_connect member function, once for each endpoint in the sequence,
|
||||
until a connection is successfully established or a timeout occurs.
|
||||
|
||||
@param s The @ref timeout_socket to be connected. If the socket
|
||||
is already open, it will be closed.
|
||||
|
||||
@param begin An iterator pointing to the start of a sequence of endpoints.
|
||||
|
||||
@param end An iterator pointing to the end of a sequence of endpoints.
|
||||
|
||||
@param handler The handler to be called when the connect operation
|
||||
completes. Ownership of the handler may be transferred. The function
|
||||
signature of the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
// Result of operation. if the sequence is empty, set to
|
||||
// boost::asio::error::not_found. Otherwise, contains the
|
||||
// error from the last connection attempt.
|
||||
error_code const& error,
|
||||
|
||||
// On success, an iterator denoting the successfully
|
||||
// connected endpoint. Otherwise, the end iterator.
|
||||
Iterator iterator
|
||||
);
|
||||
@endcode
|
||||
|
||||
Regardless of whether the asynchronous operation completes immediately or
|
||||
not, the handler will not be invoked from within this function. Invocation
|
||||
of the handler will be performed in a manner equivalent to using
|
||||
`boost::asio::io_context::post()`.
|
||||
|
||||
@par Example
|
||||
|
||||
@code
|
||||
std::vector<tcp::endpoint> endpoints = ...;
|
||||
timeout_socket s(ioc.get_executor());
|
||||
|
||||
async_connect(s,
|
||||
endpoints.begin(), endpoints.end(),
|
||||
connect_handler);
|
||||
|
||||
void connect_handler(
|
||||
boost::system::error_code const& ec,
|
||||
std::vector<tcp::endpoint>::iterator)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class Iterator,
|
||||
class IteratorConnectHandler
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
,class = typename std::enable_if<
|
||||
! boost::asio::is_endpoint_sequence<
|
||||
Iterator>::value>::type
|
||||
#endif
|
||||
>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
|
||||
void (boost::system::error_code, Iterator))
|
||||
async_connect(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
Iterator begin, Iterator end,
|
||||
IteratorConnectHandler&& handler);
|
||||
|
||||
/** Asynchronously establishes a socket connection by trying each endpoint in a sequence.
|
||||
|
||||
This function attempts to connect a socket to one of a sequence of
|
||||
endpoints. It does this by repeated calls to the underlying socket's
|
||||
@c async_connect member function, once for each endpoint in the sequence,
|
||||
until a connection is successfully established or a timeout occurs.
|
||||
|
||||
@param s The @ref basic_timeout_socket to be connected. If the socket
|
||||
is already open, it will be closed.
|
||||
|
||||
@param begin An iterator pointing to the start of a sequence of endpoints.
|
||||
|
||||
@param end An iterator pointing to the end of a sequence of endpoints.
|
||||
|
||||
@param connect_condition A function object that is called prior to each
|
||||
connection attempt. The signature of the function object must be:
|
||||
|
||||
@code
|
||||
bool connect_condition(
|
||||
boost::system::error_code const& ec,
|
||||
Iterator next);
|
||||
@endcode
|
||||
|
||||
@param handler The handler to be called when the connect operation
|
||||
completes. Ownership of the handler may be transferred. The function
|
||||
signature of the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
// Result of operation. if the sequence is empty, set to
|
||||
// boost::asio::error::not_found. Otherwise, contains the
|
||||
// error from the last connection attempt.
|
||||
error_code const& error,
|
||||
|
||||
// On success, an iterator denoting the successfully
|
||||
// connected endpoint. Otherwise, the end iterator.
|
||||
Iterator iterator
|
||||
);
|
||||
@endcode
|
||||
|
||||
Regardless of whether the asynchronous operation completes immediately or
|
||||
not, the handler will not be invoked from within this function. Invocation
|
||||
of the handler will be performed in a manner equivalent to using
|
||||
`boost::asio::io_context::post()`.
|
||||
|
||||
@par Example
|
||||
|
||||
The following connect condition function object can be used to output
|
||||
information about the individual connection attempts:
|
||||
|
||||
@code
|
||||
struct my_connect_condition
|
||||
{
|
||||
bool operator()(
|
||||
boost::system::error_code const& ec,
|
||||
boost::asio::ip::tcp::endpoint const& next)
|
||||
{
|
||||
if (ec) std::cout << "Error: " << ec.message() << std::endl;
|
||||
std::cout << "Trying: " << next << std::endl;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
It would be used with the @ref boost::beast::async_connect
|
||||
function as follows:
|
||||
|
||||
@code
|
||||
std::vector<tcp::endpoint> endpoints = ...;
|
||||
timeout_socket s(ioc.get_executor());
|
||||
|
||||
async_connect(s, endpoints.begin(), endpoints.end(),
|
||||
my_connect_condition{}, connect_handler);
|
||||
|
||||
void connect_handler(
|
||||
boost::system::error_code const& ec,
|
||||
std::vector<tcp::endpoint>::iterator)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class Iterator,
|
||||
class ConnectCondition,
|
||||
class IteratorConnectHandler
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
,class = typename std::enable_if<
|
||||
! boost::asio::is_endpoint_sequence<
|
||||
Iterator>::value>::type
|
||||
#endif
|
||||
>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
|
||||
void (boost::system::error_code, Iterator))
|
||||
async_connect(
|
||||
basic_timeout_socket<Protocol, Executor>& s,
|
||||
Iterator begin, Iterator end,
|
||||
ConnectCondition connect_condition,
|
||||
IteratorConnectHandler&& handler);
|
||||
/* @} */
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
|
@ -7,24 +7,21 @@
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_DETAIL_TIMEOUT_WORK_GUARD_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_TIMEOUT_WORK_GUARD_HPP
|
||||
#ifndef BOOST_BEAST_CORE_TIMEOUT_WORK_GUARD_HPP
|
||||
#define BOOST_BEAST_CORE_TIMEOUT_WORK_GUARD_HPP
|
||||
|
||||
#include <boost/beast/_experimental/core/detail/timeout_service.hpp>
|
||||
#include <boost/beast/_experimental/core/timeout_service.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
class timeout_work_guard
|
||||
{
|
||||
timeout_object* obj_;
|
||||
timeout_handle h_;
|
||||
|
||||
public:
|
||||
timeout_work_guard(timeout_work_guard const&) = delete;
|
||||
timeout_work_guard& operator=(timeout_work_guard&&) = delete;
|
||||
timeout_work_guard& operator=(timeout_work_guard const&) = delete;
|
||||
|
||||
~timeout_work_guard()
|
||||
@ -33,40 +30,42 @@ public:
|
||||
}
|
||||
|
||||
timeout_work_guard(timeout_work_guard&& other)
|
||||
: obj_(boost::exchange(other.obj_, nullptr))
|
||||
: h_(other.h_)
|
||||
{
|
||||
other.h_ = nullptr;
|
||||
}
|
||||
|
||||
explicit
|
||||
timeout_work_guard(timeout_object& obj)
|
||||
: obj_(&obj)
|
||||
timeout_work_guard(timeout_handle h)
|
||||
: h_(h)
|
||||
{
|
||||
obj_->service().on_work_started(*obj_);
|
||||
h_.service().on_work_started(h_);
|
||||
}
|
||||
|
||||
bool
|
||||
owns_work() const
|
||||
{
|
||||
return obj_ != nullptr;
|
||||
return h_ != nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
reset()
|
||||
{
|
||||
if(obj_)
|
||||
obj_->service().on_work_stopped(*obj_);
|
||||
if(h_)
|
||||
h_.service().on_work_stopped(h_);
|
||||
}
|
||||
|
||||
void
|
||||
complete()
|
||||
bool
|
||||
try_complete()
|
||||
{
|
||||
BOOST_ASSERT(obj_ != nullptr);
|
||||
obj_->service().on_work_complete(*obj_);
|
||||
obj_ = nullptr;
|
||||
BOOST_ASSERT(h_ != nullptr);
|
||||
auto result =
|
||||
h_.service().on_try_work_complete(h_);
|
||||
h_ = nullptr;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
@ -60,4 +60,6 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define BOOST_BEAST_DECL inline
|
||||
|
||||
#endif
|
||||
|
@ -137,7 +137,10 @@ public:
|
||||
std::make_shared<session>(
|
||||
std::move(socket_), log_)->run();
|
||||
acceptor_.async_accept(socket_,
|
||||
[this](error_code ec){ this->on_accept(ec); });
|
||||
[this](error_code ec)
|
||||
{
|
||||
this->on_accept(ec);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,4 +8,4 @@
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/beast/_experimental/core/detail/timeout_work_guard.hpp>
|
||||
#include <boost/beast/_experimental/core/timeout_work_guard.hpp>
|
||||
|
Reference in New Issue
Block a user