diff --git a/CHANGELOG.md b/CHANGELOG.md index aa11f720..f0b8e77e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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: diff --git a/example/echo-op/echo_op.cpp b/example/echo-op/echo_op.cpp index 3d9240e7..0d73da87 100644 --- a/example/echo-op/echo_op.cpp +++ b/example/echo-op/echo_op.cpp @@ -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::max)(), boost::asio::get_associated_allocator(handler)) diff --git a/include/boost/beast/core/handler_ptr.hpp b/include/boost/beast/core/handler_ptr.hpp index dfce6cce..559c0c15 100644 --- a/include/boost/beast/core/handler_ptr.hpp +++ b/include/boost/beast/core/handler_ptr.hpp @@ -49,8 +49,10 @@ namespace beast { template 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(&h_); } /// Returns a reference to the handler handler_type& handler() { - return h_; + return *reinterpret_cast(&h_); } /** Returns a pointer to the owned object. diff --git a/include/boost/beast/core/impl/handler_ptr.ipp b/include/boost/beast/core/impl/handler_ptr.ipp index 8a03e1cf..99d1a894 100644 --- a/include/boost/beast/core/impl/handler_ptr.ipp +++ b/include/boost/beast/core/impl/handler_ptr.ipp @@ -24,8 +24,9 @@ clear() { typename beast::detail::allocator_traits< boost::asio::associated_allocator_t< - Handler>>::template rebind_alloc alloc{ - boost::asio::get_associated_allocator(h_)}; + Handler>>::template rebind_alloc 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:: ~handler_ptr() { if(t_) + { clear(); + handler().~Handler(); + } } template handler_ptr:: 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 template handler_ptr:: -handler_ptr(DeducedHandler&& handler, Args&&... args) - : t_([&] - { - BOOST_STATIC_ASSERT(! std::is_array::value); - typename beast::detail::allocator_traits< - boost::asio::associated_allocator_t< - Handler>>::template rebind_alloc alloc{ - boost::asio::get_associated_allocator(handler)}; - using A = decltype(alloc); - auto const d = - [&alloc](T* p) - { - beast::detail::allocator_traits::deallocate(alloc, p, 1); - }; - std::unique_ptr p{ - beast::detail::allocator_traits::allocate(alloc, 1), d}; - beast::detail::allocator_traits::construct( - alloc, p.get(), handler, std::forward(args)...); - return p.release(); - }()) - , h_(std::forward(handler)) +handler_ptr(DeducedHandler&& h, Args&&... args) { + BOOST_STATIC_ASSERT(! std::is_array::value); + typename beast::detail::allocator_traits< + boost::asio::associated_allocator_t< + Handler>>::template rebind_alloc 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::destroy(alloc, p); + beast::detail::allocator_traits::deallocate(alloc, p, 1); + }; + std::unique_ptr t{ + beast::detail::allocator_traits::allocate(alloc, 1), deleter}; + beast::detail::allocator_traits::construct(alloc, t.get(), + static_cast(h), + std::forward(args)...); + destroy = true; + new(&h_) Handler(std::forward(h)); + t_ = t.release(); } template @@ -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 @@ -96,7 +112,14 @@ invoke(Args&&... args) { BOOST_ASSERT(t_); clear(); - h_(std::forward(args)...); + auto deleter = [](Handler* h) + { + h->~Handler(); + }; + std::unique_ptr< + Handler, decltype(deleter)> destroyer{ + &handler(), deleter}; + handler()(std::forward(args)...); } } // beast diff --git a/include/boost/beast/http/impl/read.ipp b/include/boost/beast/http/impl/read.ipp index cbabff0f..df0eb690 100644 --- a/include/boost/beast/http/impl/read.ipp +++ b/include/boost/beast/http/impl/read.ipp @@ -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_) diff --git a/include/boost/beast/http/impl/write.ipp b/include/boost/beast/http/impl/write.ipp index c07fe540..8fd82a5e 100644 --- a/include/boost/beast/http/impl/write.ipp +++ b/include/boost/beast/http/impl/write.ipp @@ -306,7 +306,7 @@ class write_msg_op Stream& s; serializer sr; - data(Handler&, Stream& s_, message< + data(Handler const&, Stream& s_, message< isRequest, Body, Fields>& m_) : s(s_) , sr(m_) diff --git a/include/boost/beast/websocket/impl/accept.ipp b/include/boost/beast/websocket/impl/accept.ipp index 8b29de46..e85bfe45 100644 --- a/include/boost/beast/websocket/impl/accept.ipp +++ b/include/boost/beast/websocket/impl/accept.ipp @@ -45,7 +45,7 @@ class stream::response_op response_type res; template - data(Handler&, stream& ws_, http::request< + data(Handler const&, stream& ws_, http::request< Body, http::basic_fields> const& req, Decorator const& decorator) : ws(ws_) @@ -142,7 +142,7 @@ class stream::accept_op stream& ws; Decorator decorator; http::request_parser p; - data(Handler&, stream& ws_, + data(Handler const&, stream& ws_, Decorator const& decorator_) : ws(ws_) , decorator(decorator_) diff --git a/include/boost/beast/websocket/impl/close.ipp b/include/boost/beast/websocket/impl/close.ipp index e2b61318..23c026aa 100644 --- a/include/boost/beast/websocket/impl/close.ipp +++ b/include/boost/beast/websocket/impl/close.ipp @@ -48,7 +48,7 @@ class stream::close_op bool cont = false; state( - Handler&, + Handler const&, stream& ws_, close_reason const& cr) : ws(ws_) diff --git a/include/boost/beast/websocket/impl/handshake.ipp b/include/boost/beast/websocket/impl/handshake.ipp index 18fd1e06..0de20986 100644 --- a/include/boost/beast/websocket/impl/handshake.ipp +++ b/include/boost/beast/websocket/impl/handshake.ipp @@ -47,7 +47,7 @@ class stream::handshake_op response_type res; template - data(Handler&, stream& ws_, + data(Handler const&, stream& ws_, response_type* res_p_, string_view host, string_view target, diff --git a/include/boost/beast/websocket/impl/ping.ipp b/include/boost/beast/websocket/impl/ping.ipp index 223d06a7..a53eaad3 100644 --- a/include/boost/beast/websocket/impl/ping.ipp +++ b/include/boost/beast/websocket/impl/ping.ipp @@ -44,7 +44,7 @@ class stream::ping_op token tok; state( - Handler&, + Handler const&, stream& ws_, detail::opcode op, ping_data const& payload) diff --git a/test/beast/core/handler_ptr.cpp b/test/beast/core/handler_ptr.cpp index 768239b2..1885de4b 100644 --- a/test/beast/core/handler_ptr.cpp +++ b/test/beast/core/handler_ptr.cpp @@ -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{}; }