saved_handler is a public interface:

This container allows completion handlers to be safely
suspended and resumed later, or destroyed.
This commit is contained in:
Vinnie Falco
2019-01-12 18:42:32 -08:00
parent be800436ea
commit 3092e43879
9 changed files with 486 additions and 2 deletions
@@ -0,0 +1,193 @@
//
// Copyright (c) 2016-2017 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_SAVED_HANDLER_HPP
#define BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_HPP
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/assert.hpp>
#include <boost/core/empty_value.hpp>
#include <boost/core/exchange.hpp>
#include <memory>
#include <utility>
namespace boost {
namespace beast {
//------------------------------------------------------------------------------
class saved_handler::base
{
protected:
~base() = default;
public:
base() = default;
virtual void destroy() = 0;
virtual void invoke() = 0;
};
//------------------------------------------------------------------------------
template<class Handler, class Alloc>
class saved_handler::impl final : public base
{
using alloc_type = typename
beast::detail::allocator_traits<
Alloc>::template rebind_alloc<impl>;
using alloc_traits =
beast::detail::allocator_traits<alloc_type>;
struct ebo_pair : boost::empty_value<alloc_type>
{
Handler h;
template<class Handler_>
ebo_pair(
alloc_type const& a,
Handler_&& h_)
: boost::empty_value<alloc_type>(
boost::empty_init_t{}, a)
, h(std::forward<Handler_>(h_))
{
}
};
ebo_pair v_;
net::executor_work_guard<
net::associated_executor_t<Handler>> wg_;
public:
template<class Handler_>
impl(alloc_type const& a, Handler_&& h)
: v_(a, std::forward<Handler_>(h))
, wg_(net::get_associated_executor(v_.h))
{
}
void
destroy() override
{
auto v = std::move(v_);
alloc_traits::destroy(v.get(), this);
alloc_traits::deallocate(v.get(), this, 1);
}
void
invoke() override
{
auto v = std::move(v_);
alloc_traits::destroy(v.get(), this);
alloc_traits::deallocate(v.get(), this, 1);
v.h();
}
};
//------------------------------------------------------------------------------
saved_handler::
~saved_handler()
{
if(p_)
p_->destroy();
}
saved_handler::
saved_handler(saved_handler&& other) noexcept
: p_(boost::exchange(other.p_, nullptr))
{
}
saved_handler&
saved_handler::
operator=(saved_handler&& other) noexcept
{
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
p_ = boost::exchange(other.p_, nullptr);
return *this;
}
template<class Handler, class Allocator>
void
saved_handler::
emplace(Handler&& handler, Allocator const& alloc)
{
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
using alloc_type = typename
beast::detail::allocator_traits<Allocator>::
template rebind_alloc<impl<Handler, Allocator>>;
using alloc_traits =
beast::detail::allocator_traits<alloc_type>;
struct storage
{
alloc_type a;
impl<Handler, Allocator>* p;
explicit
storage(Allocator const& a_)
: a(a_)
, p(alloc_traits::allocate(a, 1))
{
}
~storage()
{
if(p)
alloc_traits::deallocate(a, p, 1);
}
};
storage s(alloc);
alloc_traits::construct(s.a, s.p,
s.a, std::forward<Handler>(handler));
p_ = boost::exchange(s.p, nullptr);
}
template<class Handler>
void
saved_handler::
emplace(Handler&& handler)
{
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
emplace(
std::forward<Handler>(handler),
net::get_associated_allocator(handler));
}
void
saved_handler::
invoke()
{
// Can't invoke without a value
BOOST_ASSERT(has_value());
boost::exchange(
p_, nullptr)->invoke();
}
bool
saved_handler::
maybe_invoke()
{
if(! p_)
return false;
boost::exchange(
p_, nullptr)->invoke();
return true;
}
} // beast
} // boost
#endif
+122
View File
@@ -0,0 +1,122 @@
//
// Copyright (c) 2016-2017 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_SAVED_HANDLER_HPP
#define BOOST_BEAST_CORE_SAVED_HANDLER_HPP
#include <boost/beast/core/detail/config.hpp>
namespace boost {
namespace beast {
/** An invocable, nullary function object which holds a completion handler.
This container can hold a type-erased instance of any completion
handler, or it can be empty. When the container holds a value,
the implementation maintains an instance of `net::executor_work_guard`
for the handler's associated executor. Memory is dynamically allocated
to store the completion handler, and the allocator may optionally
be specified. Otherwise, the implementation uses the handler's
associated allocator.
*/
class saved_handler
{
class base;
template<class, class>
class impl;
base* p_ = nullptr;
public:
/// Default Constructor
saved_handler() = default;
/// Copy Constructor (deleted)
saved_handler(saved_handler const&) = delete;
/// Copy Assignment (deleted)
saved_handler& operator=(saved_handler const&) = delete;
/// Destructor
BOOST_BEAST_DECL
~saved_handler();
/// Move Constructor
BOOST_BEAST_DECL
saved_handler(saved_handler&& other) noexcept;
/// Move Assignment
BOOST_BEAST_DECL
saved_handler&
operator=(saved_handler&& other) noexcept;
/// Returns `true` if `*this` contains a completion handler.
bool
has_value() const noexcept
{
return p_ != nullptr;
}
/** Store a completion handler in the container.
Requires `this->has_value() == false`.
@param handler The completion handler to store.
@param alloc The allocator to use.
*/
template<class Handler, class Allocator>
void
emplace(Handler&& handler, Allocator const& alloc);
/** Store a completion handler in the container.
Requires `this->has_value() == false`. The
implementation will use the handler's associated
allocator to obtian storage.
@param handler The completion handler to store.
*/
template<class Handler>
void
emplace(Handler&& handler);
/** Unconditionally invoke the stored completion handler.
Requires `this->has_value() == true`. Any dynamic memory
used is deallocated before the stored completion handler
is invoked. The executor work guard is also reset before
the invocation.
*/
BOOST_BEAST_DECL
void
invoke();
/** Conditionally invoke the stored completion handler.
Invokes the stored completion handler if
`this->has_value() == true`, otherwise does nothing. Any
dynamic memory used is deallocated before the stored completion
handler is invoked. The executor work guard is also reset before
the invocation.
@return `true` if the invocation took place.
*/
BOOST_BEAST_DECL
bool
maybe_invoke();
};
} // beast
} // boost
#include <boost/beast/core/impl/saved_handler.hpp>
#endif