diff options
Diffstat (limited to 'boost/beast/websocket/detail/pausation.hpp')
-rw-r--r-- | boost/beast/websocket/detail/pausation.hpp | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/boost/beast/websocket/detail/pausation.hpp b/boost/beast/websocket/detail/pausation.hpp new file mode 100644 index 0000000000..f51ee10327 --- /dev/null +++ b/boost/beast/websocket/detail/pausation.hpp @@ -0,0 +1,229 @@ +// +// 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_WEBSOCKET_DETAIL_PAUSATION_HPP +#define BOOST_BEAST_WEBSOCKET_DETAIL_PAUSATION_HPP + +#include <boost/beast/core/handler_ptr.hpp> +#include <boost/asio/associated_allocator.hpp> +#include <boost/asio/coroutine.hpp> +#include <boost/assert.hpp> +#include <array> +#include <memory> +#include <new> +#include <utility> + +namespace boost { +namespace beast { +namespace websocket { +namespace detail { + +// A container that holds a suspended, asynchronous composed +// operation. The contained object may be invoked later to +// resume the operation, or the container may be destroyed. +// +class pausation +{ + struct base + { + base() = default; + base(base &&) = delete; + base(base const&) = delete; + virtual ~base() = default; + virtual void operator()() = 0; + }; + + template<class F> + struct holder : base + { + F f; + + holder(holder&&) = default; + + template<class U> + explicit + holder(U&& u) + : f(std::forward<U>(u)) + { + } + + void + operator()() override + { + F f_(std::move(f)); + this->~holder(); + // invocation of f_() can + // assign a new object to *this. + f_(); + } + }; + + struct exemplar : boost::asio::coroutine + { + struct H + { + void operator()(); + }; + + struct T + { + using handler_type = H; + }; + + handler_ptr<T, H> hp; + + void operator()(); + }; + + template<class Op> + class saved_op + { + Op* op_ = nullptr; + + public: + ~saved_op() + { + if(op_) + { + Op op(std::move(*op_)); + op_->~Op(); + typename std::allocator_traits< + boost::asio::associated_allocator_t<Op>>:: + template rebind_alloc<Op> alloc{ + boost::asio::get_associated_allocator(op)}; + std::allocator_traits< + decltype(alloc)>::deallocate(alloc, op_, 1); + } + } + + saved_op(saved_op&& other) + : op_(other.op_) + { + other.op_ = nullptr; + } + + saved_op& operator=(saved_op&& other) + { + BOOST_ASSERT(! op_); + op_ = other.op_; + other.op_ = 0; + return *this; + } + + explicit + saved_op(Op&& op) + { + typename std::allocator_traits< + boost::asio::associated_allocator_t<Op>>:: + template rebind_alloc<Op> alloc{ + boost::asio::get_associated_allocator(op)}; + auto const p = std::allocator_traits< + decltype(alloc)>::allocate(alloc, 1); + op_ = new(p) Op{std::move(op)}; + } + + void + operator()() + { + BOOST_ASSERT(op_); + Op op{std::move(*op_)}; + typename std::allocator_traits< + boost::asio::associated_allocator_t<Op>>:: + template rebind_alloc<Op> alloc{ + boost::asio::get_associated_allocator(op)}; + std::allocator_traits< + decltype(alloc)>::deallocate(alloc, op_, 1); + op_ = nullptr; + op(); + } + }; + + using buf_type = char[sizeof(holder<exemplar>)]; + + base* base_ = nullptr; + alignas(holder<exemplar>) buf_type buf_; + +public: + pausation() = default; + pausation(pausation const&) = delete; + pausation& operator=(pausation const&) = delete; + + ~pausation() + { + if(base_) + base_->~base(); + } + + pausation(pausation&& other) + { + boost::ignore_unused(other); + BOOST_ASSERT(! other.base_); + } + + pausation& + operator=(pausation&& other) + { + boost::ignore_unused(other); + BOOST_ASSERT(! base_); + BOOST_ASSERT(! other.base_); + return *this; + } + + template<class F> + void + emplace(F&& f); + + template<class F> + void + save(F&& f); + + explicit + operator bool() const + { + return base_ != nullptr; + } + + bool + maybe_invoke() + { + if(base_) + { + auto const basep = base_; + base_ = nullptr; + (*basep)(); + return true; + } + return false; + } +}; + +template<class F> +void +pausation::emplace(F&& f) +{ + using type = holder<typename std::decay<F>::type>; + static_assert(sizeof(buf_type) >= sizeof(type), + "buffer too small"); + BOOST_ASSERT(! base_); + base_ = ::new(buf_) type{std::forward<F>(f)}; +} + +template<class F> +void +pausation::save(F&& f) +{ + emplace(saved_op<F>{std::move(f)}); +} + +} // detail +} // websocket +} // beast +} // boost + +#endif |