• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 
15 #ifndef GRPC_SRC_CORE_LIB_RESOURCE_QUOTA_MEMORY_QUOTA_H
16 #define GRPC_SRC_CORE_LIB_RESOURCE_QUOTA_MEMORY_QUOTA_H
17 
18 #include <grpc/event_engine/memory_allocator.h>
19 #include <grpc/event_engine/memory_request.h>
20 #include <grpc/support/port_platform.h>
21 #include <stdint.h>
22 
23 #include <array>
24 #include <atomic>
25 #include <cstddef>
26 #include <limits>
27 #include <memory>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include "absl/base/thread_annotations.h"
33 #include "absl/container/flat_hash_set.h"
34 #include "absl/log/check.h"
35 #include "absl/log/log.h"
36 #include "absl/strings/string_view.h"
37 #include "absl/types/optional.h"
38 #include "src/core/lib/debug/trace.h"
39 #include "src/core/lib/experiments/experiments.h"
40 #include "src/core/lib/promise/activity.h"
41 #include "src/core/lib/promise/poll.h"
42 #include "src/core/lib/resource_quota/periodic_update.h"
43 #include "src/core/util/orphanable.h"
44 #include "src/core/util/ref_counted_ptr.h"
45 #include "src/core/util/sync.h"
46 #include "src/core/util/time.h"
47 #include "src/core/util/useful.h"
48 
49 namespace grpc_core {
50 
51 class BasicMemoryQuota;
52 class MemoryQuota;
53 class GrpcMemoryAllocatorImpl;
54 
55 using grpc_event_engine::experimental::MemoryRequest;
56 
57 // Pull in impl under a different name to keep the gRPC/EventEngine separation
58 // clear.
59 using EventEngineMemoryAllocatorImpl =
60     grpc_event_engine::experimental::internal::MemoryAllocatorImpl;
61 using grpc_event_engine::experimental::MemoryAllocator;
62 template <typename T>
63 using Vector = grpc_event_engine::experimental::Vector<T>;
64 
65 // Reclamation passes.
66 // When memory is tight, we start trying to claim some back from memory
67 // reclaimers. We do this in multiple passes: if there is a less destructive
68 // operation available, we do that, otherwise we do something more destructive.
69 enum class ReclamationPass {
70   // Benign reclamation is intended for reclamation steps that are not
71   // observable outside of gRPC (besides maybe causing an increase in CPU
72   // usage).
73   // Examples of such reclamation would be resizing buffers to fit the current
74   // load needs, rather than whatever was the peak usage requirement.
75   kBenign = 0,
76   // Idle reclamation is intended for reclamation steps that are observable
77   // outside of gRPC, but do not cause application work to be lost.
78   // Examples of such reclamation would be dropping channels that are not being
79   // used.
80   kIdle = 1,
81   // Destructive reclamation is our last resort, and is these reclamations are
82   // allowed to drop work - such as cancelling in flight requests.
83   kDestructive = 2,
84 };
85 static constexpr size_t kNumReclamationPasses = 3;
86 
87 // For each reclamation function run we construct a ReclamationSweep.
88 // When this object is finally destroyed (it may be moved several times first),
89 // then that reclamation is complete and we may continue the reclamation loop.
90 class ReclamationSweep {
91  public:
92   ReclamationSweep() = default;
ReclamationSweep(std::shared_ptr<BasicMemoryQuota> memory_quota,uint64_t sweep_token,Waker waker)93   ReclamationSweep(std::shared_ptr<BasicMemoryQuota> memory_quota,
94                    uint64_t sweep_token, Waker waker)
95       : memory_quota_(std::move(memory_quota)),
96         sweep_token_(sweep_token),
97         waker_(std::move(waker)) {}
98   ~ReclamationSweep();
99 
100   ReclamationSweep(const ReclamationSweep&) = delete;
101   ReclamationSweep& operator=(const ReclamationSweep&) = delete;
102   ReclamationSweep(ReclamationSweep&&) = default;
103   ReclamationSweep& operator=(ReclamationSweep&&) = default;
104 
105   // Has enough work been done that we would not be called upon again
106   // immediately to do reclamation work if we stopped and requeued. Reclaimers
107   // with a variable amount of work to do can use this to ascertain when they
108   // can stop more efficiently than going through the reclaimer queue once per
109   // work item.
110   bool IsSufficient() const;
111 
112   // Explicit finish for users that wish to write it.
113   // Just destroying the object is enough, but sometimes the additional
114   // explicitness is warranted.
Finish()115   void Finish() {
116     [](ReclamationSweep) {}(std::move(*this));
117   }
118 
119  private:
120   std::shared_ptr<BasicMemoryQuota> memory_quota_;
121   uint64_t sweep_token_;
122   Waker waker_;
123 };
124 
125 class ReclaimerQueue {
126  private:
127   struct QueuedNode;
128   struct State;
129 
130  public:
131   class Handle : public InternallyRefCounted<Handle> {
132    public:
133     Handle() = default;
134     template <typename F>
Handle(F reclaimer,std::shared_ptr<State> state)135     explicit Handle(F reclaimer, std::shared_ptr<State> state)
136         : sweep_(new SweepFn<F>(std::move(reclaimer), std::move(state))) {}
~Handle()137     ~Handle() override {
138       DCHECK_EQ(sweep_.load(std::memory_order_relaxed), nullptr);
139     }
140 
141     Handle(const Handle&) = delete;
142     Handle& operator=(const Handle&) = delete;
143 
144     void Orphan() final;
145     void Run(ReclamationSweep reclamation_sweep);
146     bool Requeue(ReclaimerQueue* new_queue);
147 
148    private:
149     friend class ReclaimerQueue;
150     using InternallyRefCounted<Handle>::Ref;
151 
152     class Sweep {
153      public:
154       virtual void RunAndDelete(absl::optional<ReclamationSweep> sweep) = 0;
155 
156      protected:
Sweep(std::shared_ptr<State> state)157       explicit Sweep(std::shared_ptr<State> state) : state_(std::move(state)) {}
158       ~Sweep() = default;
159       void MarkCancelled();
160 
161      private:
162       std::shared_ptr<State> state_;
163     };
164 
165     template <typename F>
166     class SweepFn final : public Sweep {
167      public:
SweepFn(F && f,std::shared_ptr<State> state)168       explicit SweepFn(F&& f, std::shared_ptr<State> state)
169           : Sweep(std::move(state)), f_(std::move(f)) {}
RunAndDelete(absl::optional<ReclamationSweep> sweep)170       void RunAndDelete(absl::optional<ReclamationSweep> sweep) override {
171         if (!sweep.has_value()) MarkCancelled();
172         f_(std::move(sweep));
173         delete this;
174       }
175 
176      private:
177       F f_;
178     };
179 
180     std::atomic<Sweep*> sweep_{nullptr};
181   };
182 
183   ReclaimerQueue();
184   ~ReclaimerQueue();
185 
186   ReclaimerQueue(const ReclaimerQueue&) = delete;
187   ReclaimerQueue& operator=(const ReclaimerQueue&) = delete;
188 
189   // Insert a new element at the back of the queue.
190   // If there is already an element from allocator at *index, then it is
191   // replaced with the new reclaimer and *index is unchanged. If there is not,
192   // then *index is set to the index of the newly queued entry.
193   // Associates the reclamation function with an allocator, and keeps that
194   // allocator alive, so that we can use the pointer as an ABA guard.
195   template <typename F>
Insert(F reclaimer)196   GRPC_MUST_USE_RESULT OrphanablePtr<Handle> Insert(F reclaimer) {
197     auto p = MakeOrphanable<Handle>(std::move(reclaimer), state_);
198     Enqueue(p->Ref());
199     return p;
200   }
201 
202   // Poll to see if an entry is available: returns Pending if not, or the
203   // removed reclamation function if so.
204   Poll<RefCountedPtr<Handle>> PollNext();
205 
206   // This callable is the promise backing Next - it resolves when there is an
207   // entry available. This really just redirects to calling PollNext().
208   class NextPromise {
209    public:
NextPromise(ReclaimerQueue * queue)210     explicit NextPromise(ReclaimerQueue* queue) : queue_(queue) {}
operator()211     Poll<RefCountedPtr<Handle>> operator()() { return queue_->PollNext(); }
212 
213    private:
214     // Borrowed ReclaimerQueue backing this promise.
215     ReclaimerQueue* queue_;
216   };
Next()217   GRPC_MUST_USE_RESULT NextPromise Next() { return NextPromise(this); }
218 
219  private:
220   void Enqueue(RefCountedPtr<Handle> handle);
221 
222   std::shared_ptr<State> state_;
223 };
224 
225 namespace memory_quota_detail {
226 // Controller: tries to adjust a control variable up or down to get memory
227 // pressure to some target. We use the control variable to size buffers
228 // throughout the stack.
229 class PressureController {
230  public:
PressureController(uint8_t max_ticks_same,uint8_t max_reduction_per_tick)231   PressureController(uint8_t max_ticks_same, uint8_t max_reduction_per_tick)
232       : max_ticks_same_(max_ticks_same),
233         max_reduction_per_tick_(max_reduction_per_tick) {}
234   // Update the controller, returns the new control value.
235   double Update(double error);
236   // Textual representation of the controller.
237   std::string DebugString() const;
238 
239  private:
240   // How many update periods have we reached the same decision in a row?
241   // Too many and we should start expanding the search space since we're not
242   // being aggressive enough.
243   uint8_t ticks_same_ = 0;
244   // Maximum number of ticks with the same value until we start expanding the
245   // control space.
246   const uint8_t max_ticks_same_;
247   // Maximum amount to reduce the reporting value per iteration (in tenths of a
248   // percentile).
249   const uint8_t max_reduction_per_tick_;
250   // Was the last error indicating a too low pressure (or if false,
251   // a too high pressure).
252   bool last_was_low_ = true;
253   // Current minimum value to report.
254   double min_ = 0.0;
255   // Current maximum value to report.
256   // Set so that the first change over will choose 1.0 for max.
257   double max_ = 2.0;
258   // Last control value reported.
259   double last_control_ = 0.0;
260 };
261 
262 // Utility to track memory pressure.
263 // Tries to be conservative (returns a higher pressure than there may actually
264 // be) but to be eventually accurate.
265 class PressureTracker {
266  public:
267   double AddSampleAndGetControlValue(double sample);
268 
269  private:
270   std::atomic<double> max_this_round_{0.0};
271   std::atomic<double> report_{0.0};
272   PeriodicUpdate update_{Duration::Seconds(1)};
273   PressureController controller_{100, 3};
274 };
275 }  // namespace memory_quota_detail
276 
277 // Minimum number of free bytes in order for allocator to move to big bucket.
278 static constexpr size_t kBigAllocatorThreshold = 0.5 * 1024 * 1024;
279 // Maximum number of free bytes in order for allocator to move to small
280 // bucket.
281 static constexpr size_t kSmallAllocatorThreshold = 0.1 * 1024 * 1024;
282 
283 class BasicMemoryQuota final
284     : public std::enable_shared_from_this<BasicMemoryQuota> {
285  public:
286   // Data about current memory pressure.
287   struct PressureInfo {
288     // The current instantaneously measured memory pressure.
289     double instantaneous_pressure = 0.0;
290     // A control value that can be used to scale buffer sizes up or down to
291     // adjust memory pressure to our target set point.
292     double pressure_control_value = 0.0;
293     // Maximum recommended individual allocation size.
294     size_t max_recommended_allocation_size = 0;
295   };
296 
297   explicit BasicMemoryQuota(std::string name);
298 
299   // Start the reclamation activity.
300   void Start();
301   // Stop the reclamation activity.
302   // Until reclamation is stopped, it's possible that circular references to the
303   // BasicMemoryQuota remain. i.e. to guarantee deletion, a singular owning
304   // object should call BasicMemoryQuota::Stop().
305   void Stop();
306 
307   // Resize the quota to new_size.
308   void SetSize(size_t new_size);
309   // Forcefully take some memory from the quota, potentially entering
310   // overcommit.
311   void Take(GrpcMemoryAllocatorImpl* allocator, size_t amount);
312   // Finish reclamation pass.
313   void FinishReclamation(uint64_t token, Waker waker);
314   // Return some memory to the quota.
315   void Return(size_t amount);
316   // Add allocator to list of allocators in small bucket. Returns allocator id.
317   void AddNewAllocator(GrpcMemoryAllocatorImpl* allocator);
318   // Remove allocator from list of allocators.
319   void RemoveAllocator(GrpcMemoryAllocatorImpl* allocator);
320   // Determine whether to move allocator to different bucket and if so, move.
321   void MaybeMoveAllocator(GrpcMemoryAllocatorImpl* allocator,
322                           size_t old_free_bytes, size_t new_free_bytes);
323   // Instantaneous memory pressure approximation.
324   PressureInfo GetPressureInfo();
325   // Get a reclamation queue
reclaimer_queue(size_t i)326   ReclaimerQueue* reclaimer_queue(size_t i) { return &reclaimers_[i]; }
327 
328   // The name of this quota
name()329   absl::string_view name() const { return name_; }
330 
331  private:
332   friend class ReclamationSweep;
333   class WaitForSweepPromise;
334 
335   class AllocatorBucket {
336    public:
337     struct Shard {
338       absl::flat_hash_set<GrpcMemoryAllocatorImpl*> allocators
339           ABSL_GUARDED_BY(shard_mu);
340       Mutex shard_mu;
341     };
342 
SelectShard(void * key)343     Shard& SelectShard(void* key) {
344       const size_t hash = HashPointer(key, shards.size());
345       return shards[hash % shards.size()];
346     }
347 
348     std::array<Shard, 16> shards;
349   };
350 
351   static constexpr intptr_t kInitialSize = std::numeric_limits<intptr_t>::max();
352 
353   // Move allocator from big bucket to small bucket.
354   void MaybeMoveAllocatorBigToSmall(GrpcMemoryAllocatorImpl* allocator);
355   // Move allocator from small bucket to big bucket.
356   void MaybeMoveAllocatorSmallToBig(GrpcMemoryAllocatorImpl* allocator);
357 
358   // The amount of memory that's free in this quota.
359   // We use intptr_t as a reasonable proxy for ssize_t that's portable.
360   // We allow arbitrary overcommit and so this must allow negative values.
361   std::atomic<intptr_t> free_bytes_{kInitialSize};
362   // The total number of bytes in this quota.
363   std::atomic<size_t> quota_size_{kInitialSize};
364 
365   // Reclaimer queues.
366   ReclaimerQueue reclaimers_[kNumReclamationPasses];
367   // List of all allocators sorted into 2 buckets, small (<100 KB free bytes)
368   // and large (>500 KB free bytes).
369   AllocatorBucket small_allocators_;
370   AllocatorBucket big_allocators_;
371   // The reclaimer activity consumes reclaimers whenever we are in overcommit to
372   // try and get back under memory limits.
373   ActivityPtr reclaimer_activity_;
374   // Each time we do a reclamation sweep, we increment this counter and give it
375   // to the sweep in question. In this way, should we choose to cancel a sweep
376   // we can do so and not get confused when the sweep reports back that it's
377   // completed.
378   // We also increment this counter on completion of a sweep, as an indicator
379   // that the wait has ended.
380   std::atomic<uint64_t> reclamation_counter_{0};
381   // Memory pressure smoothing
382   memory_quota_detail::PressureTracker pressure_tracker_;
383   // The name of this quota - used for debugging/tracing/etc..
384   std::string name_;
385 };
386 
387 // MemoryAllocatorImpl grants the owner the ability to allocate memory from an
388 // underlying resource quota.
389 class GrpcMemoryAllocatorImpl final : public EventEngineMemoryAllocatorImpl {
390  public:
391   explicit GrpcMemoryAllocatorImpl(
392       std::shared_ptr<BasicMemoryQuota> memory_quota);
393   ~GrpcMemoryAllocatorImpl() override;
394 
395   // Reserve bytes from the quota.
396   // If we enter overcommit, reclamation will begin concurrently.
397   // Returns the number of bytes reserved.
398   size_t Reserve(MemoryRequest request) override;
399 
400   /// Allocate a slice, using MemoryRequest to size the number of returned
401   /// bytes. For a variable length request, check the returned slice length to
402   /// verify how much memory was allocated. Takes care of reserving memory for
403   /// any relevant control structures also.
404   grpc_slice MakeSlice(MemoryRequest request) override;
405 
406   // Release some bytes that were previously reserved.
Release(size_t n)407   void Release(size_t n) override {
408     // Add the released memory to our free bytes counter... if this increases
409     // from  0 to non-zero, then we have more to do, otherwise, we're actually
410     // done.
411     size_t prev_free = free_bytes_.fetch_add(n, std::memory_order_release);
412     if ((!IsUnconstrainedMaxQuotaBufferSizeEnabled() &&
413          prev_free + n > kMaxQuotaBufferSize) ||
414         donate_back_.Tick([](Duration) {})) {
415       // Try to immediately return some free'ed memory back to the total quota.
416       MaybeDonateBack();
417     }
418     size_t new_free = free_bytes_.load(std::memory_order_relaxed);
419     memory_quota_->MaybeMoveAllocator(this, prev_free, new_free);
420   }
421 
422   // Return all free bytes to quota.
ReturnFree()423   void ReturnFree() {
424     size_t ret = free_bytes_.exchange(0, std::memory_order_acq_rel);
425     if (ret == 0) return;
426     GRPC_TRACE_LOG(resource_quota, INFO)
427         << "Allocator " << this << " returning " << ret << " bytes to quota";
428     taken_bytes_.fetch_sub(ret, std::memory_order_relaxed);
429     memory_quota_->Return(ret);
430     memory_quota_->MaybeMoveAllocator(this, /*old_free_bytes=*/ret,
431                                       /*new_free_bytes=*/0);
432   }
433 
434   // Post a reclamation function.
435   template <typename F>
PostReclaimer(ReclamationPass pass,F fn)436   void PostReclaimer(ReclamationPass pass, F fn) {
437     MutexLock lock(&reclaimer_mu_);
438     CHECK(!shutdown_);
439     InsertReclaimer(static_cast<size_t>(pass), std::move(fn));
440   }
441 
442   // Shutdown the allocator.
443   void Shutdown() override;
444 
445   // Read the instantaneous memory pressure
GetPressureInfo()446   BasicMemoryQuota::PressureInfo GetPressureInfo() const {
447     return memory_quota_->GetPressureInfo();
448   }
449 
GetFreeBytes()450   size_t GetFreeBytes() const {
451     return free_bytes_.load(std::memory_order_relaxed);
452   }
453 
IncrementShardIndex()454   size_t IncrementShardIndex() {
455     return chosen_shard_idx_.fetch_add(1, std::memory_order_relaxed);
456   }
457 
458  private:
459   static constexpr size_t kMaxQuotaBufferSize = 1024 * 1024;
460 
461   // Primitive reservation function.
462   GRPC_MUST_USE_RESULT absl::optional<size_t> TryReserve(MemoryRequest request);
463   // This function may be invoked during a memory release operation.
464   // It will try to return half of our free pool to the quota.
465   void MaybeDonateBack();
466   // Replenish bytes from the quota, without blocking, possibly entering
467   // overcommit.
468   void Replenish();
469   template <typename F>
InsertReclaimer(size_t pass,F fn)470   void InsertReclaimer(size_t pass, F fn)
471       ABSL_EXCLUSIVE_LOCKS_REQUIRED(reclaimer_mu_) {
472     reclamation_handles_[pass] =
473         memory_quota_->reclaimer_queue(pass)->Insert(std::move(fn));
474   }
475 
476   // Backing resource quota.
477   const std::shared_ptr<BasicMemoryQuota> memory_quota_;
478   // Amount of memory this allocator has cached for its own use: to avoid quota
479   // contention, each MemoryAllocator can keep some memory in addition to what
480   // it is immediately using, and the quota can pull it back under memory
481   // pressure.
482   std::atomic<size_t> free_bytes_{0};
483   // Amount of memory taken from the quota by this allocator.
484   std::atomic<size_t> taken_bytes_{sizeof(GrpcMemoryAllocatorImpl)};
485   // Index used to randomly choose shard to return bytes from.
486   std::atomic<size_t> chosen_shard_idx_{0};
487   // We try to donate back some memory periodically to the central quota.
488   PeriodicUpdate donate_back_{Duration::Seconds(10)};
489   Mutex reclaimer_mu_;
490   bool shutdown_ ABSL_GUARDED_BY(reclaimer_mu_) = false;
491   // Indices into the various reclaimer queues, used so that we can cancel
492   // reclamation should we shutdown or get rebound.
493   OrphanablePtr<ReclaimerQueue::Handle>
494       reclamation_handles_[kNumReclamationPasses] ABSL_GUARDED_BY(
495           reclaimer_mu_);
496 };
497 
498 // MemoryOwner is an enhanced MemoryAllocator that can also reclaim memory, and
499 // be rebound to a different memory quota.
500 // Different modules should not share a MemoryOwner between themselves, instead
501 // each module that requires a MemoryOwner should create one from a resource
502 // quota. This is because the MemoryOwner reclaimers are tied to the
503 // MemoryOwner's lifetime, and are not queryable, so passing a MemoryOwner to a
504 // new owning module means that module cannot reason about which reclaimers are
505 // active, nor what they might do.
506 class MemoryOwner final : public MemoryAllocator {
507  public:
508   MemoryOwner() = default;
509 
MemoryOwner(std::shared_ptr<GrpcMemoryAllocatorImpl> allocator)510   explicit MemoryOwner(std::shared_ptr<GrpcMemoryAllocatorImpl> allocator)
511       : MemoryAllocator(std::move(allocator)) {}
512 
513   // Post a reclaimer for some reclamation pass.
514   template <typename F>
PostReclaimer(ReclamationPass pass,F fn)515   void PostReclaimer(ReclamationPass pass, F fn) {
516     impl()->PostReclaimer(pass, std::move(fn));
517   }
518 
519   // Instantaneous memory pressure in the underlying quota.
GetPressureInfo()520   BasicMemoryQuota::PressureInfo GetPressureInfo() const {
521     if (!is_valid()) return {};
522     return impl()->GetPressureInfo();
523   }
524 
525   template <typename T, typename... Args>
MakeOrphanable(Args &&...args)526   OrphanablePtr<T> MakeOrphanable(Args&&... args) {
527     return OrphanablePtr<T>(New<T>(std::forward<Args>(args)...));
528   }
529 
530   // Is this object valid (ie has not been moved out of or reset)
is_valid()531   bool is_valid() const { return impl() != nullptr; }
532 
memory_pressure_high_threshold()533   static double memory_pressure_high_threshold() { return 0.99; }
534 
535   // Return true if the controlled memory pressure is high.
IsMemoryPressureHigh()536   bool IsMemoryPressureHigh() const {
537     return GetPressureInfo().pressure_control_value >
538            memory_pressure_high_threshold();
539   }
540 
541  private:
impl()542   const GrpcMemoryAllocatorImpl* impl() const {
543     return static_cast<const GrpcMemoryAllocatorImpl*>(get_internal_impl_ptr());
544   }
545 
impl()546   GrpcMemoryAllocatorImpl* impl() {
547     return static_cast<GrpcMemoryAllocatorImpl*>(get_internal_impl_ptr());
548   }
549 };
550 
551 // MemoryQuota tracks the amount of memory available as part of a ResourceQuota.
552 class MemoryQuota final
553     : public grpc_event_engine::experimental::MemoryAllocatorFactory {
554  public:
MemoryQuota(std::string name)555   explicit MemoryQuota(std::string name)
556       : memory_quota_(std::make_shared<BasicMemoryQuota>(std::move(name))) {
557     memory_quota_->Start();
558   }
~MemoryQuota()559   ~MemoryQuota() override {
560     if (memory_quota_ != nullptr) memory_quota_->Stop();
561   }
562 
563   MemoryQuota(const MemoryQuota&) = delete;
564   MemoryQuota& operator=(const MemoryQuota&) = delete;
565   MemoryQuota(MemoryQuota&&) = default;
566   MemoryQuota& operator=(MemoryQuota&&) = default;
567 
568   MemoryAllocator CreateMemoryAllocator(absl::string_view name) override;
569   MemoryOwner CreateMemoryOwner();
570 
571   // Resize the quota to new_size.
SetSize(size_t new_size)572   void SetSize(size_t new_size) { memory_quota_->SetSize(new_size); }
573 
IsMemoryPressureHigh()574   bool IsMemoryPressureHigh() const {
575     return memory_quota_->GetPressureInfo().pressure_control_value >
576            MemoryOwner::memory_pressure_high_threshold();
577   }
578 
579  private:
580   friend class MemoryOwner;
581   std::shared_ptr<BasicMemoryQuota> memory_quota_;
582 };
583 
584 using MemoryQuotaRefPtr = std::shared_ptr<MemoryQuota>;
MakeMemoryQuota(std::string name)585 inline MemoryQuotaRefPtr MakeMemoryQuota(std::string name) {
586   return std::make_shared<MemoryQuota>(std::move(name));
587 }
588 
589 std::vector<std::shared_ptr<BasicMemoryQuota>> AllMemoryQuotas();
590 
591 void SetContainerMemoryPressure(double pressure);
592 
593 double ContainerMemoryPressure();
594 
595 }  // namespace grpc_core
596 
597 #endif  // GRPC_SRC_CORE_LIB_RESOURCE_QUOTA_MEMORY_QUOTA_H
598