diff options
Diffstat (limited to 'src/common/async/bind_allocator.h')
-rw-r--r-- | src/common/async/bind_allocator.h | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/common/async/bind_allocator.h b/src/common/async/bind_allocator.h new file mode 100644 index 000000000..0e4b82f41 --- /dev/null +++ b/src/common/async/bind_allocator.h @@ -0,0 +1,244 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#pragma once + +// Based on bind_executor.h from Boost.Asio which is Copyright (c) +// 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/associated_executor.hpp> +#include <boost/asio/associated_allocator.hpp> +#include <boost/asio/async_result.hpp> +#include <boost/asio/execution_context.hpp> + +#include "include/uses_allocator.h" + +namespace ceph::async { +namespace detail { +template<typename T> +struct allocator_binder_check +{ + typedef void type; +}; + +// Helper to: +// - Apply the empty base optimization to the allocator. +// - Perform uses_allocator construction of the target type, if required. + +template<typename T, typename Allocator, bool UsesAllocator> +class allocator_binder_base; + +template<typename T, typename Allocator> +class allocator_binder_base<T, Allocator, true> + : protected Allocator +{ +protected: + template<typename A, typename U> + allocator_binder_base(A&& a, U&& u) + : Allocator(std::forward<A>(a), + target(ceph::make_obj_using_allocator(*this, + std::forward<U>(u)))) {} + + T target; +}; + +template <typename T, typename Allocator> +class allocator_binder_base<T, Allocator, false> + : protected Allocator + +{ +protected: + template<typename A, typename U> + allocator_binder_base(A&& a, U&& u) + : Allocator(std::forward<A>(a)), + target(std::forward<U>(u)) {} + + T target; +}; + +// Helper to enable SFINAE on zero-argument operator() below. + +template<typename T, typename = void> +struct allocator_binder_result_of0 +{ + using type = void; +}; + +template<typename T> +struct allocator_binder_result_of0< + T, typename allocator_binder_check<std::result_of_t<T()>>::type> +{ + using type = typename std::result_of_t<T()>; +}; +} // namespace detail + +/// A call wrapper type to bind an allocator of type @c Allocator to +/// an object of type @c T. +template<typename T, typename Allocator> +class allocator_binder + : private detail::allocator_binder_base<T, Allocator, + std::uses_allocator<T, Allocator> + ::value> +{ +public: + /// The type of the target object. + using target_type = T; + + /// The type of the associated allocator. + using allocator_type = Allocator; + + /// Construct an allocator wrapper for the specified object. + /** + * This constructor is only valid if the type @c T is constructible from type + * @c U. + */ + template<typename U> + allocator_binder(std::allocator_arg_t, const allocator_type& a, + U&& u) + : base_type(a, std::forward<U>(u)) {} + + /// Copy constructor. + allocator_binder(const allocator_binder& other) + : base_type(other.get_allocator(), other.get()) {} + + /// Construct a copy, but specify a different allocator. + allocator_binder(std::allocator_arg_t, const allocator_type& e, + const allocator_binder& other) + : base_type(e, other.get()) {} + + /// Construct a copy of a different allocator wrapper type. + /** + * This constructor is only valid if the @c Allocator type is + * constructible from type @c OtherAllocator, and the type @c T is + * constructible from type @c U. + */ + template<typename U, typename OtherAllocator> + allocator_binder(const allocator_binder<U, OtherAllocator>& other) + : base_type(other.get_allocator(), other.get()) {} + + /// Construct a copy of a different allocator wrapper type, but specify a + /// different allocator. + /** + * This constructor is only valid if the type @c T is constructible from type + * @c U. + */ + template<typename U, typename OtherAllocator> + allocator_binder(std::allocator_arg_t, const allocator_type& a, + const allocator_binder<U, OtherAllocator>& other) + : base_type(a, other.get()) {} + + /// Move constructor. + allocator_binder(allocator_binder&& other) + : base_type(std::move(other.get_allocator()), + std::move(other.get())) {} + + /// Move construct the target object, but specify a different allocator. + allocator_binder(std::allocator_arg_t, const allocator_type& e, + allocator_binder&& other) + : base_type(e, std::move(other.get())) {} + + /// Move construct from a different allocator wrapper type. + template<typename U, typename OtherAllocator> + allocator_binder(allocator_binder<U, OtherAllocator>&& other) + : base_type(std::move(other.get_allocator()), std::move(other.get())) {} + + /// Move construct from a different allocator wrapper type, but specify a + /// different allocator. + template<typename U, typename OtherAllocator> + allocator_binder(std::allocator_arg_t, const allocator_type& a, + allocator_binder<U, OtherAllocator>&& other) + : base_type(a, std::move(other.get())) {} + + /// Destructor. + ~allocator_binder() = default; + + /// Obtain a reference to the target object. + target_type& get() noexcept + { + return this->target; + } + + /// Obtain a reference to the target object. + const target_type& get() const noexcept + { + return this->target; + } + + /// Obtain the associated allocator. + allocator_type get_allocator() const noexcept { + return static_cast<const Allocator&>(*this); + } + + /// Forwarding function call operator. + template<typename... Args> + decltype(auto) operator()(Args&&... args) { + return this->target(std::forward<Args>(args)...); + } + + /// Forwarding function call operator. + template<typename... Args> + decltype(auto) operator()(Args&&... args) const { + return this->target(std::forward<Args>(args)...); + } + + +private: + using base_type = + detail::allocator_binder_base<T, Allocator, + std::uses_allocator_v<T, Allocator>>; +}; + +/// Associate an object of type @c T with an allocator of type @c Allocator. +template<typename Allocator, typename T> +inline allocator_binder<typename std::decay_t<T>, Allocator> +bind_allocator(const Allocator& a, T&& t) +{ + return allocator_binder<std::decay_t<T>, Allocator>(std::allocator_arg_t(), + a, std::forward<T>(t)); +} +} // namespace ceph::async + +// Since we have an allocator_type member we shouldn't need a +// uses_allocator specialization. + +namespace boost::asio { +template<typename T, typename Allocator, typename Signature> +class async_result<ceph::async::allocator_binder<T, Allocator>, Signature> +{ +public: + using completion_handler_type = + ceph::async::allocator_binder< + typename async_result<T, Signature>::completion_handler_type, Allocator>; + + using return_type = typename async_result<T, Signature>::return_type; + + explicit async_result(ceph::async::allocator_binder<T, Allocator>& b) + : target(b.get()) {} + + return_type get() { + return target.get(); + } + +private: + async_result(const async_result&) = delete; + async_result& operator=(const async_result&) = delete; + + async_result<T, Signature> target; +}; + +template<typename T, typename Allocator, typename Executor> +struct associated_executor<ceph::async::allocator_binder<T, Allocator>, + Executor> +{ + using type = typename associated_executor<T, Executor>::type; + + static type get(const ceph::async::allocator_binder<T, Allocator>& b, + Executor ex = {}) noexcept { + return get_associated_executor(b.get(), ex); + } +}; +} // namespace boost::asio |