// // 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_EXAMPLE_COMMON_SESSION_ALLOC_HPP #define BOOST_BEAST_EXAMPLE_COMMON_SESSION_ALLOC_HPP #include #include #include #include #include #include #include #include #include namespace detail { template class session_alloc_base { protected: 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; 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 pool_t() = default; public: static pool_t& construct(); ~pool_t(); pool_t& addref(); void release(); void* alloc(std::size_t n); void dealloc(void* pv, std::size_t n); }; }; template auto session_alloc_base:: pool_t:: construct() -> pool_t& { return *(new pool_t); } template session_alloc_base:: pool_t:: ~pool_t() { BOOST_ASSERT(list_.size() == 0); if(buf_) delete[] buf_; } template auto session_alloc_base:: pool_t:: addref() -> pool_t& { ++refs_; return *this; } template void session_alloc_base:: pool_t:: release() { if(--refs_) return; delete this; } template void* session_alloc_base:: pool_t:: alloc(std::size_t n) { if(list_.empty() && size_ < high_) { if(buf_) delete[] buf_; buf_ = new char[high_]; 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); auto& e = *new(new char[sizeof(element) + n]) 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) { boost::ignore_unused(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(); delete[] reinterpret_cast(&e); return; } list_.erase(list_.iterator_to(e)); e.~element(); } } // detail //------------------------------------------------------------------------------ namespace detail { template class session_alloc_wrapper; } // detail template class session_alloc : private detail::session_alloc_base { template friend class session_alloc; using pool_t = typename detail::session_alloc_base::pool_t; pool_t& pool_; 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& operator=(session_alloc const&) = delete; ~session_alloc() { pool_.release(); } session_alloc() : pool_(pool_t::construct()) { } session_alloc(session_alloc const& other) noexcept : pool_(other.pool_.addref()) { } template session_alloc(session_alloc const& other) noexcept : pool_(other.pool_) { } template detail::session_alloc_wrapper::type> wrap(Handler&& handler); 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); } protected: void* alloc(std::size_t n) { return pool_.alloc(n); } void dealloc(void* p, std::size_t n) { pool_.dealloc(p, n); } }; //------------------------------------------------------------------------------ namespace detail { template class session_alloc_wrapper { // Can't friend partial specializations, // so we just friend the whole thing. template friend struct boost::asio::associated_executor; Handler h_; session_alloc alloc_; public: session_alloc_wrapper(session_alloc_wrapper&&) = default; session_alloc_wrapper(session_alloc_wrapper const&) = default; template session_alloc_wrapper( DeducedHandler&& h, session_alloc const& alloc) : h_(std::forward(h)) , alloc_(alloc) { } using allocator_type = session_alloc; allocator_type get_allocator() const noexcept; template void operator()(Args&&... args) const { h_(std::forward(args)...); } friend bool asio_handler_is_continuation(session_alloc_wrapper* w) { using boost::asio::asio_handler_is_continuation; return asio_handler_is_continuation(std::addressof(w->h_)); } }; template auto session_alloc_wrapper:: get_allocator() const noexcept -> allocator_type { return alloc_; } } // detail //------------------------------------------------------------------------------ template template auto session_alloc:: wrap(Handler&& handler) -> detail::session_alloc_wrapper::type> { return detail::session_alloc_wrapper< typename std::decay::type>( std::forward(handler), *this); } //------------------------------------------------------------------------------ namespace boost { namespace asio { template struct associated_executor< ::detail::session_alloc_wrapper, Executor> { using type = typename associated_executor::type; static type get(::detail::session_alloc_wrapper const& h, Executor const& ex = Executor()) noexcept { return associated_executor< Handler, Executor>::get(h.h_, ex); } }; } // asio } // boost #endif