mirror of https://github.com/grpc/grpc.git
Integrate new resource quota, event engine (#27522)
* Integrate new resource quota, event engine * Automated change: Fix sanity tests * windows fix * Automated change: Fix sanity tests * Update memory_allocator.h * Automated change: Fix sanity tests * first round review feedback * review feedback * Automated change: Fix sanity tests * Update memory_quota.h * get re-export formatted right * Automated change: Fix sanity tests * Update memory_allocator.cc * Automated change: Fix sanity tests * MemoryOwner has-a MemoryAllocator * using fix * review feedback * Automated change: Fix sanity tests Co-authored-by: ctiller <ctiller@users.noreply.github.com>pull/27643/head
parent
3d83dd3776
commit
79ef60f079
30 changed files with 737 additions and 649 deletions
@ -0,0 +1,98 @@ |
||||
// Copyright 2021 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef GRPC_EVENT_ENGINE_INTERNAL_MEMORY_ALLOCATOR_IMPL_H |
||||
#define GRPC_EVENT_ENGINE_INTERNAL_MEMORY_ALLOCATOR_IMPL_H |
||||
|
||||
#include <grpc/impl/codegen/port_platform.h> |
||||
|
||||
#include <algorithm> |
||||
#include <memory> |
||||
#include <type_traits> |
||||
#include <vector> |
||||
|
||||
#include <grpc/slice.h> |
||||
|
||||
// forward-declaring an internal struct, not used publicly.
|
||||
struct grpc_slice_buffer; |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
/// Reservation request - how much memory do we want to allocate?
|
||||
class MemoryRequest { |
||||
public: |
||||
/// Request a fixed amount of memory.
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
MemoryRequest(size_t n) : min_(n), max_(n) {} |
||||
/// Request a range of memory.
|
||||
/// Requires: \a min <= \a max.
|
||||
/// Requires: \a max <= max_size()
|
||||
MemoryRequest(size_t min, size_t max) : min_(min), max_(max) {} |
||||
|
||||
/// Maximum allowable request size - hard coded to 1GB.
|
||||
static constexpr size_t max_allowed_size() { return 1024 * 1024 * 1024; } |
||||
|
||||
/// Increase the size by \a amount.
|
||||
/// Undefined behavior if min() + amount or max() + amount overflows.
|
||||
MemoryRequest Increase(size_t amount) const { |
||||
return MemoryRequest(min_ + amount, max_ + amount); |
||||
} |
||||
|
||||
size_t min() const { return min_; } |
||||
size_t max() const { return max_; } |
||||
|
||||
private: |
||||
size_t min_; |
||||
size_t max_; |
||||
}; |
||||
|
||||
namespace internal { |
||||
|
||||
/// Underlying memory allocation interface.
|
||||
/// This is an internal interface, not intended to be used by users.
|
||||
/// Its interface is subject to change at any time.
|
||||
class MemoryAllocatorImpl |
||||
: public std::enable_shared_from_this<MemoryAllocatorImpl> { |
||||
public: |
||||
MemoryAllocatorImpl() {} |
||||
virtual ~MemoryAllocatorImpl() {} |
||||
|
||||
MemoryAllocatorImpl(const MemoryAllocatorImpl&) = delete; |
||||
MemoryAllocatorImpl& operator=(const MemoryAllocatorImpl&) = delete; |
||||
|
||||
/// Reserve bytes from the quota.
|
||||
/// If we enter overcommit, reclamation will begin concurrently.
|
||||
/// Returns the number of bytes reserved.
|
||||
/// If MemoryRequest is invalid, this function will abort.
|
||||
/// If MemoryRequest is valid, this function is infallible, and will always
|
||||
/// succeed at reserving the some number of bytes between request.min() and
|
||||
/// request.max() inclusively.
|
||||
virtual size_t Reserve(MemoryRequest request) = 0; |
||||
|
||||
/// Release some bytes that were previously reserved.
|
||||
/// If more bytes are released than were reserved, we will have undefined
|
||||
/// behavior.
|
||||
virtual void Release(size_t n) = 0; |
||||
|
||||
/// Shutdown this allocator.
|
||||
/// Further usage of Reserve() is undefined behavior.
|
||||
virtual void Shutdown() = 0; |
||||
}; |
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
||||
|
||||
#endif // GRPC_EVENT_ENGINE_INTERNAL_MEMORY_ALLOCATOR_IMPL_H
|
@ -0,0 +1,210 @@ |
||||
// Copyright 2021 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H |
||||
#define GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H |
||||
|
||||
#include <grpc/impl/codegen/port_platform.h> |
||||
|
||||
#include <algorithm> |
||||
#include <memory> |
||||
#include <type_traits> |
||||
#include <vector> |
||||
|
||||
#include <grpc/event_engine/internal/memory_allocator_impl.h> |
||||
#include <grpc/slice.h> |
||||
|
||||
// forward-declaring an internal struct, not used publicly.
|
||||
struct grpc_slice_buffer; |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
// TODO(nnoble): needs implementation
|
||||
class SliceBuffer { |
||||
public: |
||||
SliceBuffer() { abort(); } |
||||
explicit SliceBuffer(grpc_slice_buffer*) { abort(); } |
||||
|
||||
grpc_slice_buffer* RawSliceBuffer() { return slice_buffer_; } |
||||
|
||||
private: |
||||
grpc_slice_buffer* slice_buffer_; |
||||
}; |
||||
|
||||
class MemoryAllocator { |
||||
public: |
||||
/// Construct a MemoryAllocator given an internal::MemoryAllocatorImpl
|
||||
/// implementation. The constructed MemoryAllocator will call
|
||||
/// MemoryAllocatorImpl::Shutdown() upon destruction.
|
||||
explicit MemoryAllocator( |
||||
std::shared_ptr<internal::MemoryAllocatorImpl> allocator) |
||||
: allocator_(std::move(allocator)) {} |
||||
~MemoryAllocator() { |
||||
if (allocator_ != nullptr) allocator_->Shutdown(); |
||||
} |
||||
|
||||
MemoryAllocator(const MemoryAllocator&) = delete; |
||||
MemoryAllocator& operator=(const MemoryAllocator&) = delete; |
||||
|
||||
MemoryAllocator(MemoryAllocator&&) = default; |
||||
MemoryAllocator& operator=(MemoryAllocator&&) = default; |
||||
|
||||
/// Reserve bytes from the quota.
|
||||
/// If we enter overcommit, reclamation will begin concurrently.
|
||||
/// Returns the number of bytes reserved.
|
||||
size_t Reserve(MemoryRequest request) { return allocator_->Reserve(request); } |
||||
|
||||
/// Release some bytes that were previously reserved.
|
||||
void Release(size_t n) { return allocator_->Release(n); } |
||||
|
||||
/// Return a pointer to the underlying implementation.
|
||||
/// Note that the interface of said implementatoin is unstable and likely to
|
||||
/// change at any time.
|
||||
internal::MemoryAllocatorImpl* get_internal_impl_ptr() { |
||||
return allocator_.get(); |
||||
} |
||||
|
||||
//
|
||||
// The remainder of this type are helper functions implemented in terms of
|
||||
// Reserve/Release.
|
||||
//
|
||||
|
||||
/// An automatic releasing reservation of memory.
|
||||
class Reservation { |
||||
public: |
||||
Reservation() = default; |
||||
Reservation(const Reservation&) = delete; |
||||
Reservation& operator=(const Reservation&) = delete; |
||||
Reservation(Reservation&&) = default; |
||||
Reservation& operator=(Reservation&&) = default; |
||||
~Reservation() { |
||||
if (allocator_ != nullptr) allocator_->Release(size_); |
||||
} |
||||
|
||||
private: |
||||
friend class MemoryAllocator; |
||||
Reservation(std::shared_ptr<internal::MemoryAllocatorImpl> allocator, |
||||
size_t size) |
||||
: allocator_(std::move(allocator)), size_(size) {} |
||||
|
||||
std::shared_ptr<internal::MemoryAllocatorImpl> allocator_; |
||||
size_t size_ = 0; |
||||
}; |
||||
|
||||
/// Reserve bytes from the quota and automatically release them when
|
||||
/// Reservation is destroyed.
|
||||
Reservation MakeReservation(MemoryRequest request) { |
||||
return Reservation(allocator_, Reserve(request)); |
||||
} |
||||
|
||||
/// Allocate a new object of type T, with constructor arguments.
|
||||
/// The returned type is wrapped, and upon destruction the reserved memory
|
||||
/// will be released to the allocator automatically. As such, T must have a
|
||||
/// virtual destructor so we can insert the necessary hook.
|
||||
template <typename T, typename... Args> |
||||
typename std::enable_if<std::has_virtual_destructor<T>::value, T*>::type New( |
||||
Args&&... args) { |
||||
// Wrap T such that when it's destroyed, we can release memory back to the
|
||||
// allocator.
|
||||
class Wrapper final : public T { |
||||
public: |
||||
explicit Wrapper(std::shared_ptr<internal::MemoryAllocatorImpl> allocator, |
||||
Args&&... args) |
||||
: T(std::forward<Args>(args)...), allocator_(std::move(allocator)) {} |
||||
~Wrapper() override { allocator_->Release(sizeof(*this)); } |
||||
|
||||
private: |
||||
const std::shared_ptr<internal::MemoryAllocatorImpl> allocator_; |
||||
}; |
||||
Reserve(sizeof(Wrapper)); |
||||
return new Wrapper(allocator_, std::forward<Args>(args)...); |
||||
} |
||||
|
||||
/// Construct a unique_ptr immediately.
|
||||
template <typename T, typename... Args> |
||||
std::unique_ptr<T> MakeUnique(Args&&... args) { |
||||
return std::unique_ptr<T>(New<T>(std::forward<Args>(args)...)); |
||||
} |
||||
|
||||
/// Allocate a slice, using MemoryRequest to size the number of returned
|
||||
/// bytes. For a variable length request, check the returned slice length to
|
||||
/// verify how much memory was allocated. Takes care of reserving memory for
|
||||
/// any relevant control structures also.
|
||||
grpc_slice MakeSlice(MemoryRequest request); |
||||
|
||||
/// A C++ allocator for containers of T.
|
||||
template <typename T> |
||||
class Container { |
||||
public: |
||||
using value_type = T; |
||||
|
||||
/// Construct the allocator: \a underlying_allocator is borrowed, and must
|
||||
/// outlive this object.
|
||||
explicit Container(MemoryAllocator* underlying_allocator) |
||||
: underlying_allocator_(underlying_allocator) {} |
||||
template <typename U> |
||||
explicit Container(const Container<U>& other) |
||||
: underlying_allocator_(other.underlying_allocator()) {} |
||||
|
||||
MemoryAllocator* underlying_allocator() const { |
||||
return underlying_allocator_; |
||||
} |
||||
|
||||
T* allocate(size_t n) { |
||||
underlying_allocator_->Reserve(n * sizeof(T)); |
||||
return static_cast<T*>(::operator new(n * sizeof(T))); |
||||
} |
||||
void deallocate(T* p, size_t n) { |
||||
::operator delete(p); |
||||
underlying_allocator_->Release(n * sizeof(T)); |
||||
} |
||||
|
||||
private: |
||||
MemoryAllocator* underlying_allocator_; |
||||
}; |
||||
|
||||
protected: |
||||
const std::shared_ptr<internal::MemoryAllocatorImpl>& allocator() { |
||||
return allocator_; |
||||
} |
||||
|
||||
private: |
||||
std::shared_ptr<internal::MemoryAllocatorImpl> allocator_; |
||||
}; |
||||
|
||||
// Wrapper type around std::vector to make initialization against a
|
||||
// MemoryAllocator based container allocator easy.
|
||||
template <typename T> |
||||
class Vector : public std::vector<T, MemoryAllocator::Container<T>> { |
||||
public: |
||||
explicit Vector(MemoryAllocator* allocator) |
||||
: std::vector<T, MemoryAllocator::Container<T>>( |
||||
MemoryAllocator::Container<T>(allocator)) {} |
||||
}; |
||||
|
||||
class MemoryAllocatorFactory { |
||||
public: |
||||
virtual ~MemoryAllocatorFactory() = default; |
||||
/// On Endpoint creation, call \a CreateMemoryAllocator to create a new
|
||||
/// allocator for the endpoint.
|
||||
/// Typically we'll want to:
|
||||
/// auto allocator = factory->CreateMemoryAllocator();
|
||||
/// auto* endpoint = allocator->New<MyEndpoint>(std::move(allocator), ...);
|
||||
virtual MemoryAllocator CreateMemoryAllocator() = 0; |
||||
}; |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
||||
|
||||
#endif // GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H
|
@ -1,71 +0,0 @@ |
||||
// Copyright 2021 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef GRPC_EVENT_ENGINE_SLICE_ALLOCATOR_H |
||||
#define GRPC_EVENT_ENGINE_SLICE_ALLOCATOR_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <functional> |
||||
|
||||
#include "absl/status/status.h" |
||||
|
||||
// forward-declaring an internal struct, not used publicly.
|
||||
struct grpc_resource_quota; |
||||
struct grpc_resource_user; |
||||
struct grpc_slice_buffer; |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
// TODO(nnoble): needs implementation
|
||||
class SliceBuffer { |
||||
public: |
||||
SliceBuffer() { abort(); } |
||||
explicit SliceBuffer(grpc_slice_buffer*) { abort(); } |
||||
|
||||
grpc_slice_buffer* RawSliceBuffer() { return slice_buffer_; } |
||||
|
||||
private: |
||||
grpc_slice_buffer* slice_buffer_; |
||||
}; |
||||
|
||||
class SliceAllocator { |
||||
public: |
||||
using AllocateCallback = std::function<void(absl::Status)>; |
||||
virtual ~SliceAllocator() = default; |
||||
/// Requests \a size bytes from gRPC, and populates \a dest with the allocated
|
||||
/// slices. Ownership of the \a SliceBuffer is not transferred.
|
||||
///
|
||||
/// gRPC provides a ResourceQuota system to cap the amount of memory used by
|
||||
/// the library. When a memory limit has been reached, slice allocation is
|
||||
/// interrupted to attempt to reclaim memory from participating gRPC
|
||||
/// internals. When there is sufficient memory available, slice allocation
|
||||
/// proceeds as normal.
|
||||
virtual absl::Status Allocate(size_t size, SliceBuffer* dest, |
||||
SliceAllocator::AllocateCallback cb) = 0; |
||||
}; |
||||
|
||||
class SliceAllocatorFactory { |
||||
public: |
||||
virtual ~SliceAllocatorFactory() = default; |
||||
/// On Endpoint creation, call \a CreateSliceAllocator with the name of the
|
||||
/// endpoint peer (a URI string, most likely).
|
||||
virtual std::unique_ptr<SliceAllocator> CreateSliceAllocator( |
||||
absl::string_view peer_name) = 0; |
||||
}; |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
||||
|
||||
#endif // GRPC_EVENT_ENGINE_SLICE_ALLOCATOR_H
|
@ -1,67 +0,0 @@ |
||||
// Copyright 2021 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/transport/chttp2/transport/chttp2_slice_allocator.h" |
||||
|
||||
#include <functional> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "absl/status/status.h" |
||||
|
||||
#include <grpc/event_engine/slice_allocator.h> |
||||
|
||||
#include "src/core/lib/iomgr/resource_quota.h" |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
Chttp2SliceAllocator::Chttp2SliceAllocator(grpc_resource_user* user) |
||||
: resource_user_(user) {} |
||||
|
||||
Chttp2SliceAllocator::~Chttp2SliceAllocator() { |
||||
if (resource_user_ != nullptr) { |
||||
grpc_resource_user_unref(resource_user_); |
||||
} |
||||
} |
||||
|
||||
absl::Status Chttp2SliceAllocator::Allocate( |
||||
size_t size, SliceBuffer* dest, SliceAllocator::AllocateCallback cb) { |
||||
// TODO(hork): merge the implementation from the uv-ee branch.
|
||||
(void)size; |
||||
(void)dest; |
||||
(void)cb; |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
Chttp2SliceAllocatorFactory::Chttp2SliceAllocatorFactory( |
||||
grpc_resource_quota* quota) |
||||
: resource_quota_(quota) { |
||||
grpc_resource_quota_ref_internal(resource_quota_); |
||||
} |
||||
|
||||
Chttp2SliceAllocatorFactory::~Chttp2SliceAllocatorFactory() { |
||||
if (resource_quota_ != nullptr) { |
||||
grpc_resource_quota_unref_internal(resource_quota_); |
||||
} |
||||
} |
||||
|
||||
std::unique_ptr<SliceAllocator> |
||||
Chttp2SliceAllocatorFactory::CreateSliceAllocator(absl::string_view peer_name) { |
||||
return absl::make_unique<Chttp2SliceAllocator>( |
||||
grpc_resource_user_create(resource_quota_, peer_name)); |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
@ -1,74 +0,0 @@ |
||||
// Copyright 2021 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_SLICE_ALLOCATOR_H |
||||
#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_SLICE_ALLOCATOR_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <functional> |
||||
|
||||
#include "absl/status/status.h" |
||||
|
||||
#include <grpc/event_engine/slice_allocator.h> |
||||
|
||||
#include "src/core/lib/iomgr/resource_quota.h" |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
class Chttp2SliceAllocator |
||||
: public grpc_event_engine::experimental::SliceAllocator { |
||||
public: |
||||
/// gRPC-internal constructor. Takes ownership of a resource_user ref from the
|
||||
/// caller.
|
||||
explicit Chttp2SliceAllocator(grpc_resource_user* user); |
||||
// Not copyable
|
||||
Chttp2SliceAllocator(Chttp2SliceAllocator& other) = delete; |
||||
Chttp2SliceAllocator& operator=(const Chttp2SliceAllocator& other) = delete; |
||||
// Not Moveable
|
||||
Chttp2SliceAllocator(Chttp2SliceAllocator&& other) = delete; |
||||
Chttp2SliceAllocator& operator=(Chttp2SliceAllocator&& other) = delete; |
||||
~Chttp2SliceAllocator() override; |
||||
absl::Status Allocate(size_t size, SliceBuffer* dest, |
||||
SliceAllocator::AllocateCallback cb) override; |
||||
|
||||
private: |
||||
grpc_resource_user* resource_user_; |
||||
}; |
||||
|
||||
class Chttp2SliceAllocatorFactory |
||||
: public grpc_event_engine::experimental::SliceAllocatorFactory { |
||||
public: |
||||
// gRPC-internal constructor
|
||||
explicit Chttp2SliceAllocatorFactory(grpc_resource_quota* quota); |
||||
// Not copyable
|
||||
Chttp2SliceAllocatorFactory(Chttp2SliceAllocatorFactory& other) = delete; |
||||
Chttp2SliceAllocatorFactory& operator=( |
||||
const Chttp2SliceAllocatorFactory& other) = delete; |
||||
// Not Moveable
|
||||
Chttp2SliceAllocatorFactory(Chttp2SliceAllocatorFactory&& other) = delete; |
||||
Chttp2SliceAllocatorFactory& operator=(Chttp2SliceAllocatorFactory&& other) = |
||||
delete; |
||||
~Chttp2SliceAllocatorFactory() override; |
||||
std::unique_ptr<SliceAllocator> CreateSliceAllocator( |
||||
absl::string_view peer_name) override; |
||||
|
||||
private: |
||||
grpc_resource_quota* resource_quota_; |
||||
}; |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
||||
|
||||
#endif // GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_SLICE_ALLOCATOR_H
|
@ -0,0 +1,70 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <grpc/event_engine/memory_allocator.h> |
||||
|
||||
#include "src/core/lib/gprpp/ref_counted.h" |
||||
#include "src/core/lib/slice/slice_refcount.h" |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
namespace { |
||||
|
||||
// Reference count for a slice allocated by MemoryAllocator::MakeSlice.
|
||||
// Takes care of releasing memory back when the slice is destroyed.
|
||||
class SliceRefCount { |
||||
public: |
||||
static void Destroy(void* p) { |
||||
auto* rc = static_cast<SliceRefCount*>(p); |
||||
rc->~SliceRefCount(); |
||||
gpr_free(rc); |
||||
} |
||||
SliceRefCount(std::shared_ptr<internal::MemoryAllocatorImpl> allocator, |
||||
size_t size) |
||||
: base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this, |
||||
&base_), |
||||
allocator_(std::move(allocator)), |
||||
size_(size) { |
||||
// Nothing to do here.
|
||||
} |
||||
~SliceRefCount() { allocator_->Release(size_); } |
||||
|
||||
grpc_slice_refcount* base_refcount() { return &base_; } |
||||
|
||||
private: |
||||
grpc_slice_refcount base_; |
||||
grpc_core::RefCount refs_; |
||||
std::shared_ptr<internal::MemoryAllocatorImpl> allocator_; |
||||
size_t size_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
grpc_slice MemoryAllocator::MakeSlice(MemoryRequest request) { |
||||
auto size = Reserve(request.Increase(sizeof(SliceRefCount))); |
||||
void* p = gpr_malloc(size); |
||||
new (p) SliceRefCount(allocator_, size); |
||||
grpc_slice slice; |
||||
slice.refcount = static_cast<SliceRefCount*>(p)->base_refcount(); |
||||
slice.data.refcounted.bytes = |
||||
static_cast<uint8_t*>(p) + sizeof(SliceRefCount); |
||||
slice.data.refcounted.length = size - sizeof(SliceRefCount); |
||||
return slice; |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
Loading…
Reference in new issue