blob: 511ffdcdf13bc5c7736c53a3dd30aeb338c90a59 [file] [log] [blame]
Craig Tiller20afa3d2016-10-17 14:52:14 -07001/*
2 *
3 * Copyright 2016, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "src/core/lib/iomgr/resource_quota.h"
35
Craig Tillerb38bfc02016-10-24 13:21:39 -070036#include <limits.h>
37#include <stdint.h>
Craig Tiller20afa3d2016-10-17 14:52:14 -070038#include <string.h>
39
40#include <grpc/support/alloc.h>
41#include <grpc/support/log.h>
42#include <grpc/support/string_util.h>
43#include <grpc/support/useful.h>
44
45#include "src/core/lib/iomgr/combiner.h"
46
47int grpc_resource_quota_trace = 0;
48
Craig Tillerb38bfc02016-10-24 13:21:39 -070049#define MEMORY_USAGE_ESTIMATION_MAX 65536
50
Craig Tillera947f1c2016-11-04 13:53:17 -070051/* Internal linked list pointers for a resource user */
52typedef struct {
53 grpc_resource_user *next;
54 grpc_resource_user *prev;
55} grpc_resource_user_link;
56
57/* Resource users are kept in (potentially) several intrusive linked lists
58 at once. These are the list names. */
59typedef enum {
60 /* Resource users that are waiting for an allocation */
61 GRPC_RULIST_AWAITING_ALLOCATION,
62 /* Resource users that have free memory available for internal reclamation */
63 GRPC_RULIST_NON_EMPTY_FREE_POOL,
64 /* Resource users that have published a benign reclamation is available */
65 GRPC_RULIST_RECLAIMER_BENIGN,
66 /* Resource users that have published a destructive reclamation is
67 available */
68 GRPC_RULIST_RECLAIMER_DESTRUCTIVE,
69 /* Number of lists: must be last */
70 GRPC_RULIST_COUNT
71} grpc_rulist;
72
73struct grpc_resource_user {
74 /* The quota this resource user consumes from */
75 grpc_resource_quota *resource_quota;
76
77 /* Closure to schedule an allocation under the resource quota combiner lock */
78 grpc_closure allocate_closure;
79 /* Closure to publish a non empty free pool under the resource quota combiner
80 lock */
81 grpc_closure add_to_free_pool_closure;
82
83 /* one ref for each ref call (released by grpc_resource_user_unref), and one
84 ref for each byte allocated (released by grpc_resource_user_free) */
85 gpr_atm refs;
86 /* is this resource user unlocked? starts at 0, increases for each shutdown
87 call */
88 gpr_atm shutdown;
89
90 gpr_mu mu;
91 /* The amount of memory (in bytes) this user has cached for its own use: to
92 avoid quota contention, each resource user can keep some memory in
93 addition to what it is immediately using (e.g., for caching), and the quota
94 can pull it back under memory pressure.
95 This value can become negative if more memory has been requested than
96 existed in the free pool, at which point the quota is consulted to bring
97 this value non-negative (asynchronously). */
98 int64_t free_pool;
99 /* A list of closures to call once free_pool becomes non-negative - ie when
100 all outstanding allocations have been granted. */
101 grpc_closure_list on_allocated;
102 /* True if we are currently trying to allocate from the quota, false if not */
103 bool allocating;
104 /* True if we are currently trying to add ourselves to the non-free quota
105 list, false otherwise */
106 bool added_to_free_pool;
107
108 /* Reclaimers: index 0 is the benign reclaimer, 1 is the destructive reclaimer
109 */
110 grpc_closure *reclaimers[2];
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800111 /* Reclaimers just posted: once we're in the combiner lock, we'll move them
112 to the array above */
113 grpc_closure *new_reclaimers[2];
Craig Tillera947f1c2016-11-04 13:53:17 -0700114 /* Trampoline closures to finish reclamation and re-enter the quota combiner
115 lock */
116 grpc_closure post_reclaimer_closure[2];
117
118 /* Closure to execute under the quota combiner to de-register and shutdown the
119 resource user */
120 grpc_closure destroy_closure;
121
122 /* Links in the various grpc_rulist lists */
123 grpc_resource_user_link links[GRPC_RULIST_COUNT];
124
125 /* The name of this resource user, for debugging/tracing */
126 char *name;
127};
128
Craig Tiller20afa3d2016-10-17 14:52:14 -0700129struct grpc_resource_quota {
Craig Tiller3798e602016-10-21 14:39:27 -0700130 /* refcount */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700131 gpr_refcount refs;
132
Craig Tillerb38bfc02016-10-24 13:21:39 -0700133 /* estimate of current memory usage
134 scaled to the range [0..RESOURCE_USAGE_ESTIMATION_MAX] */
135 gpr_atm memory_usage_estimation;
136
Craig Tiller3798e602016-10-21 14:39:27 -0700137 /* Master combiner lock: all activity on a quota executes under this combiner
Craig Tillereb30c802016-12-27 16:03:43 -0800138 * (so no mutex is needed for this data structure) */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700139 grpc_combiner *combiner;
Craig Tiller3798e602016-10-21 14:39:27 -0700140 /* Size of the resource quota */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700141 int64_t size;
Craig Tiller3798e602016-10-21 14:39:27 -0700142 /* Amount of free memory in the resource quota */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700143 int64_t free_pool;
144
Craig Tiller3798e602016-10-21 14:39:27 -0700145 /* Has rq_step been scheduled to occur? */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700146 bool step_scheduled;
Craig Tiller3798e602016-10-21 14:39:27 -0700147 /* Are we currently reclaiming memory */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700148 bool reclaiming;
Craig Tiller3798e602016-10-21 14:39:27 -0700149 /* Closure around rq_step */
150 grpc_closure rq_step_closure;
151 /* Closure around rq_reclamation_done */
152 grpc_closure rq_reclamation_done_closure;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700153
Craig Tiller13b9a9a2016-12-02 12:43:09 -0800154 /* This is only really usable for debugging: it's always a stale pointer, but
155 a stale pointer that might just be fresh enough to guide us to where the
156 reclamation system is stuck */
157 grpc_closure *debug_only_last_initiated_reclaimer;
158 grpc_resource_user *debug_only_last_reclaimer_resource_user;
159
Craig Tiller3798e602016-10-21 14:39:27 -0700160 /* Roots of all resource user lists */
161 grpc_resource_user *roots[GRPC_RULIST_COUNT];
Craig Tiller20afa3d2016-10-17 14:52:14 -0700162
163 char *name;
164};
165
166/*******************************************************************************
167 * list management
168 */
169
Craig Tiller6b5d6822016-10-25 16:13:26 -0700170static void rulist_add_head(grpc_resource_user *resource_user,
Craig Tiller3798e602016-10-21 14:39:27 -0700171 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700172 grpc_resource_quota *resource_quota = resource_user->resource_quota;
173 grpc_resource_user **root = &resource_quota->roots[list];
174 if (*root == NULL) {
175 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -0700176 resource_user->links[list].next = resource_user->links[list].prev =
177 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700178 } else {
179 resource_user->links[list].next = *root;
180 resource_user->links[list].prev = (*root)->links[list].prev;
181 resource_user->links[list].next->links[list].prev =
182 resource_user->links[list].prev->links[list].next = resource_user;
Craig Tiller8abc7962016-10-26 21:31:29 -0700183 *root = resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700184 }
185}
186
Craig Tiller6b5d6822016-10-25 16:13:26 -0700187static void rulist_add_tail(grpc_resource_user *resource_user,
Craig Tiller3798e602016-10-21 14:39:27 -0700188 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700189 grpc_resource_quota *resource_quota = resource_user->resource_quota;
190 grpc_resource_user **root = &resource_quota->roots[list];
191 if (*root == NULL) {
192 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -0700193 resource_user->links[list].next = resource_user->links[list].prev =
194 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700195 } else {
196 resource_user->links[list].next = (*root)->links[list].next;
197 resource_user->links[list].prev = *root;
198 resource_user->links[list].next->links[list].prev =
199 resource_user->links[list].prev->links[list].next = resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700200 }
201}
202
Craig Tiller3798e602016-10-21 14:39:27 -0700203static bool rulist_empty(grpc_resource_quota *resource_quota,
204 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700205 return resource_quota->roots[list] == NULL;
206}
207
Craig Tiller8abc7962016-10-26 21:31:29 -0700208static grpc_resource_user *rulist_pop_head(grpc_resource_quota *resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700209 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700210 grpc_resource_user **root = &resource_quota->roots[list];
211 grpc_resource_user *resource_user = *root;
212 if (resource_user == NULL) {
213 return NULL;
214 }
215 if (resource_user->links[list].next == resource_user) {
216 *root = NULL;
217 } else {
218 resource_user->links[list].next->links[list].prev =
219 resource_user->links[list].prev;
220 resource_user->links[list].prev->links[list].next =
221 resource_user->links[list].next;
222 *root = resource_user->links[list].next;
223 }
224 resource_user->links[list].next = resource_user->links[list].prev = NULL;
225 return resource_user;
226}
227
Craig Tiller3798e602016-10-21 14:39:27 -0700228static void rulist_remove(grpc_resource_user *resource_user, grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700229 if (resource_user->links[list].next == NULL) return;
230 grpc_resource_quota *resource_quota = resource_user->resource_quota;
231 if (resource_quota->roots[list] == resource_user) {
232 resource_quota->roots[list] = resource_user->links[list].next;
233 if (resource_quota->roots[list] == resource_user) {
234 resource_quota->roots[list] = NULL;
235 }
236 }
237 resource_user->links[list].next->links[list].prev =
238 resource_user->links[list].prev;
239 resource_user->links[list].prev->links[list].next =
240 resource_user->links[list].next;
Craig Tillerab5a4992016-12-02 17:17:07 -0800241 resource_user->links[list].next = resource_user->links[list].prev = NULL;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700242}
243
244/*******************************************************************************
Craig Tiller6b5d6822016-10-25 16:13:26 -0700245 * resource quota state machine
Craig Tiller20afa3d2016-10-17 14:52:14 -0700246 */
247
Craig Tiller3798e602016-10-21 14:39:27 -0700248static bool rq_alloc(grpc_exec_ctx *exec_ctx,
249 grpc_resource_quota *resource_quota);
Craig Tiller6b5d6822016-10-25 16:13:26 -0700250static bool rq_reclaim_from_per_user_free_pool(
251 grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota);
Craig Tiller3798e602016-10-21 14:39:27 -0700252static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
253 grpc_resource_quota *resource_quota, bool destructive);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700254
Craig Tiller6b5d6822016-10-25 16:13:26 -0700255static void rq_step(grpc_exec_ctx *exec_ctx, void *rq, grpc_error *error) {
256 grpc_resource_quota *resource_quota = rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700257 resource_quota->step_scheduled = false;
258 do {
Craig Tiller3798e602016-10-21 14:39:27 -0700259 if (rq_alloc(exec_ctx, resource_quota)) goto done;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700260 } while (rq_reclaim_from_per_user_free_pool(exec_ctx, resource_quota));
Sree Kuchibhotla894db552016-10-28 18:25:24 -0700261
262 if (!rq_reclaim(exec_ctx, resource_quota, false)) {
263 rq_reclaim(exec_ctx, resource_quota, true);
264 }
265
Craig Tiller20afa3d2016-10-17 14:52:14 -0700266done:
Craig Tillera59c16c2016-10-31 07:25:01 -0700267 grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700268}
269
Craig Tiller3798e602016-10-21 14:39:27 -0700270static void rq_step_sched(grpc_exec_ctx *exec_ctx,
271 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700272 if (resource_quota->step_scheduled) return;
273 resource_quota->step_scheduled = true;
Craig Tillera59c16c2016-10-31 07:25:01 -0700274 grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller91031da2016-12-28 15:44:25 -0800275 grpc_closure_sched(exec_ctx, &resource_quota->rq_step_closure,
276 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700277}
278
Craig Tillerb38bfc02016-10-24 13:21:39 -0700279/* update the atomically available resource estimate - use no barriers since
280 timeliness of delivery really doesn't matter much */
281static void rq_update_estimate(grpc_resource_quota *resource_quota) {
282 gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation,
283 (gpr_atm)((1.0 -
284 ((double)resource_quota->free_pool) /
285 ((double)resource_quota->size)) *
286 MEMORY_USAGE_ESTIMATION_MAX));
287}
288
Craig Tiller20afa3d2016-10-17 14:52:14 -0700289/* returns true if all allocations are completed */
Craig Tiller3798e602016-10-21 14:39:27 -0700290static bool rq_alloc(grpc_exec_ctx *exec_ctx,
291 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700292 grpc_resource_user *resource_user;
Craig Tiller8abc7962016-10-26 21:31:29 -0700293 while ((resource_user = rulist_pop_head(resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700294 GRPC_RULIST_AWAITING_ALLOCATION))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700295 gpr_mu_lock(&resource_user->mu);
296 if (resource_user->free_pool < 0 &&
297 -resource_user->free_pool <= resource_quota->free_pool) {
298 int64_t amt = -resource_user->free_pool;
299 resource_user->free_pool = 0;
300 resource_quota->free_pool -= amt;
Craig Tillerb38bfc02016-10-24 13:21:39 -0700301 rq_update_estimate(resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700302 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700303 gpr_log(GPR_DEBUG, "RQ %s %s: grant alloc %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700304 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700305 resource_quota->name, resource_user->name, amt,
306 resource_quota->free_pool);
307 }
308 } else if (grpc_resource_quota_trace && resource_user->free_pool >= 0) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700309 gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700310 resource_quota->name, resource_user->name);
311 }
312 if (resource_user->free_pool >= 0) {
313 resource_user->allocating = false;
Craig Tiller91031da2016-12-28 15:44:25 -0800314 grpc_closure_list_sched(exec_ctx, &resource_user->on_allocated);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700315 gpr_mu_unlock(&resource_user->mu);
316 } else {
Craig Tiller3798e602016-10-21 14:39:27 -0700317 rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700318 gpr_mu_unlock(&resource_user->mu);
319 return false;
320 }
321 }
322 return true;
323}
324
325/* returns true if any memory could be reclaimed from buffers */
Craig Tiller6b5d6822016-10-25 16:13:26 -0700326static bool rq_reclaim_from_per_user_free_pool(
327 grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700328 grpc_resource_user *resource_user;
Craig Tiller8abc7962016-10-26 21:31:29 -0700329 while ((resource_user = rulist_pop_head(resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700330 GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700331 gpr_mu_lock(&resource_user->mu);
332 if (resource_user->free_pool > 0) {
333 int64_t amt = resource_user->free_pool;
334 resource_user->free_pool = 0;
335 resource_quota->free_pool += amt;
Craig Tillerb38bfc02016-10-24 13:21:39 -0700336 rq_update_estimate(resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700337 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700338 gpr_log(GPR_DEBUG, "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700339 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700340 resource_quota->name, resource_user->name, amt,
341 resource_quota->free_pool);
342 }
343 gpr_mu_unlock(&resource_user->mu);
344 return true;
345 } else {
346 gpr_mu_unlock(&resource_user->mu);
347 }
348 }
349 return false;
350}
351
Craig Tiller3798e602016-10-21 14:39:27 -0700352/* returns true if reclamation is proceeding */
353static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
354 grpc_resource_quota *resource_quota, bool destructive) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700355 if (resource_quota->reclaiming) return true;
Craig Tiller3798e602016-10-21 14:39:27 -0700356 grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE
357 : GRPC_RULIST_RECLAIMER_BENIGN;
Craig Tiller8abc7962016-10-26 21:31:29 -0700358 grpc_resource_user *resource_user = rulist_pop_head(resource_quota, list);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700359 if (resource_user == NULL) return false;
360 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700361 gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation",
Craig Tillerafcc8752016-10-18 16:10:06 -0700362 resource_quota->name, resource_user->name,
363 destructive ? "destructive" : "benign");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700364 }
365 resource_quota->reclaiming = true;
Craig Tillera59c16c2016-10-31 07:25:01 -0700366 grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700367 grpc_closure *c = resource_user->reclaimers[destructive];
Craig Tiller13b9a9a2016-12-02 12:43:09 -0800368 GPR_ASSERT(c);
369 resource_quota->debug_only_last_reclaimer_resource_user = resource_user;
370 resource_quota->debug_only_last_initiated_reclaimer = c;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700371 resource_user->reclaimers[destructive] = NULL;
372 grpc_closure_run(exec_ctx, c, GRPC_ERROR_NONE);
373 return true;
374}
375
376/*******************************************************************************
Craig Tiller3798e602016-10-21 14:39:27 -0700377 * ru_slice: a slice implementation that is backed by a grpc_resource_user
Craig Tiller20afa3d2016-10-17 14:52:14 -0700378 */
379
380typedef struct {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700381 grpc_slice_refcount base;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700382 gpr_refcount refs;
383 grpc_resource_user *resource_user;
384 size_t size;
Craig Tiller3798e602016-10-21 14:39:27 -0700385} ru_slice_refcount;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700386
Craig Tiller3798e602016-10-21 14:39:27 -0700387static void ru_slice_ref(void *p) {
388 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700389 gpr_ref(&rc->refs);
390}
391
Craig Tillera59c16c2016-10-31 07:25:01 -0700392static void ru_slice_unref(grpc_exec_ctx *exec_ctx, void *p) {
Craig Tiller3798e602016-10-21 14:39:27 -0700393 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700394 if (gpr_unref(&rc->refs)) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700395 grpc_resource_user_free(exec_ctx, rc->resource_user, rc->size);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700396 gpr_free(rc);
397 }
398}
399
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800400static const grpc_slice_refcount_vtable ru_slice_vtable = {
401 ru_slice_ref, ru_slice_unref, grpc_slice_default_eq_impl,
402 grpc_slice_default_hash_impl};
403
Craig Tillerd41a4a72016-10-26 16:16:06 -0700404static grpc_slice ru_slice_create(grpc_resource_user *resource_user,
Craig Tiller28b72422016-10-26 21:15:29 -0700405 size_t size) {
Craig Tiller3798e602016-10-21 14:39:27 -0700406 ru_slice_refcount *rc = gpr_malloc(sizeof(ru_slice_refcount) + size);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800407 rc->base.vtable = &ru_slice_vtable;
408 rc->base.sub_refcount = &rc->base;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700409 gpr_ref_init(&rc->refs, 1);
410 rc->resource_user = resource_user;
411 rc->size = size;
Craig Tillerd41a4a72016-10-26 16:16:06 -0700412 grpc_slice slice;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700413 slice.refcount = &rc->base;
414 slice.data.refcounted.bytes = (uint8_t *)(rc + 1);
415 slice.data.refcounted.length = size;
416 return slice;
417}
418
419/*******************************************************************************
Craig Tiller6b5d6822016-10-25 16:13:26 -0700420 * grpc_resource_quota internal implementation: resource user manipulation under
421 * the combiner
Craig Tiller20afa3d2016-10-17 14:52:14 -0700422 */
423
Craig Tiller6b5d6822016-10-25 16:13:26 -0700424static void ru_allocate(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
425 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700426 if (rulist_empty(resource_user->resource_quota,
427 GRPC_RULIST_AWAITING_ALLOCATION)) {
428 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700429 }
Craig Tiller3798e602016-10-21 14:39:27 -0700430 rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700431}
432
Craig Tiller6b5d6822016-10-25 16:13:26 -0700433static void ru_add_to_free_pool(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700434 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700435 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700436 if (!rulist_empty(resource_user->resource_quota,
437 GRPC_RULIST_AWAITING_ALLOCATION) &&
438 rulist_empty(resource_user->resource_quota,
439 GRPC_RULIST_NON_EMPTY_FREE_POOL)) {
440 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700441 }
Craig Tiller3798e602016-10-21 14:39:27 -0700442 rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700443}
444
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800445static bool ru_post_reclaimer(grpc_exec_ctx *exec_ctx,
446 grpc_resource_user *resource_user,
447 bool destructive) {
448 grpc_closure *closure = resource_user->new_reclaimers[destructive];
449 GPR_ASSERT(closure != NULL);
450 resource_user->new_reclaimers[destructive] = NULL;
451 GPR_ASSERT(resource_user->reclaimers[destructive] == NULL);
452 if (gpr_atm_acq_load(&resource_user->shutdown) > 0) {
Craig Tiller91031da2016-12-28 15:44:25 -0800453 grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_CANCELLED);
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800454 return false;
455 }
456 resource_user->reclaimers[destructive] = closure;
457 return true;
458}
459
Craig Tiller6b5d6822016-10-25 16:13:26 -0700460static void ru_post_benign_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700461 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700462 grpc_resource_user *resource_user = ru;
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800463 if (!ru_post_reclaimer(exec_ctx, resource_user, false)) return;
Craig Tiller3798e602016-10-21 14:39:27 -0700464 if (!rulist_empty(resource_user->resource_quota,
465 GRPC_RULIST_AWAITING_ALLOCATION) &&
466 rulist_empty(resource_user->resource_quota,
467 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
468 rulist_empty(resource_user->resource_quota,
469 GRPC_RULIST_RECLAIMER_BENIGN)) {
470 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700471 }
Craig Tiller3798e602016-10-21 14:39:27 -0700472 rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700473}
474
Craig Tiller6b5d6822016-10-25 16:13:26 -0700475static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700476 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700477 grpc_resource_user *resource_user = ru;
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800478 if (!ru_post_reclaimer(exec_ctx, resource_user, true)) return;
Craig Tiller3798e602016-10-21 14:39:27 -0700479 if (!rulist_empty(resource_user->resource_quota,
480 GRPC_RULIST_AWAITING_ALLOCATION) &&
481 rulist_empty(resource_user->resource_quota,
482 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
483 rulist_empty(resource_user->resource_quota,
484 GRPC_RULIST_RECLAIMER_BENIGN) &&
485 rulist_empty(resource_user->resource_quota,
486 GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) {
487 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700488 }
Craig Tiller3798e602016-10-21 14:39:27 -0700489 rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700490}
491
Craig Tillera947f1c2016-11-04 13:53:17 -0700492static void ru_shutdown(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
493 grpc_resource_user *resource_user = ru;
Craig Tiller91031da2016-12-28 15:44:25 -0800494 grpc_closure_sched(exec_ctx, resource_user->reclaimers[0],
495 GRPC_ERROR_CANCELLED);
496 grpc_closure_sched(exec_ctx, resource_user->reclaimers[1],
497 GRPC_ERROR_CANCELLED);
Craig Tillera947f1c2016-11-04 13:53:17 -0700498 resource_user->reclaimers[0] = NULL;
499 resource_user->reclaimers[1] = NULL;
Craig Tiller13b9a9a2016-12-02 12:43:09 -0800500 rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
501 rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
Craig Tillera947f1c2016-11-04 13:53:17 -0700502}
503
Craig Tiller6b5d6822016-10-25 16:13:26 -0700504static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
505 grpc_resource_user *resource_user = ru;
Craig Tillera947f1c2016-11-04 13:53:17 -0700506 GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->refs) == 0);
Craig Tiller3798e602016-10-21 14:39:27 -0700507 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
508 rulist_remove(resource_user, (grpc_rulist)i);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700509 }
Craig Tiller91031da2016-12-28 15:44:25 -0800510 grpc_closure_sched(exec_ctx, resource_user->reclaimers[0],
511 GRPC_ERROR_CANCELLED);
512 grpc_closure_sched(exec_ctx, resource_user->reclaimers[1],
513 GRPC_ERROR_CANCELLED);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700514 if (resource_user->free_pool != 0) {
515 resource_user->resource_quota->free_pool += resource_user->free_pool;
Craig Tiller3798e602016-10-21 14:39:27 -0700516 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700517 }
Craig Tiller87a7e1f2016-11-09 09:42:19 -0800518 grpc_resource_quota_unref_internal(exec_ctx, resource_user->resource_quota);
Craig Tillera947f1c2016-11-04 13:53:17 -0700519 gpr_mu_destroy(&resource_user->mu);
520 gpr_free(resource_user->name);
521 gpr_free(resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700522}
523
Craig Tiller6b5d6822016-10-25 16:13:26 -0700524static void ru_allocated_slices(grpc_exec_ctx *exec_ctx, void *arg,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700525 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700526 grpc_resource_user_slice_allocator *slice_allocator = arg;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700527 if (error == GRPC_ERROR_NONE) {
528 for (size_t i = 0; i < slice_allocator->count; i++) {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700529 grpc_slice_buffer_add_indexed(
Craig Tiller3798e602016-10-21 14:39:27 -0700530 slice_allocator->dest, ru_slice_create(slice_allocator->resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700531 slice_allocator->length));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700532 }
533 }
534 grpc_closure_run(exec_ctx, &slice_allocator->on_done, GRPC_ERROR_REF(error));
535}
536
Craig Tiller6b5d6822016-10-25 16:13:26 -0700537/*******************************************************************************
538 * grpc_resource_quota internal implementation: quota manipulation under the
539 * combiner
540 */
541
Craig Tiller20afa3d2016-10-17 14:52:14 -0700542typedef struct {
543 int64_t size;
544 grpc_resource_quota *resource_quota;
545 grpc_closure closure;
Craig Tiller3798e602016-10-21 14:39:27 -0700546} rq_resize_args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700547
Craig Tiller3798e602016-10-21 14:39:27 -0700548static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
549 rq_resize_args *a = args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700550 int64_t delta = a->size - a->resource_quota->size;
551 a->resource_quota->size += delta;
552 a->resource_quota->free_pool += delta;
Craig Tillerb38bfc02016-10-24 13:21:39 -0700553 rq_update_estimate(a->resource_quota);
Craig Tiller6b5d6822016-10-25 16:13:26 -0700554 rq_step_sched(exec_ctx, a->resource_quota);
Craig Tillera59c16c2016-10-31 07:25:01 -0700555 grpc_resource_quota_unref_internal(exec_ctx, a->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700556 gpr_free(a);
557}
558
Craig Tiller6b5d6822016-10-25 16:13:26 -0700559static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *rq,
Craig Tiller3798e602016-10-21 14:39:27 -0700560 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700561 grpc_resource_quota *resource_quota = rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700562 resource_quota->reclaiming = false;
Craig Tiller3798e602016-10-21 14:39:27 -0700563 rq_step_sched(exec_ctx, resource_quota);
Craig Tillera59c16c2016-10-31 07:25:01 -0700564 grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700565}
566
567/*******************************************************************************
568 * grpc_resource_quota api
569 */
570
Craig Tiller6b5d6822016-10-25 16:13:26 -0700571/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700572grpc_resource_quota *grpc_resource_quota_create(const char *name) {
573 grpc_resource_quota *resource_quota = gpr_malloc(sizeof(*resource_quota));
574 gpr_ref_init(&resource_quota->refs, 1);
575 resource_quota->combiner = grpc_combiner_create(NULL);
576 resource_quota->free_pool = INT64_MAX;
577 resource_quota->size = INT64_MAX;
578 resource_quota->step_scheduled = false;
579 resource_quota->reclaiming = false;
Craig Tillerb38bfc02016-10-24 13:21:39 -0700580 gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation, 0);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700581 if (name != NULL) {
582 resource_quota->name = gpr_strdup(name);
583 } else {
584 gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR,
585 (intptr_t)resource_quota);
586 }
Craig Tiller91031da2016-12-28 15:44:25 -0800587 grpc_closure_init(
588 &resource_quota->rq_step_closure, rq_step, resource_quota,
589 grpc_combiner_finally_scheduler(resource_quota->combiner, true));
Craig Tiller3798e602016-10-21 14:39:27 -0700590 grpc_closure_init(&resource_quota->rq_reclamation_done_closure,
Craig Tiller91031da2016-12-28 15:44:25 -0800591 rq_reclamation_done, resource_quota,
592 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller3798e602016-10-21 14:39:27 -0700593 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700594 resource_quota->roots[i] = NULL;
595 }
596 return resource_quota;
597}
598
Craig Tillera59c16c2016-10-31 07:25:01 -0700599void grpc_resource_quota_unref_internal(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700600 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700601 if (gpr_unref(&resource_quota->refs)) {
Craig Tiller3845e552017-02-09 15:19:28 -0800602 GRPC_COMBINER_UNREF(exec_ctx, resource_quota->combiner, "resource_quota");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700603 gpr_free(resource_quota->name);
604 gpr_free(resource_quota);
605 }
606}
607
Craig Tiller6b5d6822016-10-25 16:13:26 -0700608/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700609void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) {
610 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
Craig Tillera59c16c2016-10-31 07:25:01 -0700611 grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700612 grpc_exec_ctx_finish(&exec_ctx);
613}
614
Craig Tillera59c16c2016-10-31 07:25:01 -0700615grpc_resource_quota *grpc_resource_quota_ref_internal(
Craig Tillerafcc8752016-10-18 16:10:06 -0700616 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700617 gpr_ref(&resource_quota->refs);
618 return resource_quota;
619}
620
Craig Tiller6b5d6822016-10-25 16:13:26 -0700621/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700622void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700623 grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700624}
625
Craig Tillerb38bfc02016-10-24 13:21:39 -0700626double grpc_resource_quota_get_memory_pressure(
627 grpc_resource_quota *resource_quota) {
628 return ((double)(gpr_atm_no_barrier_load(
629 &resource_quota->memory_usage_estimation))) /
630 ((double)MEMORY_USAGE_ESTIMATION_MAX);
631}
632
Craig Tiller6b5d6822016-10-25 16:13:26 -0700633/* Public API */
Craig Tillerafcc8752016-10-18 16:10:06 -0700634void grpc_resource_quota_resize(grpc_resource_quota *resource_quota,
635 size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700636 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
Craig Tiller3798e602016-10-21 14:39:27 -0700637 rq_resize_args *a = gpr_malloc(sizeof(*a));
Craig Tillera59c16c2016-10-31 07:25:01 -0700638 a->resource_quota = grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700639 a->size = (int64_t)size;
Craig Tiller91031da2016-12-28 15:44:25 -0800640 grpc_closure_init(&a->closure, rq_resize, a, grpc_schedule_on_exec_ctx);
641 grpc_closure_sched(&exec_ctx, &a->closure, GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700642 grpc_exec_ctx_finish(&exec_ctx);
643}
644
645/*******************************************************************************
646 * grpc_resource_user channel args api
647 */
648
649grpc_resource_quota *grpc_resource_quota_from_channel_args(
650 const grpc_channel_args *channel_args) {
651 for (size_t i = 0; i < channel_args->num_args; i++) {
Craig Tiller153eaa72016-10-21 13:52:36 -0700652 if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700653 if (channel_args->args[i].type == GRPC_ARG_POINTER) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700654 return grpc_resource_quota_ref_internal(
Craig Tiller20afa3d2016-10-17 14:52:14 -0700655 channel_args->args[i].value.pointer.p);
656 } else {
Craig Tiller153eaa72016-10-21 13:52:36 -0700657 gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700658 }
659 }
660 }
661 return grpc_resource_quota_create(NULL);
662}
663
Craig Tiller6b5d6822016-10-25 16:13:26 -0700664static void *rq_copy(void *rq) {
665 grpc_resource_quota_ref(rq);
666 return rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700667}
668
Craig Tillera59c16c2016-10-31 07:25:01 -0700669static void rq_destroy(grpc_exec_ctx *exec_ctx, void *rq) {
670 grpc_resource_quota_unref_internal(exec_ctx, rq);
671}
Craig Tiller20afa3d2016-10-17 14:52:14 -0700672
Craig Tiller3798e602016-10-21 14:39:27 -0700673static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700674
675const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void) {
Craig Tiller3798e602016-10-21 14:39:27 -0700676 static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp};
Craig Tiller20afa3d2016-10-17 14:52:14 -0700677 return &vtable;
678}
679
680/*******************************************************************************
681 * grpc_resource_user api
682 */
683
Craig Tillera947f1c2016-11-04 13:53:17 -0700684grpc_resource_user *grpc_resource_user_create(
685 grpc_resource_quota *resource_quota, const char *name) {
686 grpc_resource_user *resource_user = gpr_malloc(sizeof(*resource_user));
Craig Tillerafcc8752016-10-18 16:10:06 -0700687 resource_user->resource_quota =
Craig Tillera59c16c2016-10-31 07:25:01 -0700688 grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller3798e602016-10-21 14:39:27 -0700689 grpc_closure_init(&resource_user->allocate_closure, &ru_allocate,
Craig Tiller91031da2016-12-28 15:44:25 -0800690 resource_user,
691 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700692 grpc_closure_init(&resource_user->add_to_free_pool_closure,
Craig Tiller91031da2016-12-28 15:44:25 -0800693 &ru_add_to_free_pool, resource_user,
694 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700695 grpc_closure_init(&resource_user->post_reclaimer_closure[0],
Craig Tiller91031da2016-12-28 15:44:25 -0800696 &ru_post_benign_reclaimer, resource_user,
697 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700698 grpc_closure_init(&resource_user->post_reclaimer_closure[1],
Craig Tiller91031da2016-12-28 15:44:25 -0800699 &ru_post_destructive_reclaimer, resource_user,
700 grpc_combiner_scheduler(resource_quota->combiner, false));
701 grpc_closure_init(&resource_user->destroy_closure, &ru_destroy, resource_user,
702 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700703 gpr_mu_init(&resource_user->mu);
Craig Tillera947f1c2016-11-04 13:53:17 -0700704 gpr_atm_rel_store(&resource_user->refs, 1);
705 gpr_atm_rel_store(&resource_user->shutdown, 0);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700706 resource_user->free_pool = 0;
707 grpc_closure_list_init(&resource_user->on_allocated);
708 resource_user->allocating = false;
709 resource_user->added_to_free_pool = false;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700710 resource_user->reclaimers[0] = NULL;
711 resource_user->reclaimers[1] = NULL;
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800712 resource_user->new_reclaimers[0] = NULL;
713 resource_user->new_reclaimers[1] = NULL;
Craig Tiller3798e602016-10-21 14:39:27 -0700714 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700715 resource_user->links[i].next = resource_user->links[i].prev = NULL;
716 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700717 if (name != NULL) {
718 resource_user->name = gpr_strdup(name);
719 } else {
720 gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR,
721 (intptr_t)resource_user);
722 }
Craig Tillera947f1c2016-11-04 13:53:17 -0700723 return resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700724}
725
Dan Borne955c1f2016-12-20 13:48:11 -0800726grpc_resource_quota *grpc_resource_user_quota(
Craig Tillereb30c802016-12-27 16:03:43 -0800727 grpc_resource_user *resource_user) {
728 return resource_user->resource_quota;
729}
730
Craig Tillera947f1c2016-11-04 13:53:17 -0700731static void ru_ref_by(grpc_resource_user *resource_user, gpr_atm amount) {
732 GPR_ASSERT(amount > 0);
733 GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&resource_user->refs, amount) != 0);
734}
735
736static void ru_unref_by(grpc_exec_ctx *exec_ctx,
737 grpc_resource_user *resource_user, gpr_atm amount) {
738 GPR_ASSERT(amount > 0);
739 gpr_atm old = gpr_atm_full_fetch_add(&resource_user->refs, -amount);
740 GPR_ASSERT(old >= amount);
741 if (old == amount) {
Craig Tiller91031da2016-12-28 15:44:25 -0800742 grpc_closure_sched(exec_ctx, &resource_user->destroy_closure,
743 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700744 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700745}
746
Craig Tillera947f1c2016-11-04 13:53:17 -0700747void grpc_resource_user_ref(grpc_resource_user *resource_user) {
748 ru_ref_by(resource_user, 1);
749}
750
751void grpc_resource_user_unref(grpc_exec_ctx *exec_ctx,
752 grpc_resource_user *resource_user) {
753 ru_unref_by(exec_ctx, resource_user, 1);
754}
755
756void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx,
757 grpc_resource_user *resource_user) {
758 if (gpr_atm_full_fetch_add(&resource_user->shutdown, 1) == 0) {
Craig Tiller91031da2016-12-28 15:44:25 -0800759 grpc_closure_sched(exec_ctx,
760 grpc_closure_create(
761 ru_shutdown, resource_user,
762 grpc_combiner_scheduler(
763 resource_user->resource_quota->combiner, false)),
764 GRPC_ERROR_NONE);
Craig Tillera947f1c2016-11-04 13:53:17 -0700765 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700766}
767
768void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700769 grpc_resource_user *resource_user, size_t size,
770 grpc_closure *optional_on_done) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700771 gpr_mu_lock(&resource_user->mu);
Craig Tillera947f1c2016-11-04 13:53:17 -0700772 ru_ref_by(resource_user, (gpr_atm)size);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700773 resource_user->free_pool -= (int64_t)size;
774 if (grpc_resource_quota_trace) {
Craig Tillera947f1c2016-11-04 13:53:17 -0700775 gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700776 resource_user->resource_quota->name, resource_user->name, size,
Craig Tillera947f1c2016-11-04 13:53:17 -0700777 resource_user->free_pool);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700778 }
779 if (resource_user->free_pool < 0) {
780 grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
781 GRPC_ERROR_NONE);
782 if (!resource_user->allocating) {
783 resource_user->allocating = true;
Craig Tiller91031da2016-12-28 15:44:25 -0800784 grpc_closure_sched(exec_ctx, &resource_user->allocate_closure,
785 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700786 }
787 } else {
Craig Tiller91031da2016-12-28 15:44:25 -0800788 grpc_closure_sched(exec_ctx, optional_on_done, GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700789 }
790 gpr_mu_unlock(&resource_user->mu);
791}
792
793void grpc_resource_user_free(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700794 grpc_resource_user *resource_user, size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700795 gpr_mu_lock(&resource_user->mu);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700796 bool was_zero_or_negative = resource_user->free_pool <= 0;
797 resource_user->free_pool += (int64_t)size;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700798 if (grpc_resource_quota_trace) {
Craig Tillera947f1c2016-11-04 13:53:17 -0700799 gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700800 resource_user->resource_quota->name, resource_user->name, size,
Craig Tillera947f1c2016-11-04 13:53:17 -0700801 resource_user->free_pool);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700802 }
803 bool is_bigger_than_zero = resource_user->free_pool > 0;
804 if (is_bigger_than_zero && was_zero_or_negative &&
805 !resource_user->added_to_free_pool) {
806 resource_user->added_to_free_pool = true;
Craig Tiller91031da2016-12-28 15:44:25 -0800807 grpc_closure_sched(exec_ctx, &resource_user->add_to_free_pool_closure,
808 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700809 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700810 gpr_mu_unlock(&resource_user->mu);
Craig Tillera947f1c2016-11-04 13:53:17 -0700811 ru_unref_by(exec_ctx, resource_user, (gpr_atm)size);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700812}
813
814void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700815 grpc_resource_user *resource_user,
816 bool destructive,
817 grpc_closure *closure) {
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800818 GPR_ASSERT(resource_user->new_reclaimers[destructive] == NULL);
819 resource_user->new_reclaimers[destructive] = closure;
Craig Tiller91031da2016-12-28 15:44:25 -0800820 grpc_closure_sched(exec_ctx,
821 &resource_user->post_reclaimer_closure[destructive],
822 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700823}
824
Craig Tiller3798e602016-10-21 14:39:27 -0700825void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
826 grpc_resource_user *resource_user) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700827 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700828 gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700829 resource_user->resource_quota->name, resource_user->name);
830 }
Craig Tiller91031da2016-12-28 15:44:25 -0800831 grpc_closure_sched(
832 exec_ctx, &resource_user->resource_quota->rq_reclamation_done_closure,
833 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700834}
835
836void grpc_resource_user_slice_allocator_init(
837 grpc_resource_user_slice_allocator *slice_allocator,
838 grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) {
Craig Tiller5082d7e2017-01-26 11:21:11 -0800839 grpc_closure_init(&slice_allocator->on_allocated, ru_allocated_slices,
840 slice_allocator, grpc_schedule_on_exec_ctx);
841 grpc_closure_init(&slice_allocator->on_done, cb, p,
842 grpc_schedule_on_exec_ctx);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700843 slice_allocator->resource_user = resource_user;
844}
845
846void grpc_resource_user_alloc_slices(
Craig Tillerafcc8752016-10-18 16:10:06 -0700847 grpc_exec_ctx *exec_ctx,
848 grpc_resource_user_slice_allocator *slice_allocator, size_t length,
Craig Tillerd41a4a72016-10-26 16:16:06 -0700849 size_t count, grpc_slice_buffer *dest) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700850 slice_allocator->length = length;
851 slice_allocator->count = count;
852 slice_allocator->dest = dest;
Craig Tillerafcc8752016-10-18 16:10:06 -0700853 grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user,
854 count * length, &slice_allocator->on_allocated);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700855}
murgatroid9969259d42016-10-31 14:34:10 -0700856
Craig Tiller32df4672016-11-04 08:21:56 -0700857grpc_slice grpc_resource_user_slice_malloc(grpc_exec_ctx *exec_ctx,
858 grpc_resource_user *resource_user,
859 size_t size) {
murgatroid9969259d42016-10-31 14:34:10 -0700860 grpc_resource_user_alloc(exec_ctx, resource_user, size, NULL);
861 return ru_slice_create(resource_user, size);
862}