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
* 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:

View File

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

View File

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

View File

@ -24,8 +24,9 @@ clear()
{
typename beast::detail::allocator_traits<
boost::asio::associated_allocator_t<
Handler>>::template rebind_alloc<T> alloc{
boost::asio::get_associated_allocator(h_)};
Handler>>::template rebind_alloc<T> alloc(
boost::asio::get_associated_allocator(
handler()));
beast::detail::allocator_traits<
decltype(alloc)>::destroy(alloc, t_);
beast::detail::allocator_traits<
@ -38,43 +39,51 @@ handler_ptr<T, Handler>::
~handler_ptr()
{
if(t_)
{
clear();
handler().~Handler();
}
}
template<class T, class Handler>
handler_ptr<T, Handler>::
handler_ptr(handler_ptr&& other)
: 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 DeducedHandler, class... Args>
handler_ptr<T, Handler>::
handler_ptr(DeducedHandler&& handler, 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))
handler_ptr(DeducedHandler&& h, Args&&... args)
{
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>
@ -85,7 +94,14 @@ release_handler() ->
{
BOOST_ASSERT(t_);
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>
@ -96,7 +112,14 @@ invoke(Args&&... args)
{
BOOST_ASSERT(t_);
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

View File

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

View File

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

View File

@ -45,7 +45,7 @@ class stream<NextLayer>::response_op
response_type res;
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,
Decorator const& decorator)
: ws(ws_)
@ -142,7 +142,7 @@ class stream<NextLayer>::accept_op
stream<NextLayer>& ws;
Decorator decorator;
http::request_parser<http::empty_body> p;
data(Handler&, stream<NextLayer>& ws_,
data(Handler const&, stream<NextLayer>& ws_,
Decorator const& decorator_)
: ws(ws_)
, decorator(decorator_)

View File

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

View File

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

View File

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

View File

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