1 // Copyright 2021 The gRPC Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 #ifndef GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H 15 #define GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H 16 17 #include <stdlib.h> // for abort() 18 19 #include <algorithm> 20 #include <memory> 21 #include <type_traits> 22 #include <vector> 23 24 #include <grpc/event_engine/internal/memory_allocator_impl.h> 25 #include <grpc/slice.h> 26 #include <grpc/support/port_platform.h> 27 28 namespace grpc_event_engine { 29 namespace experimental { 30 31 // Tracks memory allocated by one system. 32 // Is effectively a thin wrapper/smart pointer for a MemoryAllocatorImpl, 33 // providing a convenient and stable API. 34 class MemoryAllocator { 35 public: 36 /// Construct a MemoryAllocator given an internal::MemoryAllocatorImpl 37 /// implementation. The constructed MemoryAllocator will call 38 /// MemoryAllocatorImpl::Shutdown() upon destruction. MemoryAllocator(std::shared_ptr<internal::MemoryAllocatorImpl> allocator)39 explicit MemoryAllocator( 40 std::shared_ptr<internal::MemoryAllocatorImpl> allocator) 41 : allocator_(std::move(allocator)) {} 42 // Construct an invalid MemoryAllocator. MemoryAllocator()43 MemoryAllocator() : allocator_(nullptr) {} ~MemoryAllocator()44 ~MemoryAllocator() { 45 if (allocator_ != nullptr) allocator_->Shutdown(); 46 } 47 48 MemoryAllocator(const MemoryAllocator&) = delete; 49 MemoryAllocator& operator=(const MemoryAllocator&) = delete; 50 51 MemoryAllocator(MemoryAllocator&&) = default; 52 MemoryAllocator& operator=(MemoryAllocator&&) = default; 53 54 /// Drop the underlying allocator and make this an empty object. 55 /// The object will not be usable after this call unless it's a valid 56 /// allocator is moved into it. Reset()57 void Reset() { 58 auto a = std::move(allocator_); 59 if (a != nullptr) a->Shutdown(); 60 } 61 62 /// Reserve bytes from the quota. 63 /// If we enter overcommit, reclamation will begin concurrently. 64 /// Returns the number of bytes reserved. Reserve(MemoryRequest request)65 size_t Reserve(MemoryRequest request) { return allocator_->Reserve(request); } 66 67 /// Release some bytes that were previously reserved. Release(size_t n)68 void Release(size_t n) { return allocator_->Release(n); } 69 70 // 71 // The remainder of this type are helper functions implemented in terms of 72 // Reserve/Release. 73 // 74 75 /// An automatic releasing reservation of memory. 76 class Reservation { 77 public: 78 Reservation() = default; 79 Reservation(const Reservation&) = delete; 80 Reservation& operator=(const Reservation&) = delete; 81 Reservation(Reservation&&) = default; 82 Reservation& operator=(Reservation&&) = default; ~Reservation()83 ~Reservation() { 84 if (allocator_ != nullptr) allocator_->Release(size_); 85 } 86 87 private: 88 friend class MemoryAllocator; Reservation(std::shared_ptr<internal::MemoryAllocatorImpl> allocator,size_t size)89 Reservation(std::shared_ptr<internal::MemoryAllocatorImpl> allocator, 90 size_t size) 91 : allocator_(std::move(allocator)), size_(size) {} 92 93 std::shared_ptr<internal::MemoryAllocatorImpl> allocator_; 94 size_t size_ = 0; 95 }; 96 97 /// Reserve bytes from the quota and automatically release them when 98 /// Reservation is destroyed. MakeReservation(MemoryRequest request)99 Reservation MakeReservation(MemoryRequest request) { 100 return Reservation(allocator_, Reserve(request)); 101 } 102 103 /// Allocate a new object of type T, with constructor arguments. 104 /// The returned type is wrapped, and upon destruction the reserved memory 105 /// will be released to the allocator automatically. As such, T must have a 106 /// virtual destructor so we can insert the necessary hook. 107 template <typename T, typename... Args> New(Args &&...args)108 typename std::enable_if<std::has_virtual_destructor<T>::value, T*>::type New( 109 Args&&... args) { 110 // Wrap T such that when it's destroyed, we can release memory back to the 111 // allocator. 112 class Wrapper final : public T { 113 public: 114 explicit Wrapper(std::shared_ptr<internal::MemoryAllocatorImpl> allocator, 115 Args&&... args) 116 : T(std::forward<Args>(args)...), allocator_(std::move(allocator)) {} 117 ~Wrapper() override { allocator_->Release(sizeof(*this)); } 118 119 private: 120 const std::shared_ptr<internal::MemoryAllocatorImpl> allocator_; 121 }; 122 Reserve(sizeof(Wrapper)); 123 return new Wrapper(allocator_, std::forward<Args>(args)...); 124 } 125 126 /// Construct a unique_ptr immediately. 127 template <typename T, typename... Args> MakeUnique(Args &&...args)128 std::unique_ptr<T> MakeUnique(Args&&... args) { 129 return std::unique_ptr<T>(New<T>(std::forward<Args>(args)...)); 130 } 131 132 /// Allocate a slice, using MemoryRequest to size the number of returned 133 /// bytes. For a variable length request, check the returned slice length to 134 /// verify how much memory was allocated. Takes care of reserving memory for 135 /// any relevant control structures also. MakeSlice(MemoryRequest request)136 grpc_slice MakeSlice(MemoryRequest request) { 137 return allocator_->MakeSlice(request); 138 } 139 140 /// A C++ allocator for containers of T. 141 template <typename T> 142 class Container { 143 public: 144 using value_type = T; 145 146 /// Construct the allocator: \a underlying_allocator is borrowed, and must 147 /// outlive this object. Container(MemoryAllocator * underlying_allocator)148 explicit Container(MemoryAllocator* underlying_allocator) 149 : underlying_allocator_(underlying_allocator) {} 150 template <typename U> Container(const Container<U> & other)151 explicit Container(const Container<U>& other) 152 : underlying_allocator_(other.underlying_allocator()) {} 153 underlying_allocator()154 MemoryAllocator* underlying_allocator() const { 155 return underlying_allocator_; 156 } 157 allocate(size_t n)158 T* allocate(size_t n) { 159 underlying_allocator_->Reserve(n * sizeof(T)); 160 return static_cast<T*>(::operator new(n * sizeof(T))); 161 } deallocate(T * p,size_t n)162 void deallocate(T* p, size_t n) { 163 ::operator delete(p); 164 underlying_allocator_->Release(n * sizeof(T)); 165 } 166 167 private: 168 MemoryAllocator* underlying_allocator_; 169 }; 170 171 protected: 172 /// Return a pointer to the underlying implementation. 173 /// Note that the interface of said implementation is unstable and likely to 174 /// change at any time. get_internal_impl_ptr()175 internal::MemoryAllocatorImpl* get_internal_impl_ptr() { 176 return allocator_.get(); 177 } 178 get_internal_impl_ptr()179 const internal::MemoryAllocatorImpl* get_internal_impl_ptr() const { 180 return allocator_.get(); 181 } 182 183 private: 184 std::shared_ptr<internal::MemoryAllocatorImpl> allocator_; 185 }; 186 187 // Wrapper type around std::vector to make initialization against a 188 // MemoryAllocator based container allocator easy. 189 template <typename T> 190 class Vector : public std::vector<T, MemoryAllocator::Container<T>> { 191 public: Vector(MemoryAllocator * allocator)192 explicit Vector(MemoryAllocator* allocator) 193 : std::vector<T, MemoryAllocator::Container<T>>( 194 MemoryAllocator::Container<T>(allocator)) {} 195 }; 196 197 class MemoryAllocatorFactory { 198 public: 199 virtual ~MemoryAllocatorFactory() = default; 200 /// On Endpoint creation, call \a CreateMemoryAllocator to create a new 201 /// allocator for the endpoint. 202 /// \a name is used to label the memory allocator in debug logs. 203 /// Typically we'll want to: 204 /// auto allocator = factory->CreateMemoryAllocator(peer_address_string); 205 /// auto* endpoint = allocator->New<MyEndpoint>(std::move(allocator), ...); 206 virtual MemoryAllocator CreateMemoryAllocator(absl::string_view name) = 0; 207 }; 208 209 } // namespace experimental 210 } // namespace grpc_event_engine 211 212 #endif // GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H 213