handler_ptr gives the strong exception guarantee (API Change):

* The handler_ptr constructor now provides the
  strong exception guarantee.

* The signature for managed objects constructed by
  `handler_ptr` receives a const reference to the handler.

Actions required:

* Change the constructor signature for state objects
  used with handler_ptr to receive a const reference to
  the handler.
This commit is contained in:
Damian Jarek
2017-12-04 13:02:31 -08:00
committed by Vinnie Falco
parent e08132106e
commit 285965d82e
11 changed files with 81 additions and 41 deletions

View File

@ -8,6 +8,16 @@ Version 149:
* Don't copy completion handlers * Don't copy completion handlers
* handler_ptr is move-only * handler_ptr is move-only
API Changes:
* handler_ptr gives the strong exception guarantee
Actions Required:
* Change the constructor signature for state objects
used with handler_ptr to receive a const reference to
the handler.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Version 148: Version 148:

View File

@ -107,7 +107,7 @@ class echo_op
// contained object constructor is a reference to the // contained object constructor is a reference to the
// managed final completion handler. // managed final completion handler.
// //
explicit state(Handler& handler, AsyncStream& stream_) explicit state(Handler const& handler, AsyncStream& stream_)
: stream(stream_) : stream(stream_)
, buffer((std::numeric_limits<std::size_t>::max)(), , buffer((std::numeric_limits<std::size_t>::max)(),
boost::asio::get_associated_allocator(handler)) boost::asio::get_associated_allocator(handler))

View File

