From 1d7d7445d5999e97525b4ca44e92862802cd2ad8 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 26 Jun 2017 19:11:45 -0700 Subject: [PATCH] Add session_alloc to example/common --- CHANGELOG.md | 2 +- example/common/session_alloc.hpp | 477 +++++++++++++++++++++++++++++++ test/common/CMakeLists.txt | 1 + test/common/Jamfile | 1 + test/common/session_alloc.cpp | 10 + 5 files changed, 490 insertions(+), 1 deletion(-) create mode 100644 example/common/session_alloc.hpp create mode 100644 test/common/session_alloc.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3f4179..cbb24e3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ * Documentation tidying * is_invocable works with move-only types * Use std::function and reference wrapper +* Add session_alloc to example/common WebSocket @@ -204,7 +205,6 @@ WebSocket: * Add websocket-server-async example - -------------------------------------------------------------------------------- Version 71: diff --git a/example/common/session_alloc.hpp b/example/common/session_alloc.hpp new file mode 100644 index 00000000..cb1df6a1 --- /dev/null +++ b/example/common/session_alloc.hpp @@ -0,0 +1,477 @@ +// +// Copyright (c) 2013-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) +// + +#ifndef BEAST_EXAMPLE_COMMON_SESSION_ALLOC_HPP +#define BEAST_EXAMPLE_COMMON_SESSION_ALLOC_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#if 1 +#include "extras/beast/unit_test/dstream.hpp" +#endif + +template +class session_alloc_base +{ + template + class wrapped_handler; + + class pool_t + { + using hook_type = + boost::intrusive::list_base_hook< + boost::intrusive::link_mode< + boost::intrusive::normal_link>>; + + class element : public hook_type + { + std::size_t size_; + std::size_t used_; + // add padding here + + public: + explicit + element(std::size_t size, std::size_t used) + : size_(size) + , used_(used) + { + } + + std::size_t + size() const + { + return size_; + } + + std::size_t + used() const + { + return used_; + } + + char* + end() const + { + return data() + size_; + } + + char* + data() const + { + return const_cast( + reinterpret_cast< + char const *>(this + 1)); + } + }; + + using list_type = typename + boost::intrusive::make_list>::type; + + Context* ctx_; + std::size_t refs_ = 1; // shared count + std::size_t high_ = 0; // highest used + std::size_t size_ = 0; // size of buf_ + char* buf_ = nullptr; // a large block + list_type list_; // list of allocations + + explicit + pool_t(Context* ctx) + : ctx_(ctx) + { + } + + public: + static + pool_t& + construct(Context* ctx); + + ~pool_t(); + + pool_t& + addref(); + + void + release(); + + void* + alloc(std::size_t n); + + void + dealloc(void* pv, std::size_t n); + }; + + pool_t& pool_; + +public: + session_alloc_base& operator=(session_alloc_base const&) = delete; + + ~session_alloc_base() + { + pool_.release(); + } + + session_alloc_base() + : pool_(pool_t::construct(nullptr)) + { + static_assert(std::is_same::value, + "Context requirements not met"); + } + + session_alloc_base(session_alloc_base const& other) + : pool_(other.pool_.addref()) + { + } + + template, + typename std::decay::type + >::value>::type> + explicit + session_alloc_base(DeducedContext& ctx) + : pool_(pool_t::construct(std::addressof(ctx))) + { + static_assert(! std::is_same::value, + "Context requirements not met"); + } + + template + wrapped_handler::type> + wrap(Handler&& handler) + { + return wrapped_handler< + typename std::decay::type>( + std::forward(handler), *this); + } + +protected: + void* + alloc(std::size_t n) + { + return pool_.alloc(n); + } + + void + dealloc(void* p, std::size_t n) + { + pool_.dealloc(p, n); + } +}; + +//------------------------------------------------------------------------------ + +template +class session_alloc : public session_alloc_base +{ + template + friend class session_alloc; + +public: + using value_type = T; + using is_always_equal = std::false_type; + using pointer = T*; + using reference = T&; + using const_pointer = T const*; + using const_reference = T const&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + template + struct rebind + { + using other = session_alloc; + }; + + session_alloc() = default; + session_alloc(session_alloc const&) = default; + + template + session_alloc(session_alloc const& other) + : session_alloc_base(static_cast< + session_alloc_base const&>(other)) + { + } + + explicit + session_alloc(Context& ctx) + : session_alloc_base(ctx) + { + } + + value_type* + allocate(size_type n) + { + return static_cast( + this->alloc(n * sizeof(T))); + } + + void + deallocate(value_type* p, size_type n) + { + this->dealloc(p, n * sizeof(T)); + } + +#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000 + template + void + construct(U* ptr, Args&&... args) + { + ::new((void*)ptr) U(std::forward(args)...); + } + + template + void + destroy(U* ptr) + { + ptr->~U(); + } +#endif + + template + friend + bool + operator==( + session_alloc const& lhs, + session_alloc const& rhs) + { + return &lhs.pool_ == &rhs.pool_; + } + + template + friend + bool + operator!=( + session_alloc const& lhs, + session_alloc const& rhs) + { + return ! (lhs == rhs); + } +}; + +//------------------------------------------------------------------------------ + +template +template +class session_alloc_base::wrapped_handler +{ + Handler h_; + session_alloc_base alloc_; + + void* + alloc(std::size_t size) + { + return alloc_.alloc(size); + } + + void + dealloc(void* p, std::size_t size) + { + alloc_.dealloc(p, size); + } + +public: + wrapped_handler(wrapped_handler&&) = default; + wrapped_handler(wrapped_handler const&) = default; + + template + explicit + wrapped_handler(DeducedHandler&& h, + session_alloc_base const& alloc) + : h_(std::forward(h)) + , alloc_(alloc) + { + } + + template + void + operator()(Args&&... args) const + { + h_(std::forward(args)...); + } + + friend + void* + asio_handler_allocate( + std::size_t size, wrapped_handler* w) + { + return w->alloc(size); + } + + friend + void + asio_handler_deallocate( + void* p, std::size_t size, wrapped_handler* w) + { + w->dealloc(p, size); + } + + friend + bool + asio_handler_is_continuation(wrapped_handler* w) + { + using boost::asio::asio_handler_is_continuation; + return asio_handler_is_continuation(std::addressof(w->h_)); + } + + template + friend + void + asio_handler_invoke(F&& f, wrapped_handler* w) + { + using boost::asio::asio_handler_invoke; + asio_handler_invoke( + f, std::addressof(w->h_)); + } +}; + +//------------------------------------------------------------------------------ + +template +auto +session_alloc_base:: +pool_t:: +construct(Context* ctx) -> + pool_t& +{ + using boost::asio::asio_handler_allocate; + return *new(asio_handler_allocate( + sizeof(pool_t), ctx)) pool_t{ctx}; +} + +template +session_alloc_base:: +pool_t:: +~pool_t() +{ + BOOST_ASSERT(list_.size() == 0); + if(buf_) + { + using boost::asio::asio_handler_deallocate; + asio_handler_deallocate(buf_, size_, ctx_); + } +} + +template +auto +session_alloc_base:: +pool_t:: +addref() -> + pool_t& +{ + ++refs_; + return *this; +} + +template +void +session_alloc_base:: +pool_t:: +release() +{ + if(--refs_) + return; + this->~pool_t(); + using boost::asio::asio_handler_deallocate; + asio_handler_deallocate(this, sizeof(*this), ctx_); +} + +template +void* +session_alloc_base:: +pool_t:: +alloc(std::size_t n) +{ +#if 0 +beast::unit_test::dstream dout{std::cout}; +dout << +"n=" << n << ", " +"list_.size()=" << list_.size() << ", " +"used=" << (list_.empty() ? 0 : list_.back().used()) << ", " +"size_=" << size_ << ", " +"high_=" << high_ << +std::endl; +#endif + if(list_.empty() && size_ < high_) + { + if(buf_) + { + using boost::asio::asio_handler_deallocate; + asio_handler_deallocate(buf_, size_, ctx_); + } + using boost::asio::asio_handler_allocate; + buf_ = reinterpret_cast( + asio_handler_allocate(high_, ctx_)); + size_ = high_; + } + if(buf_) + { + char* end; + std::size_t used; + if(list_.empty()) + { + end = buf_; + used = sizeof(element) + n; + } + else + { + end = list_.back().end(); + used = list_.back().used() + + sizeof(element) + n; + } + if(end >= buf_ && end + + sizeof(element) + n <= buf_ + size_) + { + auto& e = *new(end) element{n, used}; + list_.push_back(e); + high_ = (std::max)(high_, used); + return e.data(); + } + } + std::size_t const used = + sizeof(element) + n + ( + buf_ && ! list_.empty() ? + list_.back().used() : 0); + using boost::asio::asio_handler_allocate; + auto& e = *new(asio_handler_allocate( + sizeof(element) + n, ctx_)) element{n, used}; + list_.push_back(e); + high_ = (std::max)(high_, used); + return e.data(); +} + +template +void +session_alloc_base:: +pool_t:: +dealloc(void* pv, std::size_t n) +{ + auto& e = *(reinterpret_cast(pv) - 1); + BOOST_ASSERT(e.size() == n); + if( (e.end() > buf_ + size_) || + reinterpret_cast(&e) < buf_) + { + list_.erase(list_.iterator_to(e)); + e.~element(); + using boost::asio::asio_handler_deallocate; + asio_handler_deallocate( + &e, sizeof(e) + n, ctx_); + return; + } + list_.erase(list_.iterator_to(e)); + e.~element(); +} + +#endif diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index 651732cb..d9fb4d02 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable (common-test detect_ssl.cpp mime_types.cpp rfc7231.cpp + session_alloc.cpp ssl_stream.cpp write_msg.cpp main.cpp diff --git a/test/common/Jamfile b/test/common/Jamfile index 282b842b..3668a464 100644 --- a/test/common/Jamfile +++ b/test/common/Jamfile @@ -9,6 +9,7 @@ exe common-test : detect_ssl.cpp mime_types.cpp rfc7231.cpp + session_alloc.cpp ssl_stream.cpp write_msg.cpp main.cpp diff --git a/test/common/session_alloc.cpp b/test/common/session_alloc.cpp new file mode 100644 index 00000000..4ff359d5 --- /dev/null +++ b/test/common/session_alloc.cpp @@ -0,0 +1,10 @@ +// +// Copyright (c) 2013-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) +// + +// Test that header file is self-contained. +#include "example/common/session_alloc.hpp" +