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