@ -49,8 +49,10 @@ namespace beast {
template<class T, class Handler> template<class T, class Handler>
class handler_ptr class handler_ptr
{ {
using handler_storage_t = typename detail::aligned_union<1, Handler>::type;
T* t_ = nullptr; T* t_ = nullptr;
Handler h_; handler_storage_t h_;
void clear(); void clear();
@ -100,9 +102,12 @@ public:
following equivalent signature: following equivalent signature:
@code @code
T::T(Handler&, Args&&...) T::T(Handler const&, Args&&...)
@endcode @endcode
@par Exception Safety
Strong guarantee.
@param handler The handler to associate with the owned @param handler The handler to associate with the owned
object. The argument will be moved if it is an xvalue. object. The argument will be moved if it is an xvalue.
@ -116,14 +121,14 @@ public:
handler_type const& handler_type const&
handler() const handler() const
{ {
return h_; return *reinterpret_cast<Handler const*>(&h_);
} }
/// Returns a reference to the handler /// Returns a reference to the handler
handler_type& handler_type&
handler() handler()
{ {
return h_; return *reinterpret_cast<Handler*>(&h_);
} }
/** Returns a pointer to the owned object. /** Returns a pointer to the owned object.

View File

@ -24,8 +24,9 @@ clear()
{ {
typename beast::detail::allocator_traits< typename beast::detail::allocator_traits<
boost::asio::associated_allocator_t< boost::asio::associated_allocator_t<
Handler>>::template rebind_alloc<T> alloc{ Handler>>::template rebind_alloc<T> alloc(
boost::asio::get_associated_allocator(h_)}; boost::asio::get_associated_allocator(
handler()));
beast::detail::allocator_traits< beast::detail::allocator_traits<
decltype(alloc)>::destroy(alloc, t_); decltype(alloc)>::destroy(alloc, t_);
beast::detail::allocator_traits< beast::detail::allocator_traits<
@ -38,43 +39,51 @@ handler_ptr<T, Handler>::
~handler_ptr() ~handler_ptr()
{ {
if(t_) if(t_)
{
clear(); clear();
handler().~Handler();
}
} }
template<class T, class Handler> template<class T, class Handler>
handler_ptr<T, Handler>:: handler_ptr<T, Handler>::
handler_ptr(handler_ptr&& other) handler_ptr(handler_ptr&& other)
: t_(other.t_) : t_(other.t_)
, h_(std::move(other.h_))
{ {
other.t_ = nullptr; if(other.t_)
{
new(&h_) Handler(std::move(other.handler()));
other.handler().~Handler();
other.t_ = nullptr;
}
} }
template<class T, class Handler> template<class T, class Handler>
template<class DeducedHandler, class... Args> template<class DeducedHandler, class... Args>
handler_ptr<T, Handler>:: handler_ptr<T, Handler>::
handler_ptr(DeducedHandler&& handler, Args&&... args) handler_ptr(DeducedHandler&& h, Args&&... args)
: t_([&]
{
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
typename beast::detail::allocator_traits<
boost::asio::associated_allocator_t<
Handler>>::template rebind_alloc<T> alloc{
boost::asio::get_associated_allocator(handler)};
using A = decltype(alloc);
auto const d =
[&alloc](T* p)
{
beast::detail::allocator_traits<A>::deallocate(alloc, p, 1);
};
std::unique_ptr<T, decltype(d)> p{
beast::detail::allocator_traits<A>::allocate(alloc, 1), d};
beast::detail::allocator_traits<A>::construct(
alloc, p.get(), handler, std::forward<Args>(args)...);
return p.release();
}())
, h_(std::forward<DeducedHandler>(handler))
{ {
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
typename beast::detail::allocator_traits<
boost::asio::associated_allocator_t<
Handler>>::template rebind_alloc<T> alloc{
boost::asio::get_associated_allocator(h)};
using A = decltype(alloc);
bool destroy = false;
auto deleter = [&alloc, &destroy](T* p)
{
if(destroy)
beast::detail::allocator_traits<A>::destroy(alloc, p);
beast::detail::allocator_traits<A>::deallocate(alloc, p, 1);
};
std::unique_ptr<T, decltype(deleter)> t{
beast::detail::allocator_traits<A>::allocate(alloc, 1), deleter};
beast::detail::allocator_traits<A>::construct(alloc, t.get(),
static_cast<DeducedHandler const&>(h),
std::forward<Args>(args)...);
destroy = true;
new(&h_) Handler(std::forward<DeducedHandler>(h));
t_ = t.release();
} }
template<class T, class Handler> template<class T, class Handler>
@ -85,7 +94,14 @@ release_handler() ->
{ {
BOOST_ASSERT(t_); BOOST_ASSERT(t_);
clear(); clear();
return std::move(h_); auto deleter = [](Handler* h)
{
h->~Handler();
};
std::unique_ptr<
Handler, decltype(deleter)> destroyer{
&handler(), deleter};
return std::move(handler());
} }
template<class T, class Handler> template<class T, class Handler>
@ -96,7 +112,14 @@ invoke(Args&&... args)
{ {
BOOST_ASSERT(t_); BOOST_ASSERT(t_);
clear(); clear();
h_(std::forward<Args>(args)...); auto deleter = [](Handler* h)
{
h->~Handler();
};
std::unique_ptr<
Handler, decltype(deleter)> destroyer{
&handler(), deleter};
handler()(std::forward<Args>(args)...);
} }
} // beast } // beast

View File

@ -316,7 +316,7 @@ class read_msg_op
std::size_t bytes_transferred = 0; std::size_t bytes_transferred = 0;
bool cont = false; bool cont = false;
data(Handler&, Stream& s_, data(Handler const&, Stream& s_,
DynamicBuffer& b_, message_type& m_) DynamicBuffer& b_, message_type& m_)
: s(s_) : s(s_)
, b(b_) , b(b_)

View File

@ -306,7 +306,7 @@ class write_msg_op
Stream& s; Stream& s;
serializer<isRequest, Body, Fields> sr; serializer<isRequest, Body, Fields> sr;
data(Handler&, Stream& s_, message< data(Handler const&, Stream& s_, message<
isRequest, Body, Fields>& m_) isRequest, Body, Fields>& m_)
: s(s_) : s(s_)
, sr(m_) , sr(m_)

View File

@ -45,7 +45,7 @@ class stream<NextLayer>::response_op
response_type res; response_type res;
template<class Body, class Allocator, class Decorator> template<class Body, class Allocator, class Decorator>
data(Handler&, stream<NextLayer>& ws_, http::request< data(Handler const&, stream<NextLayer>& ws_, http::request<
Body, http::basic_fields<Allocator>> const& req, Body, http::basic_fields<Allocator>> const& req,
Decorator const& decorator) Decorator const& decorator)
: ws(ws_) : ws(ws_)
@ -142,7 +142,7 @@ class stream<NextLayer>::accept_op
stream<NextLayer>& ws; stream<NextLayer>& ws;
Decorator decorator; Decorator decorator;
http::request_parser<http::empty_body> p; http::request_parser<http::empty_body> p;
data(Handler&, stream<NextLayer>& ws_, data(Handler const&, stream<NextLayer>& ws_,
Decorator const& decorator_) Decorator const& decorator_)
: ws(ws_) : ws(ws_)
, decorator(decorator_) , decorator(decorator_)

View File

@ -48,7 +48,7 @@ class stream<NextLayer>::close_op
bool cont = false; bool cont = false;
state( state(
Handler&, Handler const&,
stream<NextLayer>& ws_, stream<NextLayer>& ws_,
close_reason const& cr) close_reason const& cr)
: ws(ws_) : ws(ws_)

View File

@ -47,7 +47,7 @@ class stream<NextLayer>::handshake_op
response_type res; response_type res;
template<class Decorator> template<class Decorator>
data(Handler&, stream<NextLayer>& ws_, data(Handler const&, stream<NextLayer>& ws_,
response_type* res_p_, response_type* res_p_,
string_view host, string_view host,
string_view target, string_view target,

View File

@ -44,7 +44,7 @@ class stream<NextLayer>::ping_op
token tok; token tok;
state( state(
Handler&, Handler const&,
stream<NextLayer>& ws_, stream<NextLayer>& ws_,
detail::opcode op, detail::opcode op,
ping_data const& payload) ping_data const& payload)

View File

@ -34,7 +34,8 @@ public:
struct T struct T
{ {
T(handler&) explicit
T(handler const&)
{ {
} }
@ -45,7 +46,8 @@ public:
struct U struct U
{ {
U(handler&) explicit
U(handler const&)
{ {
throw std::exception{}; throw std::exception{};
} }