mirror of
https://github.com/boostorg/beast.git
synced 2025-07-29 20:37:31 +02:00
beast support per-op cancellation
- websocket supports cancellation. - Iterating test for ws cancellation. - Only terminal cancellation is forwarded by default. - basic_stream supports cancellation. - supported cancellation is documented. - http cancellation additions. - Added cancellation_slot tests to http, utils and saved_handler. - Added post to write.cpp, to avoid SIGSEV in test. - Refresher describes cancellation in more detail.
This commit is contained in:
committed by
Klemens Morgenstern
parent
0bf3d971a0
commit
3ebff60b1a
@ -1,3 +1,9 @@
|
||||
Version 342:
|
||||
|
||||
* Support per operation cancellation
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Version 341:
|
||||
|
||||
* Expect header field with the "100-continue" is handled in upgrade.
|
||||
@ -19,7 +25,6 @@ Version 339:
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
Version 338:
|
||||
|
||||
* Added per message compression options.
|
||||
|
@ -199,6 +199,10 @@ has both an
|
||||
[@boost:/doc/html/boost_asio/overview/core/allocation.html ['associated allocator]]
|
||||
returned by
|
||||
[@boost:/doc/html/boost_asio/reference/get_associated_allocator.html `net::get_associated_allocator`],
|
||||
, an
|
||||
[@boost:/doc/html/boost_asio/reference/associated_cancellation_slot.html ['associated cancellation slot]]
|
||||
returned by
|
||||
[@boost:/doc/html/boost_asio/reference/associated_cancellation_slot.html `net::get_associated_cancellation_slot`].
|
||||
and an
|
||||
[@boost:/doc/html/boost_asio/reference/associated_executor.html ['associated executor]]
|
||||
returned by
|
||||
@ -210,6 +214,8 @@ These associations may be specified intrusively:
|
||||
Or these associations may be specified non-intrusively, by specializing
|
||||
the class templates
|
||||
[@boost:/doc/html/boost_asio/reference/associated_allocator.html `net::associated_allocator`]
|
||||
,
|
||||
[@boost:/doc/html/boost_asio/reference/associated_cancellation_slot.html `net::associated_cancellation_slot`]
|
||||
and
|
||||
[@boost:/doc/html/boost_asio/reference/associated_executor.html `net::associated_executor`]:
|
||||
|
||||
@ -227,6 +233,36 @@ object providing the algorithm used to invoke the completion handler. Unless
|
||||
customized by the caller, a completion handler defaults to using
|
||||
`std::allocator<void>` and the executor of the corresponding I/O object.
|
||||
|
||||
The function
|
||||
[@boost:/doc/html/boost_asio/reference/bind_allocator.html `net::bind_allocator`]
|
||||
can be used whent he caller wants to assign a custom allocator to the operation.
|
||||
|
||||
|
||||
A completion token's associated cancellation_slot can be used to cancel single
|
||||
operations. This is often passed through by the completion token such as
|
||||
[@boost:/doc/html/boost_asio/reference/use_awaitable.html `net::use_awaitable`]
|
||||
or
|
||||
[@boost:/doc/html/boost_asio/reference/yield_context.html `net::yield_context`]
|
||||
.
|
||||
|
||||
The available [@boost:/doc/html/boost_asio/reference/cancellation_type.html cancellation types] are listed below.
|
||||
|
||||
# `terminal`
|
||||
Requests cancellation where, following a successful cancellation,
|
||||
the only safe operations on the I/O object are closure or destruction.
|
||||
|
||||
# `partial`
|
||||
Requests cancellation where a successful cancellation may result in partial
|
||||
side effects or no side effects. Following cancellation,
|
||||
the I/O object is in a well-known state, and may be used for further operations.
|
||||
|
||||
# `total`
|
||||
Requests cancellation where a successful cancellation results in no apparent side effects.
|
||||
Following cancellation, the I/O object is in the same observable state as it was prior to the operation.
|
||||
|
||||
|
||||
|
||||
|
||||
Networking prescribes facilities to determine the context in which
|
||||
handlers run. Every I/O object refers to an __ExecutionContext__ for
|
||||
obtaining the __Executor__ instance used to invoke completion handlers.
|
||||
|
@ -77,6 +77,16 @@ class basic_stream<Executor>::read_op : public detail::stream_read_op_base
|
||||
return net::get_associated_allocator(h_);
|
||||
}
|
||||
|
||||
using cancellation_slot_type =
|
||||
net::associated_cancellation_slot_t<Handler>;
|
||||
|
||||
cancellation_slot_type
|
||||
get_cancellation_slot() const noexcept
|
||||
{
|
||||
return net::get_associated_cancellation_slot(h_,
|
||||
net::cancellation_slot());
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec)
|
||||
{
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/detail/allocator.hpp>
|
||||
#include <boost/beast/core/detail/async_base.hpp>
|
||||
#include <boost/beast/core/detail/filtering_cancellation_slot.hpp>
|
||||
#include <boost/beast/core/detail/work_guard.hpp>
|
||||
#include <boost/asio/associated_allocator.hpp>
|
||||
#include <boost/asio/associated_executor.hpp>
|
||||
@ -187,7 +188,7 @@ class async_base
|
||||
|
||||
Handler h_;
|
||||
detail::select_work_guard_t<Executor1> wg1_;
|
||||
|
||||
net::cancellation_type act_{net::cancellation_type::terminal};
|
||||
public:
|
||||
/** The type of executor associated with this object.
|
||||
|
||||
@ -308,6 +309,38 @@ public:
|
||||
h_, wg1_.get_executor());
|
||||
}
|
||||
|
||||
/** The type of cancellation_slot associated with this object.
|
||||
|
||||
If a class derived from @ref async_base is a completion
|
||||
handler, then the associated cancellation_slot of the
|
||||
derived class will be this type.
|
||||
|
||||
The default type is a filtering cancellation slot,
|
||||
that only allows terminal cancellation.
|
||||
*/
|
||||
using cancellation_slot_type =
|
||||
beast::detail::filtering_cancellation_slot<net::associated_cancellation_slot_t<Handler>>;
|
||||
|
||||
/** Returns the cancellation_slot associated with this object.
|
||||
|
||||
If a class derived from @ref async_base is a completion
|
||||
handler, then the object returned from this function will be used
|
||||
as the associated cancellation_slot of the derived class.
|
||||
*/
|
||||
cancellation_slot_type
|
||||
get_cancellation_slot() const noexcept
|
||||
{
|
||||
return cancellation_slot_type(act_, net::get_associated_cancellation_slot(h_,
|
||||
net::cancellation_slot()));
|
||||
}
|
||||
|
||||
/// Set the allowed cancellation types, default is `terminal`.
|
||||
void set_allowed_cancellation(
|
||||
net::cancellation_type allowed_cancellation_types = net::cancellation_type::terminal)
|
||||
{
|
||||
act_ = allowed_cancellation_types;
|
||||
}
|
||||
|
||||
/// Returns the handler associated with this object
|
||||
Handler const&
|
||||
handler() const noexcept
|
||||
|
@ -919,6 +919,17 @@ public:
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_connect operation.
|
||||
|
||||
@see async_connect
|
||||
*/
|
||||
template<
|
||||
@ -972,6 +983,18 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_connect operation.
|
||||
|
||||
*/
|
||||
template<
|
||||
class EndpointSequence,
|
||||
@ -1064,6 +1087,17 @@ public:
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_connect operation.
|
||||
*/
|
||||
template<
|
||||
class EndpointSequence,
|
||||
@ -1129,6 +1163,17 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_connect operation.
|
||||
*/
|
||||
template<
|
||||
class Iterator,
|
||||
@ -1189,6 +1234,17 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_connect operation.
|
||||
*/
|
||||
template<
|
||||
class Iterator,
|
||||
@ -1316,6 +1372,17 @@ public:
|
||||
number of bytes. Consider using the function `net::async_read` if you need
|
||||
to ensure that the requested amount of data is read before the asynchronous
|
||||
operation completes.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_read_some operation.
|
||||
*/
|
||||
template<
|
||||
class MutableBufferSequence,
|
||||
@ -1439,6 +1506,17 @@ public:
|
||||
number of bytes. Consider using the function `net::async_write` if you need
|
||||
to ensure that the requested amount of data is sent before the asynchronous
|
||||
operation completes.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_write_some operation.
|
||||
*/
|
||||
template<
|
||||
class ConstBufferSequence,
|
||||
|
@ -67,6 +67,16 @@ public:
|
||||
h_, this->get());
|
||||
}
|
||||
|
||||
using cancellation_slot_type =
|
||||
net::associated_cancellation_slot_t<Handler>;
|
||||
|
||||
cancellation_slot_type
|
||||
get_cancellation_slot() const noexcept
|
||||
{
|
||||
return net::get_associated_cancellation_slot(h_,
|
||||
net::cancellation_slot());
|
||||
}
|
||||
|
||||
// The allocation hooks are still defined because they trivially forward to
|
||||
// user hooks. Forward here ensures that the user will get a compile error
|
||||
// if they build their code with BOOST_ASIO_NO_DEPRECATED.
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/detail/tuple.hpp>
|
||||
#include <boost/asio/associated_allocator.hpp>
|
||||
#include <boost/asio/associated_cancellation_slot.hpp>
|
||||
#include <boost/asio/associated_executor.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
@ -48,6 +49,9 @@ class bind_wrapper
|
||||
template<class T, class Allocator>
|
||||
friend struct net::associated_allocator;
|
||||
|
||||
template<class T, class CancellationSlot>
|
||||
friend struct net::associated_cancellation_slot;
|
||||
|
||||
template<class Arg, class Vals>
|
||||
static
|
||||
typename std::enable_if<
|
||||
@ -217,6 +221,10 @@ class bind_front_wrapper
|
||||
template<class T, class Allocator>
|
||||
friend struct net::associated_allocator;
|
||||
|
||||
template<class T, class CancellationSlot>
|
||||
friend struct net::associated_cancellation_slot;
|
||||
|
||||
|
||||
template<std::size_t... I, class... Ts>
|
||||
void
|
||||
invoke(
|
||||
@ -385,6 +393,42 @@ struct associated_allocator<
|
||||
}
|
||||
};
|
||||
|
||||
template<class Handler, class... Args, class CancellationSlot>
|
||||
struct associated_cancellation_slot<
|
||||
beast::detail::bind_wrapper<Handler, Args...>, CancellationSlot>
|
||||
{
|
||||
using type = typename
|
||||
associated_cancellation_slot<Handler>::type;
|
||||
|
||||
static
|
||||
type
|
||||
get(beast::detail::bind_wrapper<Handler, Args...> const& op,
|
||||
CancellationSlot const& slot = CancellationSlot{}) noexcept
|
||||
{
|
||||
return associated_cancellation_slot<
|
||||
Handler, CancellationSlot>::get(op.h_, slot);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Handler, class... Args, class CancellationSlot>
|
||||
struct associated_cancellation_slot<
|
||||
beast::detail::bind_front_wrapper<Handler, Args...>, CancellationSlot>
|
||||
{
|
||||
using type = typename
|
||||
associated_cancellation_slot<Handler>::type;
|
||||
|
||||
static
|
||||
type
|
||||
get(beast::detail::bind_front_wrapper<Handler, Args...> const& op,
|
||||
CancellationSlot const& slot = CancellationSlot{}) noexcept
|
||||
{
|
||||
return associated_cancellation_slot<
|
||||
Handler, CancellationSlot>::get(op.h_, slot);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // asio
|
||||
} // boost
|
||||
|
||||
|
@ -0,0 +1,65 @@
|
||||
//
|
||||
// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
|
||||
//
|
||||
// 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 BOOST_BEAST_CORE_DETAIL_FILTERING_CANCELLATION_SLOT_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_FILTERING_CANCELLATION_SLOT_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/asio/cancellation_signal.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<typename CancellationSlot = net::cancellation_slot>
|
||||
struct filtering_cancellation_slot : CancellationSlot
|
||||
{
|
||||
template<typename ... Args>
|
||||
filtering_cancellation_slot(net::cancellation_type type, Args && ... args)
|
||||
: CancellationSlot(std::forward<Args>(args)...), type(type) {}
|
||||
|
||||
net::cancellation_type type = net::cancellation_type::terminal;
|
||||
|
||||
using CancellationSlot::operator=;
|
||||
|
||||
template<typename Handler>
|
||||
struct handler_wrapper
|
||||
{
|
||||
Handler handler;
|
||||
const net::cancellation_type type;
|
||||
|
||||
template<typename ... Args>
|
||||
handler_wrapper(net::cancellation_type type, Args && ... args)
|
||||
: handler(std::forward<Args>(args)...),
|
||||
type(type) {}
|
||||
|
||||
void operator()(net::cancellation_type tp)
|
||||
{
|
||||
if ((tp & type) != net::cancellation_type::none)
|
||||
handler(tp);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename CancellationHandler, typename ... Args>
|
||||
CancellationHandler& emplace(Args && ... args)
|
||||
{
|
||||
return CancellationSlot::template emplace<handler_wrapper<CancellationHandler>>(
|
||||
type, std::forward<Args>(args)...).handler;
|
||||
}
|
||||
|
||||
template <typename CancellationHandler>
|
||||
CancellationHandler& assign(CancellationHandler && ch)
|
||||
{
|
||||
return CancellationSlot::template emplace<handler_wrapper<CancellationHandler>>(
|
||||
type, std::forward<CancellationHandler>(ch)).handler;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //BOOST_BEAST_CORE_DETAIL_FILTERING_CANCELLATION_SLOT_HPP
|
@ -283,6 +283,7 @@ public:
|
||||
, pg_()
|
||||
, b_(b)
|
||||
{
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
if (buffer_bytes(b_) == 0 && state().pending)
|
||||
{
|
||||
// Workaround:
|
||||
@ -452,6 +453,7 @@ public:
|
||||
, pg0_(impl_->read.pending)
|
||||
, pg1_(impl_->write.pending)
|
||||
{
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
if(state().timer.expiry() != stream_base::never())
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
@ -489,6 +491,7 @@ public:
|
||||
, pg0_(impl_->read.pending)
|
||||
, pg1_(impl_->write.pending)
|
||||
{
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
if(state().timer.expiry() != stream_base::never())
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
@ -526,6 +529,7 @@ public:
|
||||
, pg0_(impl_->read.pending)
|
||||
, pg1_(impl_->write.pending)
|
||||
{
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
if(state().timer.expiry() != stream_base::never())
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
|
@ -12,7 +12,9 @@
|
||||
|
||||
#include <boost/beast/core/detail/allocator.hpp>
|
||||
#include <boost/asio/associated_allocator.hpp>
|
||||
#include <boost/asio/associated_cancellation_slot.hpp>
|
||||
#include <boost/asio/associated_executor.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/executor_work_guard.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/empty_value.hpp>
|
||||
@ -28,9 +30,10 @@ class saved_handler::base
|
||||
{
|
||||
protected:
|
||||
~base() = default;
|
||||
|
||||
saved_handler * owner_;
|
||||
public:
|
||||
base() = default;
|
||||
base(saved_handler * owner) : owner_(owner){}
|
||||
void set_owner(saved_handler * new_owner) { owner_ = new_owner;}
|
||||
virtual void destroy() = 0;
|
||||
virtual void invoke() = 0;
|
||||
};
|
||||
@ -72,11 +75,12 @@ class saved_handler::impl final : public base
|
||||
net::executor_work_guard<
|
||||
net::associated_executor_t<Handler>> wg2_;
|
||||
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
|
||||
|
||||
net::cancellation_slot slot_{net::get_associated_cancellation_slot(v_.h)};
|
||||
public:
|
||||
template<class Handler_>
|
||||
impl(alloc_type const& a, Handler_&& h)
|
||||
: v_(a, std::forward<Handler_>(h))
|
||||
impl(alloc_type const& a, Handler_&& h,
|
||||
saved_handler * owner)
|
||||
: base(owner), v_(a, std::forward<Handler_>(h))
|
||||
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
|
||||
, wg2_(net::prefer(
|
||||
net::get_associated_executor(v_.h),
|
||||
@ -87,10 +91,15 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~impl()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
destroy() override
|
||||
{
|
||||
auto v = std::move(v_);
|
||||
slot_.clear();
|
||||
alloc_traits::destroy(v.get(), this);
|
||||
alloc_traits::deallocate(v.get(), this, 1);
|
||||
}
|
||||
@ -98,11 +107,22 @@ public:
|
||||
void
|
||||
invoke() override
|
||||
{
|
||||
slot_.clear();
|
||||
auto v = std::move(v_);
|
||||
alloc_traits::destroy(v.get(), this);
|
||||
alloc_traits::deallocate(v.get(), this, 1);
|
||||
v.h();
|
||||
}
|
||||
|
||||
void self_complete()
|
||||
{
|
||||
slot_.clear();
|
||||
owner_->p_ = nullptr;
|
||||
auto v = std::move(v_);
|
||||
alloc_traits::destroy(v.get(), this);
|
||||
alloc_traits::deallocate(v.get(), this, 1);
|
||||
v.h(net::error::operation_aborted);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -110,7 +130,8 @@ public:
|
||||
template<class Handler, class Allocator>
|
||||
void
|
||||
saved_handler::
|
||||
emplace(Handler&& handler, Allocator const& alloc)
|
||||
emplace(Handler&& handler, Allocator const& alloc,
|
||||
net::cancellation_type cancel_type)
|
||||
{
|
||||
// Can't delete a handler before invoking
|
||||
BOOST_ASSERT(! has_value());
|
||||
@ -140,22 +161,47 @@ emplace(Handler&& handler, Allocator const& alloc)
|
||||
alloc_traits::deallocate(a, p, 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
auto cancel_slot = net::get_associated_cancellation_slot(handler);
|
||||
storage s(alloc);
|
||||
alloc_traits::construct(s.a, s.p,
|
||||
s.a, std::forward<Handler>(handler));
|
||||
p_ = boost::exchange(s.p, nullptr);
|
||||
s.a, std::forward<Handler>(handler), this);
|
||||
|
||||
auto tmp = boost::exchange(s.p, nullptr);
|
||||
p_ = tmp;
|
||||
|
||||
if (cancel_slot.is_connected())
|
||||
{
|
||||
struct cancel_op
|
||||
{
|
||||
impl<Handler, Allocator>* p;
|
||||
net::cancellation_type accepted_ct;
|
||||
cancel_op(impl<Handler, Allocator>* p,
|
||||
net::cancellation_type accepted_ct)
|
||||
: p(p), accepted_ct(accepted_ct) {}
|
||||
|
||||
void operator()(net::cancellation_type ct)
|
||||
{
|
||||
if ((ct & accepted_ct) != net::cancellation_type::none)
|
||||
p->self_complete();
|
||||
}
|
||||
};
|
||||
cancel_slot.template emplace<cancel_op>(tmp, cancel_type);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Handler>
|
||||
void
|
||||
saved_handler::
|
||||
emplace(Handler&& handler)
|
||||
emplace(Handler&& handler, net::cancellation_type cancel_type)
|
||||
{
|
||||
// Can't delete a handler before invoking
|
||||
BOOST_ASSERT(! has_value());
|
||||
emplace(
|
||||
std::forward<Handler>(handler),
|
||||
net::get_associated_allocator(handler));
|
||||
net::get_associated_allocator(handler),
|
||||
cancel_type);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
@ -27,6 +27,7 @@ saved_handler::
|
||||
saved_handler(saved_handler&& other) noexcept
|
||||
: p_(boost::exchange(other.p_, nullptr))
|
||||
{
|
||||
p_->set_owner(this);
|
||||
}
|
||||
|
||||
saved_handler&
|
||||
@ -36,6 +37,7 @@ operator=(saved_handler&& other) noexcept
|
||||
// Can't delete a handler before invoking
|
||||
BOOST_ASSERT(! has_value());
|
||||
p_ = boost::exchange(other.p_, nullptr);
|
||||
p_->set_owner(this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#define BOOST_BEAST_CORE_SAVED_HANDLER_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/asio/cancellation_type.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
@ -72,10 +73,13 @@ public:
|
||||
The implementation takes ownership of the handler by performing a decay-copy.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
|
||||
@param cancel_type The type of cancellation allowed to complete this op.
|
||||
*/
|
||||
template<class Handler, class Allocator>
|
||||
void
|
||||
emplace(Handler&& handler, Allocator const& alloc);
|
||||
emplace(Handler&& handler, Allocator const& alloc,
|
||||
net::cancellation_type cancel_type = net::cancellation_type::terminal);
|
||||
|
||||
/** Store a completion handler in the container.
|
||||
|
||||
@ -85,10 +89,13 @@ public:
|
||||
|
||||
@param handler The completion handler to store.
|
||||
The implementation takes ownership of the handler by performing a decay-copy.
|
||||
|
||||
@param cancel_type The type of cancellation allowed to complete this op.
|
||||
*/
|
||||
template<class Handler>
|
||||
void
|
||||
emplace(Handler&& handler);
|
||||
emplace(Handler&& handler,
|
||||
net::cancellation_type cancel_type = net::cancellation_type::terminal);
|
||||
|
||||
/** Discard the saved handler, if one exists.
|
||||
|
||||
|
@ -178,8 +178,18 @@ class write_op
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields>& sr_;
|
||||
std::size_t bytes_transferred_ = 0;
|
||||
net::cancellation_state st_{this->
|
||||
beast::async_base<Handler, beast::executor_type<Stream>>
|
||||
::get_cancellation_slot()};
|
||||
|
||||
public:
|
||||
using cancellation_slot_type = net::cancellation_slot;
|
||||
cancellation_slot_type get_cancellation_slot() const noexcept
|
||||
{
|
||||
return st_.slot();
|
||||
}
|
||||
|
||||
|
||||
template<class Handler_>
|
||||
write_op(
|
||||
Handler_&& h,
|
||||
@ -227,6 +237,8 @@ public:
|
||||
s_, sr_, std::move(*this));
|
||||
}
|
||||
bytes_transferred_ += bytes_transferred;
|
||||
if (!ec && st_.cancelled() != net::cancellation_type::none)
|
||||
ec = net::error::operation_aborted;
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(Predicate{}(sr_))
|
||||
|
@ -200,6 +200,19 @@ read_some(
|
||||
@note The completion handler will receive as a parameter the total number
|
||||
of bytes transferred from the stream. This may be zero for the case where
|
||||
there is sufficient pre-existing message data in the dynamic buffer.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
@ -397,6 +410,19 @@ read_header(
|
||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||
implementation will call @ref basic_parser::eager with the value `false`
|
||||
on the parser passed in.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
@ -594,6 +620,19 @@ read(
|
||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||
implementation will call @ref basic_parser::eager with the value `true`
|
||||
on the parser passed in.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
@ -800,6 +839,19 @@ read(
|
||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||
implementation will call @ref basic_parser::eager with the value `true`
|
||||
on the parser passed in.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
|
@ -161,6 +161,18 @@ write_some(
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
@ -291,6 +303,18 @@ write_header(
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
@ -412,6 +436,18 @@ write(
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
@ -637,6 +673,18 @@ write(
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see message
|
||||
*/
|
||||
template<
|
||||
@ -699,6 +747,18 @@ async_write(
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see message
|
||||
*/
|
||||
template<
|
||||
|
@ -266,6 +266,9 @@ public:
|
||||
|
||||
// read and respond to an upgrade request
|
||||
//
|
||||
// Cancellation: the async_accept cancellation can be terminal
|
||||
// because it will just interrupt the reading of the header.
|
||||
//
|
||||
template<class NextLayer, bool deflateSupported>
|
||||
template<class Handler, class Decorator>
|
||||
class stream<NextLayer, deflateSupported>::accept_op
|
||||
|
@ -90,9 +90,15 @@ public:
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"websocket::async_close"));
|
||||
|
||||
impl.op_close.emplace(std::move(*this));
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
impl.op_close.emplace(std::move(*this),
|
||||
net::cancellation_type::all);
|
||||
}
|
||||
// cancel fired before we could do anything.
|
||||
if (ec == net::error::operation_aborted)
|
||||
return this->complete(cont, ec);
|
||||
this->set_allowed_cancellation(net::cancellation_type::terminal);
|
||||
|
||||
impl.wr_block.lock(this);
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
@ -143,9 +149,17 @@ public:
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"websocket::async_close"));
|
||||
|
||||
// terminal only, that's the default
|
||||
impl.op_r_close.emplace(std::move(*this));
|
||||
}
|
||||
if (ec == net::error::operation_aborted)
|
||||
{
|
||||
// if a cancellation fires here, we do a dirty shutdown
|
||||
impl.change_status(status::closed);
|
||||
close_socket(get_lowest_layer(impl.stream()));
|
||||
return this->complete(cont, ec);
|
||||
}
|
||||
|
||||
impl.rd_block.lock(this);
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
@ -185,7 +199,7 @@ public:
|
||||
beast::detail::bind_continuation(std::move(*this)));
|
||||
}
|
||||
impl.rd_buf.commit(bytes_transferred);
|
||||
if(impl.check_stop_now(ec))
|
||||
if(impl.check_stop_now(ec)) //< this catches cancellation
|
||||
goto upcall;
|
||||
}
|
||||
if(detail::is_control(impl.rd_fh.op))
|
||||
|
@ -86,9 +86,12 @@ public:
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"websocket::async_ping"));
|
||||
|
||||
impl.op_ping.emplace(std::move(*this));
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
impl.op_ping.emplace(std::move(*this), net::cancellation_type::all);
|
||||
}
|
||||
if (ec)
|
||||
return this->complete(cont, ec);
|
||||
this->set_allowed_cancellation(net::cancellation_type::terminal);
|
||||
impl.wr_block.lock(this);
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
|
@ -107,8 +107,14 @@ public:
|
||||
__FILE__, __LINE__,
|
||||
"websocket::async_read_some"));
|
||||
|
||||
impl.op_r_rd.emplace(std::move(*this));
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
impl.op_r_rd.emplace(std::move(*this), net::cancellation_type::all);
|
||||
}
|
||||
if (ec)
|
||||
return this->complete(cont, ec, bytes_written_);
|
||||
|
||||
this->set_allowed_cancellation(net::cancellation_type::terminal);
|
||||
|
||||
impl.rd_block.lock(this);
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
@ -275,6 +281,9 @@ public:
|
||||
|
||||
impl.op_rd.emplace(std::move(*this));
|
||||
}
|
||||
if (ec)
|
||||
return this->complete(cont, ec, bytes_written_);
|
||||
|
||||
impl.wr_block.lock(this);
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
@ -629,6 +638,9 @@ public:
|
||||
|
||||
impl.op_rd.emplace(std::move(*this));
|
||||
}
|
||||
if (ec)
|
||||
return this->complete(cont, ec, bytes_written_);
|
||||
|
||||
impl.wr_block.lock(this);
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
|
@ -66,6 +66,8 @@ struct ssl_shutdown_op
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
self.reset_cancellation_state(net::enable_total_cancellation());
|
||||
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
s_.async_shutdown(std::move(self));
|
||||
ec_ = ec;
|
||||
|
@ -59,6 +59,7 @@ public:
|
||||
, nb_(false)
|
||||
{
|
||||
(*this)({}, 0, false);
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -181,9 +181,14 @@ operator()(
|
||||
"websocket::async_write" :
|
||||
"websocket::async_write_some"
|
||||
));
|
||||
|
||||
impl.op_wr.emplace(std::move(*this));
|
||||
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||
impl.op_wr.emplace(std::move(*this),
|
||||
net::cancellation_type::all);
|
||||
}
|
||||
if (ec)
|
||||
return this->complete(cont, ec, bytes_transferred_);
|
||||
|
||||
this->set_allowed_cancellation(net::cancellation_type::terminal);
|
||||
impl.wr_block.lock(this);
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
|
@ -68,6 +68,18 @@ teardown(
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_teardown
|
||||
and @c async_shutdown operation.
|
||||
|
||||
*/
|
||||
template<class AsyncStream, class TeardownHandler>
|
||||
void
|
||||
|
@ -1602,6 +1602,20 @@ public:
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
@note `terminal` cancellation will may close the underlying socket.
|
||||
|
||||
@see
|
||||
@li <a href="https://tools.ietf.org/html/rfc6455#section-7.1.2">Websocket Closing Handshake (RFC6455)</a>
|
||||
*/
|
||||
@ -1706,6 +1720,21 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
*/
|
||||
template<
|
||||
BOOST_BEAST_ASYNC_TPARAM1 WriteHandler =
|
||||
@ -1814,6 +1843,21 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
*/
|
||||
template<
|
||||
BOOST_BEAST_ASYNC_TPARAM1 WriteHandler =
|
||||
@ -1975,6 +2019,21 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
*/
|
||||
template<
|
||||
class DynamicBuffer,
|
||||
@ -2154,6 +2213,21 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
*/
|
||||
template<
|
||||
class DynamicBuffer,
|
||||
@ -2258,6 +2332,21 @@ public:
|
||||
from the beginning.
|
||||
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
*/
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
@ -2329,6 +2418,22 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
*/
|
||||
template<
|
||||
class MutableBufferSequence,
|
||||
@ -2452,6 +2557,21 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
*/
|
||||
template<
|
||||
class ConstBufferSequence,
|
||||
@ -2490,6 +2610,7 @@ public:
|
||||
@return The number of bytes sent from the buffers.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
@ -2575,6 +2696,21 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||
control operations such as a ping/pong.
|
||||
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
*/
|
||||
template<
|
||||
class ConstBufferSequence,
|
||||
|
@ -163,6 +163,17 @@ teardown(
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
@li @c net::cancellation_type::partial
|
||||
@li @c net::cancellation_type::total
|
||||
|
||||
if they are also supported by the socket's @c async_wait operation.
|
||||
|
||||
*/
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
|
@ -48,6 +48,7 @@ add_executable (tests-beast-core
|
||||
file_posix.cpp
|
||||
file_stdio.cpp
|
||||
file_win32.cpp
|
||||
filtering_cancellation_slot.cpp
|
||||
flat_buffer.cpp
|
||||
flat_static_buffer.cpp
|
||||
flat_stream.cpp
|
||||
|
@ -39,6 +39,7 @@ local SOURCES =
|
||||
file_posix.cpp
|
||||
file_stdio.cpp
|
||||
file_win32.cpp
|
||||
filtering_cancellation_slot.cpp
|
||||
flat_buffer.cpp
|
||||
flat_static_buffer.cpp
|
||||
flat_stream.cpp
|
||||
|
48
test/beast/core/filtering_cancellation_slot.cpp
Normal file
48
test/beast/core/filtering_cancellation_slot.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/beast/core/detail/filtering_cancellation_slot.hpp>
|
||||
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
|
||||
struct filtering_cancellation_slot_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
run()
|
||||
{
|
||||
using ct = net::cancellation_type;
|
||||
ct fired = ct::none;
|
||||
auto l = [&fired](ct tp){fired = tp;};
|
||||
|
||||
net::cancellation_signal sl;
|
||||
|
||||
detail::filtering_cancellation_slot<> slot{ct::terminal, sl.slot()};
|
||||
slot.type |= ct::total;
|
||||
slot = sl.slot();
|
||||
|
||||
slot.assign(l);
|
||||
|
||||
BEAST_EXPECT(fired == ct::none);
|
||||
sl.emit(ct::total);
|
||||
BEAST_EXPECT(fired == ct::total);
|
||||
sl.emit(ct::partial);
|
||||
BEAST_EXPECT(fired == ct::total);
|
||||
sl.emit(ct::terminal);
|
||||
BEAST_EXPECT(fired == ct::terminal);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,core,filtering_cancellation_slot);
|
||||
|
||||
} // beast
|
||||
} // boost
|
@ -9,7 +9,7 @@
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/beast/core/saved_handler.hpp>
|
||||
|
||||
#include <boost/asio/bind_cancellation_slot.hpp>
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
@ -46,7 +46,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
operator()(system::error_code ec_ = {})
|
||||
{
|
||||
failed_ = false;
|
||||
}
|
||||
@ -74,7 +74,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
operator()(system::error_code = {})
|
||||
{
|
||||
invoked_ = true;
|
||||
}
|
||||
@ -90,7 +90,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
operator()(system::error_code = {})
|
||||
{
|
||||
}
|
||||
};
|
||||
@ -119,7 +119,7 @@ public:
|
||||
{
|
||||
saved_handler sh;
|
||||
try
|
||||
{
|
||||
{
|
||||
sh.emplace(throwing_handler{});
|
||||
fail();
|
||||
}
|
||||
@ -131,10 +131,88 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testSavedHandlerCancellation()
|
||||
{
|
||||
{
|
||||
net::cancellation_signal sig;
|
||||
|
||||
saved_handler sh;
|
||||
BEAST_EXPECT(! sh.has_value());
|
||||
|
||||
sh.emplace(
|
||||
net::bind_cancellation_slot(
|
||||
sig.slot(), handler{}));
|
||||
BEAST_EXPECT(sh.has_value());
|
||||
BEAST_EXPECT(sig.slot().has_handler());
|
||||
sig.emit(net::cancellation_type::all);
|
||||
BEAST_EXPECT(! sh.has_value());
|
||||
BEAST_EXPECT(!sig.slot().has_handler());
|
||||
|
||||
|
||||
sh.emplace(
|
||||
net::bind_cancellation_slot(
|
||||
sig.slot(), handler{}));
|
||||
BEAST_EXPECT(sh.has_value());
|
||||
BEAST_EXPECT(sig.slot().has_handler());
|
||||
sig.emit(net::cancellation_type::total);
|
||||
BEAST_EXPECT(sh.has_value());
|
||||
BEAST_EXPECT(sig.slot().has_handler());
|
||||
sig.emit(net::cancellation_type::terminal);
|
||||
BEAST_EXPECT(! sh.has_value());
|
||||
BEAST_EXPECT(!sig.slot().has_handler());
|
||||
|
||||
sh.emplace(
|
||||
net::bind_cancellation_slot(
|
||||
sig.slot(), handler{}),
|
||||
net::cancellation_type::total);
|
||||
BEAST_EXPECT(sh.has_value());
|
||||
BEAST_EXPECT(sig.slot().has_handler());
|
||||
sig.emit(net::cancellation_type::total);
|
||||
BEAST_EXPECT(! sh.has_value());
|
||||
BEAST_EXPECT(!sig.slot().has_handler());
|
||||
|
||||
{
|
||||
saved_handler sh_inner;
|
||||
sh_inner.emplace(
|
||||
net::bind_cancellation_slot(
|
||||
sig.slot(), handler{}));
|
||||
|
||||
sh = std::move(sh_inner);
|
||||
}
|
||||
BEAST_EXPECT(sh.has_value());
|
||||
BEAST_EXPECT(sig.slot().has_handler());
|
||||
sig.emit(net::cancellation_type::all);
|
||||
BEAST_EXPECT(! sh.has_value());
|
||||
BEAST_EXPECT(!sig.slot().has_handler());
|
||||
|
||||
}
|
||||
{
|
||||
saved_handler sh;
|
||||
net::cancellation_signal sig;
|
||||
|
||||
try
|
||||
{
|
||||
sh.emplace(
|
||||
net::bind_cancellation_slot(
|
||||
sig.slot(),
|
||||
throwing_handler{}));
|
||||
fail();
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
BEAST_EXPECT(!sig.slot().has_handler());
|
||||
BEAST_EXPECT(! sh.has_value());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testSavedHandler();
|
||||
testSavedHandlerCancellation();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -25,6 +25,12 @@
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/asio/bind_cancellation_slot.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/connect_pipe.hpp>
|
||||
#include <boost/asio/readable_pipe.hpp>
|
||||
#include <boost/asio/writable_pipe.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#if BOOST_ASIO_HAS_CO_AWAIT
|
||||
@ -729,7 +735,57 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
testCancellation(yield_context do_yield)
|
||||
{
|
||||
// this is tested on a pipe
|
||||
// because the test::stream doesn't implement cancellation
|
||||
{
|
||||
response<string_body> m;
|
||||
error_code ec;
|
||||
net::writable_pipe ts{ioc_};
|
||||
net::readable_pipe tr{ioc_};
|
||||
net::connect_pipe(tr, ts);
|
||||
net::cancellation_signal cl;
|
||||
net::post(ioc_, [&]{cl.emit(net::cancellation_type::all);});
|
||||
net::steady_timer timeout(ioc_, std::chrono::seconds(5));
|
||||
timeout.async_wait(
|
||||
[&](error_code ec)
|
||||
{
|
||||
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||
if (!ec) // this means the cancel failed!
|
||||
ts.close();
|
||||
});
|
||||
multi_buffer b;
|
||||
async_read(tr, b, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
|
||||
timeout.cancel();
|
||||
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||
}
|
||||
{
|
||||
response<string_body> m;
|
||||
error_code ec;
|
||||
net::writable_pipe ts{ioc_};
|
||||
net::readable_pipe tr{ioc_};
|
||||
net::connect_pipe(tr, ts);
|
||||
net::cancellation_signal cl;
|
||||
net::post(ioc_, [&]{cl.emit(net::cancellation_type::all);});
|
||||
net::steady_timer timeout(ioc_, std::chrono::seconds(5));
|
||||
timeout.async_wait(
|
||||
[&](error_code ec)
|
||||
{
|
||||
// using BEAST_EXPECT HERE is a race condition, since the test suite might
|
||||
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||
if (!ec) // this means the cancel failed!
|
||||
ts.close();
|
||||
});
|
||||
multi_buffer b;
|
||||
async_read(tr, b, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
|
||||
timeout.cancel();
|
||||
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||
}
|
||||
// the timer handler may be invoked after the test suite is complete if we don't post.
|
||||
asio::post(ioc_, do_yield);
|
||||
}
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@ -761,6 +817,11 @@ public:
|
||||
testReadSomeHeader(yield);
|
||||
});
|
||||
testReadSomeHeader();
|
||||
yield_to(
|
||||
[&](yield_context yield)
|
||||
{
|
||||
testCancellation(yield);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,8 +22,13 @@
|
||||
#include <boost/beast/_experimental/test/stream.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <boost/asio/bind_cancellation_slot.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/connect_pipe.hpp>
|
||||
#include <boost/asio/readable_pipe.hpp>
|
||||
#include <boost/asio/writable_pipe.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@ -1050,6 +1055,69 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
testCancellation(yield_context do_yield)
|
||||
{
|
||||
// this is tested on a pipe
|
||||
// because the test::stream doesn't implement cancellation
|
||||
{
|
||||
response<string_body> m;
|
||||
m.version(10);
|
||||
m.result(status::ok);
|
||||
m.set(field::server, "test");
|
||||
m.set(field::content_length, "5");
|
||||
// make the content big enough so it overflows the buffer
|
||||
// that'll make the op never complete if we don't cancel
|
||||
m.body().assign(10000000, '*');
|
||||
error_code ec;
|
||||
net::writable_pipe ts{ioc_};
|
||||
net::readable_pipe tr{ioc_};
|
||||
net::connect_pipe(tr, ts);
|
||||
net::cancellation_signal cl;
|
||||
net::post(ioc_, [&]{cl.emit(net::cancellation_type::all);});
|
||||
net::steady_timer timeout(ioc_, std::chrono::seconds(5));
|
||||
timeout.async_wait(
|
||||
[&](error_code ec)
|
||||
{
|
||||
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||
if (!ec) // this means the cancel failed!
|
||||
ts.close();
|
||||
});
|
||||
|
||||
async_write(ts, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
|
||||
timeout.cancel();
|
||||
net::post(ioc_, do_yield); // wait for the timeout to finish
|
||||
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||
}
|
||||
{
|
||||
response<string_body> m;
|
||||
m.version(11);
|
||||
m.result(status::ok);
|
||||
m.set(field::server, "test");
|
||||
m.set(field::transfer_encoding, "chunked");
|
||||
m.body().assign(10000000, '*');
|
||||
error_code ec;
|
||||
net::writable_pipe ts{ioc_};
|
||||
net::readable_pipe tr{ioc_};
|
||||
net::connect_pipe(tr, ts);
|
||||
net::cancellation_signal cl;
|
||||
net::post(ioc_, [&]{cl.emit(net::cancellation_type::all);});
|
||||
net::steady_timer timeout(ioc_, std::chrono::seconds(5));
|
||||
timeout.async_wait(
|
||||
[&](error_code ec)
|
||||
{
|
||||
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||
if (!ec) // this means the cancel failed!
|
||||
ts.close();
|
||||
});
|
||||
async_write(ts, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
|
||||
timeout.cancel();
|
||||
net::post(ioc_, do_yield); // wait for the timeout to finish
|
||||
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||
}
|
||||
// the timer handler may be invoked after the test suite is complete if we don't post.
|
||||
asio::post(ioc_, do_yield);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
@ -1077,6 +1145,11 @@ public:
|
||||
#if BOOST_ASIO_HAS_CO_AWAIT
|
||||
boost::ignore_unused(&write_test::testAwaitableCompiles);
|
||||
#endif
|
||||
yield_to(
|
||||
[&](yield_context yield)
|
||||
{
|
||||
testCancellation(yield);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -21,6 +21,7 @@ add_executable (tests-beast-websocket
|
||||
test.hpp
|
||||
_detail_prng.cpp
|
||||
accept.cpp
|
||||
cancel.cpp
|
||||
close.cpp
|
||||
error.cpp
|
||||
frame.cpp
|
||||
|
@ -12,6 +12,7 @@ local SOURCES =
|
||||
_detail_impl_base.cpp
|
||||
_detail_prng.cpp
|
||||
accept.cpp
|
||||
cancel.cpp
|
||||
close.cpp
|
||||
error.cpp
|
||||
frame.cpp
|
||||
|
230
test/beast/websocket/cancel.cpp
Normal file
230
test/beast/websocket/cancel.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/beast/websocket/stream.hpp>
|
||||
#include <boost/beast/core/tcp_stream.hpp>
|
||||
|
||||
#include <boost/beast/_experimental/test/stream.hpp>
|
||||
#include <boost/beast/_experimental/test/tcp.hpp>
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/asio/bind_cancellation_slot.hpp>
|
||||
#include <boost/asio/deferred.hpp>
|
||||
#include <boost/asio/redirect_error.hpp>
|
||||
|
||||
#include "test.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
struct async_all_server_op : boost::asio::coroutine
|
||||
{
|
||||
stream<asio::ip::tcp::socket> & ws;
|
||||
|
||||
async_all_server_op(stream<asio::ip::tcp::socket> & ws) : ws(ws) {}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self && self, error_code ec = {}, std::size_t sz = 0)
|
||||
{
|
||||
if (ec)
|
||||
return self.complete(ec);
|
||||
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
self.reset_cancellation_state([](net::cancellation_type ct){return ct;});
|
||||
BOOST_ASIO_CORO_YIELD ws.async_handshake("test", "/", std::move(self));
|
||||
BOOST_ASIO_CORO_YIELD ws.async_ping("", std::move(self));
|
||||
BOOST_ASIO_CORO_YIELD ws.async_write_some(false, net::buffer("FOO", 3), std::move(self));
|
||||
BOOST_ASIO_CORO_YIELD ws.async_write_some(true, net::buffer("BAR", 3), std::move(self));
|
||||
BOOST_ASIO_CORO_YIELD ws.async_close("testing", std::move(self));
|
||||
self.complete({});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<BOOST_BEAST_ASYNC_TPARAM1 CompletionToken>
|
||||
BOOST_BEAST_ASYNC_RESULT1(CompletionToken)
|
||||
async_all_server(
|
||||
stream<asio::ip::tcp::socket> & ws,
|
||||
CompletionToken && token)
|
||||
{
|
||||
return net::async_compose<CompletionToken, void(error_code)>
|
||||
(
|
||||
async_all_server_op{ws},
|
||||
token, ws
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
struct async_all_client_op : boost::asio::coroutine
|
||||
{
|
||||
stream<asio::ip::tcp::socket> & ws;
|
||||
|
||||
async_all_client_op(stream<asio::ip::tcp::socket> & ws) : ws(ws) {}
|
||||
struct impl_t
|
||||
{
|
||||
impl_t () = default;
|
||||
std::string res;
|
||||
net::dynamic_string_buffer<char, std::char_traits<char>, std::allocator<char>> buf =
|
||||
net::dynamic_buffer(res);
|
||||
};
|
||||
|
||||
std::shared_ptr<impl_t> impl{std::make_shared<impl_t>()};
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self && self, error_code ec = {}, std::size_t sz = 0)
|
||||
{
|
||||
if (ec)
|
||||
return self.complete(ec);
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
// let everything pass
|
||||
self.reset_cancellation_state([](net::cancellation_type ct){return ct;});
|
||||
|
||||
BOOST_ASIO_CORO_YIELD ws.async_accept(std::move(self));
|
||||
BOOST_ASIO_CORO_YIELD ws.async_pong("", std::move(self));
|
||||
|
||||
|
||||
BOOST_ASIO_CORO_YIELD ws.async_read(impl->buf, std::move(self));
|
||||
BEAST_EXPECTS(impl->res == "FOOBAR", impl->res);
|
||||
|
||||
BOOST_ASIO_CORO_YIELD ws.async_read(impl->buf, std::move(self));
|
||||
BEAST_EXPECTS(ec == websocket::error::closed
|
||||
|| ec == net::error::connection_reset
|
||||
// hard coded winapi error (WSAECONNRESET), same as connection_reset, but asio delivers it with system_category
|
||||
|| (ec.value() == 10054
|
||||
&& ec.category() == system::system_category())
|
||||
|| ec == net::error::not_connected, ec.message());
|
||||
self.complete({});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
template<BOOST_BEAST_ASYNC_TPARAM1 CompletionToken>
|
||||
BOOST_BEAST_ASYNC_RESULT1(CompletionToken)
|
||||
async_all_client(
|
||||
stream<asio::ip::tcp::socket> & ws,
|
||||
CompletionToken && token)
|
||||
{
|
||||
return net::async_compose<CompletionToken, void(error_code)>
|
||||
(
|
||||
async_all_client_op{ws},
|
||||
token, ws
|
||||
);
|
||||
}
|
||||
|
||||
class cancel_test : public websocket_test_suite
|
||||
{
|
||||
public:
|
||||
std::size_t run_impl(net::cancellation_signal &sl,
|
||||
net::io_context &ctx,
|
||||
std::size_t trigger = -1,
|
||||
net::cancellation_type tp = net::cancellation_type::terminal)
|
||||
{
|
||||
std::size_t cnt = 0;
|
||||
std::size_t res = 0u;
|
||||
while ((res = ctx.run_one()) != 0)
|
||||
if (trigger == cnt ++)
|
||||
{
|
||||
sl.emit(tp);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
std::size_t testAll(std::size_t & cancel_counter,
|
||||
bool cancel_server = false,
|
||||
std::size_t trigger = -1)
|
||||
{
|
||||
net::cancellation_signal sig1, sig2;
|
||||
|
||||
net::io_context ioc;
|
||||
using tcp = net::ip::tcp;
|
||||
|
||||
stream<tcp::socket> ws1(ioc.get_executor());
|
||||
stream<tcp::socket> ws2(ioc.get_executor());
|
||||
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||
|
||||
async_all_server(ws1,
|
||||
net::bind_cancellation_slot(sig1.slot(), [&](system::error_code ec)
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
if (ec == net::error::operation_aborted &&
|
||||
(cancel_server || trigger == static_cast<std::size_t>(-1)))
|
||||
cancel_counter++;
|
||||
|
||||
BEAST_EXPECTS(ec == net::error::operation_aborted
|
||||
|| ec == net::error::broken_pipe
|
||||
// winapi WSAECONNRESET, as system_category
|
||||
|| ec == error_code(10054, boost::system::system_category())
|
||||
|| ec == net::error::bad_descriptor
|
||||
|| ec == net::error::eof, ec.message());
|
||||
get_lowest_layer(ws1).close();
|
||||
}
|
||||
}));
|
||||
|
||||
async_all_client(
|
||||
ws2,
|
||||
net::bind_cancellation_slot(sig2.slot(), [&](system::error_code ec)
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
if (ec == net::error::operation_aborted &&
|
||||
(!cancel_server || trigger == static_cast<std::size_t>(-1)))
|
||||
cancel_counter++;
|
||||
BEAST_EXPECTS(ec == net::error::operation_aborted
|
||||
|| ec == error::closed
|
||||
|| ec == net::error::broken_pipe
|
||||
|| ec == net::error::connection_reset
|
||||
|| ec == net::error::not_connected
|
||||
// winapi WSAECONNRESET, as system_category
|
||||
|| ec == error_code(10054, boost::system::system_category())
|
||||
|| ec == net::error::eof, ec.message());
|
||||
get_lowest_layer(ws1).close();
|
||||
}
|
||||
}));
|
||||
|
||||
return run_impl(cancel_server ? sig1 : sig2, ioc, trigger);
|
||||
}
|
||||
|
||||
void brute_force()
|
||||
{
|
||||
std::size_t cancel_counter = 0;
|
||||
const auto init = testAll(cancel_counter);
|
||||
BEAST_EXPECT(cancel_counter == 0u);
|
||||
for (std::size_t cnt = 0; cnt < init; cnt ++)
|
||||
testAll(cancel_counter, true, cnt);
|
||||
|
||||
BEAST_EXPECT(cancel_counter > 0u);
|
||||
cancel_counter = 0u;
|
||||
for (std::size_t cnt = 0; cnt < init; cnt ++)
|
||||
testAll(cancel_counter, false, cnt);
|
||||
|
||||
|
||||
BEAST_EXPECT(cancel_counter > 0u);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
brute_force();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,websocket,cancel);
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
@ -251,6 +251,9 @@ struct handler
|
||||
using executor_type = boost::asio::io_context::executor_type;
|
||||
executor_type get_executor() const noexcept;
|
||||
|
||||
using cancellation_slot_type = boost::asio::cancellation_slot;
|
||||
cancellation_slot_type get_cancellation_slot() const noexcept;
|
||||
|
||||
void operator()(boost::beast::error_code, std::size_t);
|
||||
};
|
||||
//]
|
||||
@ -265,6 +268,12 @@ inline auto handler::get_executor() const noexcept ->
|
||||
static boost::asio::io_context ioc;
|
||||
return ioc.get_executor();
|
||||
}
|
||||
inline auto handler::get_cancellation_slot() const noexcept ->
|
||||
cancellation_slot_type
|
||||
{
|
||||
return cancellation_slot_type();
|
||||
}
|
||||
|
||||
inline void handler::operator()(
|
||||
boost::beast::error_code, std::size_t)
|
||||
{
|
||||
@ -296,6 +305,18 @@ struct associated_executor<handler, Executor>
|
||||
Executor const& ex = Executor{}) noexcept;
|
||||
};
|
||||
|
||||
template<class CancellationSlot>
|
||||
struct associated_cancellation_slot<handler, CancellationSlot>
|
||||
{
|
||||
using type = cancellation_slot;
|
||||
|
||||
static
|
||||
type
|
||||
get(handler const& h,
|
||||
CancellationSlot const& cs = CancellationSlot{}) noexcept;
|
||||
};
|
||||
|
||||
|
||||
} // boost
|
||||
} // asio
|
||||
//]
|
||||
@ -315,6 +336,15 @@ get(handler const&, Executor const&) noexcept -> type
|
||||
return {};
|
||||
}
|
||||
|
||||
template<class CancellationSlot>
|
||||
auto
|
||||
boost::asio::associated_cancellation_slot<handler, CancellationSlot>::
|
||||
get(handler const&, CancellationSlot const&) noexcept -> type
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace boost {
|
||||
|
Reference in New Issue
Block a user