blob: d5995a5ac6ecc1311908b6f4db8170eb479beaca [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
36#include <string.h>
37
38#include <grpc/support/alloc.h>
39#include <grpc/support/log.h>
40#include <grpc/support/string_util.h>
41#include <grpc/support/useful.h>
42
43#include "src/core/lib/iomgr/combiner.h"
44
45int grpc_resource_quota_trace = 0;
46
Craig Tillera947f1c2016-11-04 13:53:17 -070047/* Internal linked list pointers for a resource user */
48typedef struct {
49 grpc_resource_user *next;
50 grpc_resource_user *prev;
51} grpc_resource_user_link;
52
53/* Resource users are kept in (potentially) several intrusive linked lists
54 at once. These are the list names. */
55typedef enum {
56 /* Resource users that are waiting for an allocation */
57 GRPC_RULIST_AWAITING_ALLOCATION,
58 /* Resource users that have free memory available for internal reclamation */
59 GRPC_RULIST_NON_EMPTY_FREE_POOL,
60 /* Resource users that have published a benign reclamation is available */
61 GRPC_RULIST_RECLAIMER_BENIGN,
62 /* Resource users that have published a destructive reclamation is
63 available */
64 GRPC_RULIST_RECLAIMER_DESTRUCTIVE,
65 /* Number of lists: must be last */
66 GRPC_RULIST_COUNT
67} grpc_rulist;
68
69struct grpc_resource_user {
70 /* The quota this resource user consumes from */
71 grpc_resource_quota *resource_quota;
72
73 /* Closure to schedule an allocation under the resource quota combiner lock */
74 grpc_closure allocate_closure;
75 /* Closure to publish a non empty free pool under the resource quota combiner
76 lock */
77 grpc_closure add_to_free_pool_closure;
78
79 /* one ref for each ref call (released by grpc_resource_user_unref), and one
80 ref for each byte allocated (released by grpc_resource_user_free) */
81 gpr_atm refs;
82 /* is this resource user unlocked? starts at 0, increases for each shutdown
83 call */
84 gpr_atm shutdown;
85
86 gpr_mu mu;
87 /* The amount of memory (in bytes) this user has cached for its own use: to
88 avoid quota contention, each resource user can keep some memory in
89 addition to what it is immediately using (e.g., for caching), and the quota
90 can pull it back under memory pressure.
91 This value can become negative if more memory has been requested than
92 existed in the free pool, at which point the quota is consulted to bring
93 this value non-negative (asynchronously). */
94 int64_t free_pool;
95 /* A list of closures to call once free_pool becomes non-negative - ie when
96 all outstanding allocations have been granted. */
97 grpc_closure_list on_allocated;
98 /* True if we are currently trying to allocate from the quota, false if not */
99 bool allocating;
100 /* True if we are currently trying to add ourselves to the non-free quota
101 list, false otherwise */
102 bool added_to_free_pool;
103
104 /* Reclaimers: index 0 is the benign reclaimer, 1 is the destructive reclaimer
105 */
106 grpc_closure *reclaimers[2];
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800107 /* Reclaimers just posted: once we're in the combiner lock, we'll move them
108 to the array above */
109 grpc_closure *new_reclaimers[2];
Craig Tillera947f1c2016-11-04 13:53:17 -0700110 /* Trampoline closures to finish reclamation and re-enter the quota combiner
111 lock */
112 grpc_closure post_reclaimer_closure[2];
113
114 /* Closure to execute under the quota combiner to de-register and shutdown the
115 resource user */
116 grpc_closure destroy_closure;
117
118 /* Links in the various grpc_rulist lists */
119 grpc_resource_user_link links[GRPC_RULIST_COUNT];
120
121 /* The name of this resource user, for debugging/tracing */
122 char *name;
123};
124
Craig Tiller20afa3d2016-10-17 14:52:14 -0700125struct grpc_resource_quota {
Craig Tiller3798e602016-10-21 14:39:27 -0700126 /* refcount */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700127 gpr_refcount refs;
128
Craig Tiller3798e602016-10-21 14:39:27 -0700129 /* Master combiner lock: all activity on a quota executes under this combiner
Craig Tiller6b5d6822016-10-25 16:13:26 -0700130 * (so no mutex is needed for this data structure)
Craig Tiller3798e602016-10-21 14:39:27 -0700131 */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700132 grpc_combiner *combiner;
Craig Tiller3798e602016-10-21 14:39:27 -0700133 /* Size of the resource quota */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700134 int64_t size;
Craig Tiller3798e602016-10-21 14:39:27 -0700135 /* Amount of free memory in the resource quota */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700136 int64_t free_pool;
137
Craig Tiller3798e602016-10-21 14:39:27 -0700138 /* Has rq_step been scheduled to occur? */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700139 bool step_scheduled;
Craig Tiller3798e602016-10-21 14:39:27 -0700140 /* Are we currently reclaiming memory */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700141 bool reclaiming;
Craig Tiller3798e602016-10-21 14:39:27 -0700142 /* Closure around rq_step */
143 grpc_closure rq_step_closure;
144 /* Closure around rq_reclamation_done */
145 grpc_closure rq_reclamation_done_closure;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700146
Craig Tiller13b9a9a2016-12-02 12:43:09 -0800147 /* This is only really usable for debugging: it's always a stale pointer, but
148 a stale pointer that might just be fresh enough to guide us to where the
149 reclamation system is stuck */
150 grpc_closure *debug_only_last_initiated_reclaimer;
151 grpc_resource_user *debug_only_last_reclaimer_resource_user;
152
Craig Tiller3798e602016-10-21 14:39:27 -0700153 /* Roots of all resource user lists */
154 grpc_resource_user *roots[GRPC_RULIST_COUNT];
Craig Tiller20afa3d2016-10-17 14:52:14 -0700155
156 char *name;
157};
158
159/*******************************************************************************
160 * list management
161 */
162
Craig Tiller6b5d6822016-10-25 16:13:26 -0700163static void rulist_add_head(grpc_resource_user *resource_user,
Craig Tiller3798e602016-10-21 14:39:27 -0700164 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700165 grpc_resource_quota *resource_quota = resource_user->resource_quota;
166 grpc_resource_user **root = &resource_quota->roots[list];
167 if (*root == NULL) {
168 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -0700169 resource_user->links[list].next = resource_user->links[list].prev =
170 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700171 } else {
172 resource_user->links[list].next = *root;
173 resource_user->links[list].prev = (*root)->links[list].prev;
174 resource_user->links[list].next->links[list].prev =
175 resource_user->links[list].prev->links[list].next = resource_user;
Craig Tiller8abc7962016-10-26 21:31:29 -0700176 *root = resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700177 }
178}
179
Craig Tiller6b5d6822016-10-25 16:13:26 -0700180static void rulist_add_tail(grpc_resource_user *resource_user,
Craig Tiller3798e602016-10-21 14:39:27 -0700181 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700182 grpc_resource_quota *resource_quota = resource_user->resource_quota;
183 grpc_resource_user **root = &resource_quota->roots[list];
184 if (*root == NULL) {
185 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -0700186 resource_user->links[list].next = resource_user->links[list].prev =
187 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700188 } else {
189 resource_user->links[list].next = (*root)->links[list].next;
190 resource_user->links[list].prev = *root;
191 resource_user->links[list].next->links[list].prev =
192 resource_user->links[list].prev->links[list].next = resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700193 }
194}
195
Craig Tiller3798e602016-10-21 14:39:27 -0700196static bool rulist_empty(grpc_resource_quota *resource_quota,
197 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700198 return resource_quota->roots[list] == NULL;
199}
200
Craig Tiller8abc7962016-10-26 21:31:29 -0700201static grpc_resource_user *rulist_pop_head(grpc_resource_quota *resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700202 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700203 grpc_resource_user **root = &resource_quota->roots[list];
204 grpc_resource_user *resource_user = *root;
205 if (resource_user == NULL) {
206 return NULL;
207 }
208 if (resource_user->links[list].next == resource_user) {
209 *root = NULL;
210 } else {
211 resource_user->links[list].next->links[list].prev =
212 resource_user->links[list].prev;
213 resource_user->links[list].prev->links[list].next =
214 resource_user->links[list].next;
215 *root = resource_user->links[list].next;
216 }
217 resource_user->links[list].next = resource_user->links[list].prev = NULL;
218 return resource_user;
219}
220
Craig Tiller3798e602016-10-21 14:39:27 -0700221static void rulist_remove(grpc_resource_user *resource_user, grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700222 if (resource_user->links[list].next == NULL) return;
223 grpc_resource_quota *resource_quota = resource_user->resource_quota;
224 if (resource_quota->roots[list] == resource_user) {
225 resource_quota->roots[list] = resource_user->links[list].next;
226 if (resource_quota->roots[list] == resource_user) {
227 resource_quota->roots[list] = NULL;
228 }
229 }
230 resource_user->links[list].next->links[list].prev =
231 resource_user->links[list].prev;
232 resource_user->links[list].prev->links[list].next =
233 resource_user->links[list].next;
Craig Tillerab5a4992016-12-02 17:17:07 -0800234 resource_user->links[list].next = resource_user->links[list].prev = NULL;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700235}
236
237/*******************************************************************************
Craig Tiller6b5d6822016-10-25 16:13:26 -0700238 * resource quota state machine
Craig Tiller20afa3d2016-10-17 14:52:14 -0700239 */
240
Craig Tiller3798e602016-10-21 14:39:27 -0700241static bool rq_alloc(grpc_exec_ctx *exec_ctx,
242 grpc_resource_quota *resource_quota);
Craig Tiller6b5d6822016-10-25 16:13:26 -0700243static bool rq_reclaim_from_per_user_free_pool(
244 grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota);
Craig Tiller3798e602016-10-21 14:39:27 -0700245static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
246 grpc_resource_quota *resource_quota, bool destructive);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700247
Craig Tiller6b5d6822016-10-25 16:13:26 -0700248static void rq_step(grpc_exec_ctx *exec_ctx, void *rq, grpc_error *error) {
249 grpc_resource_quota *resource_quota = rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700250 resource_quota->step_scheduled = false;
251 do {
Craig Tiller3798e602016-10-21 14:39:27 -0700252 if (rq_alloc(exec_ctx, resource_quota)) goto done;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700253 } while (rq_reclaim_from_per_user_free_pool(exec_ctx, resource_quota));
Sree Kuchibhotla894db552016-10-28 18:25:24 -0700254
255 if (!rq_reclaim(exec_ctx, resource_quota, false)) {
256 rq_reclaim(exec_ctx, resource_quota, true);
257 }
258
Craig Tiller20afa3d2016-10-17 14:52:14 -0700259done:
Craig Tillera59c16c2016-10-31 07:25:01 -0700260 grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700261}
262
Craig Tiller3798e602016-10-21 14:39:27 -0700263static void rq_step_sched(grpc_exec_ctx *exec_ctx,
264 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700265 if (resource_quota->step_scheduled) return;
266 resource_quota->step_scheduled = true;
Craig Tillera59c16c2016-10-31 07:25:01 -0700267 grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller91031da2016-12-28 15:44:25 -0800268 grpc_closure_sched(exec_ctx, &resource_quota->rq_step_closure,
269 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700270}
271
272/* returns true if all allocations are completed */
Craig Tiller3798e602016-10-21 14:39:27 -0700273static bool rq_alloc(grpc_exec_ctx *exec_ctx,
274 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700275 grpc_resource_user *resource_user;
Craig Tiller8abc7962016-10-26 21:31:29 -0700276 while ((resource_user = rulist_pop_head(resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700277 GRPC_RULIST_AWAITING_ALLOCATION))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700278 gpr_mu_lock(&resource_user->mu);
279 if (resource_user->free_pool < 0 &&
280 -resource_user->free_pool <= resource_quota->free_pool) {
281 int64_t amt = -resource_user->free_pool;
282 resource_user->free_pool = 0;
283 resource_quota->free_pool -= amt;
284 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700285 gpr_log(GPR_DEBUG, "RQ %s %s: grant alloc %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700286 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700287 resource_quota->name, resource_user->name, amt,
288 resource_quota->free_pool);
289 }
290 } else if (grpc_resource_quota_trace && resource_user->free_pool >= 0) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700291 gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700292 resource_quota->name, resource_user->name);
293 }
294 if (resource_user->free_pool >= 0) {
295 resource_user->allocating = false;
Craig Tiller91031da2016-12-28 15:44:25 -0800296 grpc_closure_list_sched(exec_ctx, &resource_user->on_allocated);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700297 gpr_mu_unlock(&resource_user->mu);
298 } else {
Craig Tiller8abc7962016-10-26 21:31:29 -0700299 rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700300 gpr_mu_unlock(&resource_user->mu);
301 return false;
302 }
303 }
304 return true;
305}
306
307/* returns true if any memory could be reclaimed from buffers */
Craig Tiller6b5d6822016-10-25 16:13:26 -0700308static bool rq_reclaim_from_per_user_free_pool(
309 grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700310 grpc_resource_user *resource_user;
Craig Tiller8abc7962016-10-26 21:31:29 -0700311 while ((resource_user = rulist_pop_head(resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700312 GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700313 gpr_mu_lock(&resource_user->mu);
314 if (resource_user->free_pool > 0) {
315 int64_t amt = resource_user->free_pool;
316 resource_user->free_pool = 0;
317 resource_quota->free_pool += amt;
318 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700319 gpr_log(GPR_DEBUG, "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700320 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700321 resource_quota->name, resource_user->name, amt,
322 resource_quota->free_pool);
323 }
324 gpr_mu_unlock(&resource_user->mu);
325 return true;
326 } else {
327 gpr_mu_unlock(&resource_user->mu);
328 }
329 }
330 return false;
331}
332
Craig Tiller3798e602016-10-21 14:39:27 -0700333/* returns true if reclamation is proceeding */
334static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
335 grpc_resource_quota *resource_quota, bool destructive) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700336 if (resource_quota->reclaiming) return true;
Craig Tiller3798e602016-10-21 14:39:27 -0700337 grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE
338 : GRPC_RULIST_RECLAIMER_BENIGN;
Craig Tiller8abc7962016-10-26 21:31:29 -0700339 grpc_resource_user *resource_user = rulist_pop_head(resource_quota, list);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700340 if (resource_user == NULL) return false;
341 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700342 gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation",
Craig Tillerafcc8752016-10-18 16:10:06 -0700343 resource_quota->name, resource_user->name,
344 destructive ? "destructive" : "benign");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700345 }
346 resource_quota->reclaiming = true;
Craig Tillera59c16c2016-10-31 07:25:01 -0700347 grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700348 grpc_closure *c = resource_user->reclaimers[destructive];
Craig Tiller13b9a9a2016-12-02 12:43:09 -0800349 GPR_ASSERT(c);
350 resource_quota->debug_only_last_reclaimer_resource_user = resource_user;
351 resource_quota->debug_only_last_initiated_reclaimer = c;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700352 resource_user->reclaimers[destructive] = NULL;
353 grpc_closure_run(exec_ctx, c, GRPC_ERROR_NONE);
354 return true;
355}
356
357/*******************************************************************************
Craig Tiller3798e602016-10-21 14:39:27 -0700358 * ru_slice: a slice implementation that is backed by a grpc_resource_user
Craig Tiller20afa3d2016-10-17 14:52:14 -0700359 */
360
361typedef struct {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700362 grpc_slice_refcount base;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700363 gpr_refcount refs;
364 grpc_resource_user *resource_user;
365 size_t size;
Craig Tiller3798e602016-10-21 14:39:27 -0700366} ru_slice_refcount;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700367
Craig Tiller3798e602016-10-21 14:39:27 -0700368static void ru_slice_ref(void *p) {
369 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700370 gpr_ref(&rc->refs);
371}
372
Craig Tillera59c16c2016-10-31 07:25:01 -0700373static void ru_slice_unref(grpc_exec_ctx *exec_ctx, void *p) {
Craig Tiller3798e602016-10-21 14:39:27 -0700374 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700375 if (gpr_unref(&rc->refs)) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700376 grpc_resource_user_free(exec_ctx, rc->resource_user, rc->size);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700377 gpr_free(rc);
378 }
379}
380
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800381static const grpc_slice_refcount_vtable ru_slice_vtable = {
382 ru_slice_ref, ru_slice_unref, grpc_slice_default_eq_impl,
383 grpc_slice_default_hash_impl};
384
Craig Tillerd41a4a72016-10-26 16:16:06 -0700385static grpc_slice ru_slice_create(grpc_resource_user *resource_user,
Craig Tiller28b72422016-10-26 21:15:29 -0700386 size_t size) {
Craig Tiller3798e602016-10-21 14:39:27 -0700387 ru_slice_refcount *rc = gpr_malloc(sizeof(ru_slice_refcount) + size);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800388 rc->base.vtable = &ru_slice_vtable;
389 rc->base.sub_refcount = &rc->base;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700390 gpr_ref_init(&rc->refs, 1);
391 rc->resource_user = resource_user;
392 rc->size = size;
Craig Tillerd41a4a72016-10-26 16:16:06 -0700393 grpc_slice slice;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700394 slice.refcount = &rc->base;
395 slice.data.refcounted.bytes = (uint8_t *)(rc + 1);
396 slice.data.refcounted.length = size;
397 return slice;
398}
399
400/*******************************************************************************
Craig Tiller6b5d6822016-10-25 16:13:26 -0700401 * grpc_resource_quota internal implementation: resource user manipulation under
402 * the combiner
Craig Tiller20afa3d2016-10-17 14:52:14 -0700403 */
404
Craig Tiller6b5d6822016-10-25 16:13:26 -0700405static void ru_allocate(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
406 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700407 if (rulist_empty(resource_user->resource_quota,
408 GRPC_RULIST_AWAITING_ALLOCATION)) {
409 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700410 }
Craig Tiller8abc7962016-10-26 21:31:29 -0700411 rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700412}
413
Craig Tiller6b5d6822016-10-25 16:13:26 -0700414static void ru_add_to_free_pool(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700415 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700416 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700417 if (!rulist_empty(resource_user->resource_quota,
418 GRPC_RULIST_AWAITING_ALLOCATION) &&
419 rulist_empty(resource_user->resource_quota,
420 GRPC_RULIST_NON_EMPTY_FREE_POOL)) {
421 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700422 }
Craig Tiller8abc7962016-10-26 21:31:29 -0700423 rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700424}
425
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800426static bool ru_post_reclaimer(grpc_exec_ctx *exec_ctx,
427 grpc_resource_user *resource_user,
428 bool destructive) {
429 grpc_closure *closure = resource_user->new_reclaimers[destructive];
430 GPR_ASSERT(closure != NULL);
431 resource_user->new_reclaimers[destructive] = NULL;
432 GPR_ASSERT(resource_user->reclaimers[destructive] == NULL);
433 if (gpr_atm_acq_load(&resource_user->shutdown) > 0) {
Craig Tiller91031da2016-12-28 15:44:25 -0800434 grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_CANCELLED);
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800435 return false;
436 }
437 resource_user->reclaimers[destructive] = closure;
438 return true;
439}
440
Craig Tiller6b5d6822016-10-25 16:13:26 -0700441static void ru_post_benign_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700442 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700443 grpc_resource_user *resource_user = ru;
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800444 if (!ru_post_reclaimer(exec_ctx, resource_user, false)) return;
Craig Tiller3798e602016-10-21 14:39:27 -0700445 if (!rulist_empty(resource_user->resource_quota,
446 GRPC_RULIST_AWAITING_ALLOCATION) &&
447 rulist_empty(resource_user->resource_quota,
448 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
449 rulist_empty(resource_user->resource_quota,
450 GRPC_RULIST_RECLAIMER_BENIGN)) {
451 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700452 }
Craig Tiller8abc7962016-10-26 21:31:29 -0700453 rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700454}
455
Craig Tiller6b5d6822016-10-25 16:13:26 -0700456static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700457 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700458 grpc_resource_user *resource_user = ru;
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800459 if (!ru_post_reclaimer(exec_ctx, resource_user, true)) return;
Craig Tiller3798e602016-10-21 14:39:27 -0700460 if (!rulist_empty(resource_user->resource_quota,
461 GRPC_RULIST_AWAITING_ALLOCATION) &&
462 rulist_empty(resource_user->resource_quota,
463 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
464 rulist_empty(resource_user->resource_quota,
465 GRPC_RULIST_RECLAIMER_BENIGN) &&
466 rulist_empty(resource_user->resource_quota,
467 GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) {
468 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700469 }
Craig Tiller8abc7962016-10-26 21:31:29 -0700470 rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700471}
472
Craig Tillera947f1c2016-11-04 13:53:17 -0700473static void ru_shutdown(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
474 grpc_resource_user *resource_user = ru;
Craig Tiller91031da2016-12-28 15:44:25 -0800475 grpc_closure_sched(exec_ctx, resource_user->reclaimers[0],
476 GRPC_ERROR_CANCELLED);
477 grpc_closure_sched(exec_ctx, resource_user->reclaimers[1],
478 GRPC_ERROR_CANCELLED);
Craig Tillera947f1c2016-11-04 13:53:17 -0700479 resource_user->reclaimers[0] = NULL;
480 resource_user->reclaimers[1] = NULL;
Craig Tiller13b9a9a2016-12-02 12:43:09 -0800481 rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
482 rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
Craig Tillera947f1c2016-11-04 13:53:17 -0700483}
484
Craig Tiller6b5d6822016-10-25 16:13:26 -0700485static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
486 grpc_resource_user *resource_user = ru;
Craig Tillera947f1c2016-11-04 13:53:17 -0700487 GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->refs) == 0);
Craig Tiller3798e602016-10-21 14:39:27 -0700488 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
489 rulist_remove(resource_user, (grpc_rulist)i);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700490 }
Craig Tiller91031da2016-12-28 15:44:25 -0800491 grpc_closure_sched(exec_ctx, resource_user->reclaimers[0],
492 GRPC_ERROR_CANCELLED);
493 grpc_closure_sched(exec_ctx, resource_user->reclaimers[1],
494 GRPC_ERROR_CANCELLED);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700495 if (resource_user->free_pool != 0) {
496 resource_user->resource_quota->free_pool += resource_user->free_pool;
Craig Tiller3798e602016-10-21 14:39:27 -0700497 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700498 }
Craig Tiller87a7e1f2016-11-09 09:42:19 -0800499 grpc_resource_quota_unref_internal(exec_ctx, resource_user->resource_quota);
Craig Tillera947f1c2016-11-04 13:53:17 -0700500 gpr_mu_destroy(&resource_user->mu);
501 gpr_free(resource_user->name);
502 gpr_free(resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700503}
504
Craig Tiller6b5d6822016-10-25 16:13:26 -0700505static void ru_allocated_slices(grpc_exec_ctx *exec_ctx, void *arg,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700506 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700507 grpc_resource_user_slice_allocator *slice_allocator = arg;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700508 if (error == GRPC_ERROR_NONE) {
509 for (size_t i = 0; i < slice_allocator->count; i++) {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700510 grpc_slice_buffer_add_indexed(
Craig Tiller3798e602016-10-21 14:39:27 -0700511 slice_allocator->dest, ru_slice_create(slice_allocator->resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700512 slice_allocator->length));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700513 }
514 }
515 grpc_closure_run(exec_ctx, &slice_allocator->on_done, GRPC_ERROR_REF(error));
516}
517
Craig Tiller6b5d6822016-10-25 16:13:26 -0700518/*******************************************************************************
519 * grpc_resource_quota internal implementation: quota manipulation under the
520 * combiner
521 */
522
Craig Tiller20afa3d2016-10-17 14:52:14 -0700523typedef struct {
524 int64_t size;
525 grpc_resource_quota *resource_quota;
526 grpc_closure closure;
Craig Tiller3798e602016-10-21 14:39:27 -0700527} rq_resize_args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700528
Craig Tiller3798e602016-10-21 14:39:27 -0700529static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
530 rq_resize_args *a = args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700531 int64_t delta = a->size - a->resource_quota->size;
532 a->resource_quota->size += delta;
533 a->resource_quota->free_pool += delta;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700534 rq_step_sched(exec_ctx, a->resource_quota);
Craig Tillera59c16c2016-10-31 07:25:01 -0700535 grpc_resource_quota_unref_internal(exec_ctx, a->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700536 gpr_free(a);
537}
538
Craig Tiller6b5d6822016-10-25 16:13:26 -0700539static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *rq,
Craig Tiller3798e602016-10-21 14:39:27 -0700540 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700541 grpc_resource_quota *resource_quota = rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700542 resource_quota->reclaiming = false;
Craig Tiller3798e602016-10-21 14:39:27 -0700543 rq_step_sched(exec_ctx, resource_quota);
Craig Tillera59c16c2016-10-31 07:25:01 -0700544 grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700545}
546
547/*******************************************************************************
548 * grpc_resource_quota api
549 */
550
Craig Tiller6b5d6822016-10-25 16:13:26 -0700551/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700552grpc_resource_quota *grpc_resource_quota_create(const char *name) {
553 grpc_resource_quota *resource_quota = gpr_malloc(sizeof(*resource_quota));
554 gpr_ref_init(&resource_quota->refs, 1);
555 resource_quota->combiner = grpc_combiner_create(NULL);
556 resource_quota->free_pool = INT64_MAX;
557 resource_quota->size = INT64_MAX;
558 resource_quota->step_scheduled = false;
559 resource_quota->reclaiming = false;
560 if (name != NULL) {
561 resource_quota->name = gpr_strdup(name);
562 } else {
563 gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR,
564 (intptr_t)resource_quota);
565 }
Craig Tiller91031da2016-12-28 15:44:25 -0800566 grpc_closure_init(
567 &resource_quota->rq_step_closure, rq_step, resource_quota,
568 grpc_combiner_finally_scheduler(resource_quota->combiner, true));
Craig Tiller3798e602016-10-21 14:39:27 -0700569 grpc_closure_init(&resource_quota->rq_reclamation_done_closure,
Craig Tiller91031da2016-12-28 15:44:25 -0800570 rq_reclamation_done, resource_quota,
571 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller3798e602016-10-21 14:39:27 -0700572 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700573 resource_quota->roots[i] = NULL;
574 }
575 return resource_quota;
576}
577
Craig Tillera59c16c2016-10-31 07:25:01 -0700578void grpc_resource_quota_unref_internal(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700579 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700580 if (gpr_unref(&resource_quota->refs)) {
581 grpc_combiner_destroy(exec_ctx, resource_quota->combiner);
582 gpr_free(resource_quota->name);
583 gpr_free(resource_quota);
584 }
585}
586
Craig Tiller6b5d6822016-10-25 16:13:26 -0700587/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700588void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) {
589 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
Craig Tillera59c16c2016-10-31 07:25:01 -0700590 grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700591 grpc_exec_ctx_finish(&exec_ctx);
592}
593
Craig Tillera59c16c2016-10-31 07:25:01 -0700594grpc_resource_quota *grpc_resource_quota_ref_internal(
Craig Tillerafcc8752016-10-18 16:10:06 -0700595 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700596 gpr_ref(&resource_quota->refs);
597 return resource_quota;
598}
599
Craig Tiller6b5d6822016-10-25 16:13:26 -0700600/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700601void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700602 grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700603}
604
Craig Tiller6b5d6822016-10-25 16:13:26 -0700605/* Public API */
Craig Tillerafcc8752016-10-18 16:10:06 -0700606void grpc_resource_quota_resize(grpc_resource_quota *resource_quota,
607 size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700608 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
Craig Tiller3798e602016-10-21 14:39:27 -0700609 rq_resize_args *a = gpr_malloc(sizeof(*a));
Craig Tillera59c16c2016-10-31 07:25:01 -0700610 a->resource_quota = grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700611 a->size = (int64_t)size;
Craig Tiller91031da2016-12-28 15:44:25 -0800612 grpc_closure_init(&a->closure, rq_resize, a, grpc_schedule_on_exec_ctx);
613 grpc_closure_sched(&exec_ctx, &a->closure, GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700614 grpc_exec_ctx_finish(&exec_ctx);
615}
616
617/*******************************************************************************
618 * grpc_resource_user channel args api
619 */
620
621grpc_resource_quota *grpc_resource_quota_from_channel_args(
622 const grpc_channel_args *channel_args) {
623 for (size_t i = 0; i < channel_args->num_args; i++) {
Craig Tiller153eaa72016-10-21 13:52:36 -0700624 if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700625 if (channel_args->args[i].type == GRPC_ARG_POINTER) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700626 return grpc_resource_quota_ref_internal(
Craig Tiller20afa3d2016-10-17 14:52:14 -0700627 channel_args->args[i].value.pointer.p);
628 } else {
Craig Tiller153eaa72016-10-21 13:52:36 -0700629 gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700630 }
631 }
632 }
633 return grpc_resource_quota_create(NULL);
634}
635
Craig Tiller6b5d6822016-10-25 16:13:26 -0700636static void *rq_copy(void *rq) {
637 grpc_resource_quota_ref(rq);
638 return rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700639}
640
Craig Tillera59c16c2016-10-31 07:25:01 -0700641static void rq_destroy(grpc_exec_ctx *exec_ctx, void *rq) {
642 grpc_resource_quota_unref_internal(exec_ctx, rq);
643}
Craig Tiller20afa3d2016-10-17 14:52:14 -0700644
Craig Tiller3798e602016-10-21 14:39:27 -0700645static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700646
647const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void) {
Craig Tiller3798e602016-10-21 14:39:27 -0700648 static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp};
Craig Tiller20afa3d2016-10-17 14:52:14 -0700649 return &vtable;
650}
651
652/*******************************************************************************
653 * grpc_resource_user api
654 */
655
Craig Tillera947f1c2016-11-04 13:53:17 -0700656grpc_resource_user *grpc_resource_user_create(
657 grpc_resource_quota *resource_quota, const char *name) {
658 grpc_resource_user *resource_user = gpr_malloc(sizeof(*resource_user));
Craig Tillerafcc8752016-10-18 16:10:06 -0700659 resource_user->resource_quota =
Craig Tillera59c16c2016-10-31 07:25:01 -0700660 grpc_resource_quota_ref_internal(resource_quota);
Craig Tiller3798e602016-10-21 14:39:27 -0700661 grpc_closure_init(&resource_user->allocate_closure, &ru_allocate,
Craig Tiller91031da2016-12-28 15:44:25 -0800662 resource_user,
663 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700664 grpc_closure_init(&resource_user->add_to_free_pool_closure,
Craig Tiller91031da2016-12-28 15:44:25 -0800665 &ru_add_to_free_pool, resource_user,
666 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700667 grpc_closure_init(&resource_user->post_reclaimer_closure[0],
Craig Tiller91031da2016-12-28 15:44:25 -0800668 &ru_post_benign_reclaimer, resource_user,
669 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700670 grpc_closure_init(&resource_user->post_reclaimer_closure[1],
Craig Tiller91031da2016-12-28 15:44:25 -0800671 &ru_post_destructive_reclaimer, resource_user,
672 grpc_combiner_scheduler(resource_quota->combiner, false));
673 grpc_closure_init(&resource_user->destroy_closure, &ru_destroy, resource_user,
674 grpc_combiner_scheduler(resource_quota->combiner, false));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700675 gpr_mu_init(&resource_user->mu);
Craig Tillera947f1c2016-11-04 13:53:17 -0700676 gpr_atm_rel_store(&resource_user->refs, 1);
677 gpr_atm_rel_store(&resource_user->shutdown, 0);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700678 resource_user->free_pool = 0;
679 grpc_closure_list_init(&resource_user->on_allocated);
680 resource_user->allocating = false;
681 resource_user->added_to_free_pool = false;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700682 resource_user->reclaimers[0] = NULL;
683 resource_user->reclaimers[1] = NULL;
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800684 resource_user->new_reclaimers[0] = NULL;
685 resource_user->new_reclaimers[1] = NULL;
Craig Tiller3798e602016-10-21 14:39:27 -0700686 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700687 resource_user->links[i].next = resource_user->links[i].prev = NULL;
688 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700689 if (name != NULL) {
690 resource_user->name = gpr_strdup(name);
691 } else {
692 gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR,
693 (intptr_t)resource_user);
694 }
Craig Tillera947f1c2016-11-04 13:53:17 -0700695 return resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700696}
697
Dan Borne955c1f2016-12-20 13:48:11 -0800698grpc_resource_quota *grpc_resource_user_quota(
699 grpc_resource_user *resource_user) {
700 return resource_user->resource_quota;
701}
702
Craig Tillera947f1c2016-11-04 13:53:17 -0700703static void ru_ref_by(grpc_resource_user *resource_user, gpr_atm amount) {
704 GPR_ASSERT(amount > 0);
705 GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&resource_user->refs, amount) != 0);
706}
707
708static void ru_unref_by(grpc_exec_ctx *exec_ctx,
709 grpc_resource_user *resource_user, gpr_atm amount) {
710 GPR_ASSERT(amount > 0);
711 gpr_atm old = gpr_atm_full_fetch_add(&resource_user->refs, -amount);
712 GPR_ASSERT(old >= amount);
713 if (old == amount) {
Craig Tiller91031da2016-12-28 15:44:25 -0800714 grpc_closure_sched(exec_ctx, &resource_user->destroy_closure,
715 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700716 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700717}
718
Craig Tillera947f1c2016-11-04 13:53:17 -0700719void grpc_resource_user_ref(grpc_resource_user *resource_user) {
720 ru_ref_by(resource_user, 1);
721}
722
723void grpc_resource_user_unref(grpc_exec_ctx *exec_ctx,
724 grpc_resource_user *resource_user) {
725 ru_unref_by(exec_ctx, resource_user, 1);
726}
727
728void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx,
729 grpc_resource_user *resource_user) {
730 if (gpr_atm_full_fetch_add(&resource_user->shutdown, 1) == 0) {
Craig Tiller91031da2016-12-28 15:44:25 -0800731 grpc_closure_sched(exec_ctx,
732 grpc_closure_create(
733 ru_shutdown, resource_user,
734 grpc_combiner_scheduler(
735 resource_user->resource_quota->combiner, false)),
736 GRPC_ERROR_NONE);
Craig Tillera947f1c2016-11-04 13:53:17 -0700737 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700738}
739
740void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700741 grpc_resource_user *resource_user, size_t size,
742 grpc_closure *optional_on_done) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700743 gpr_mu_lock(&resource_user->mu);
Craig Tillera947f1c2016-11-04 13:53:17 -0700744 ru_ref_by(resource_user, (gpr_atm)size);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700745 resource_user->free_pool -= (int64_t)size;
746 if (grpc_resource_quota_trace) {
Craig Tillera947f1c2016-11-04 13:53:17 -0700747 gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700748 resource_user->resource_quota->name, resource_user->name, size,
Craig Tillera947f1c2016-11-04 13:53:17 -0700749 resource_user->free_pool);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700750 }
751 if (resource_user->free_pool < 0) {
752 grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
753 GRPC_ERROR_NONE);
754 if (!resource_user->allocating) {
755 resource_user->allocating = true;
Craig Tiller91031da2016-12-28 15:44:25 -0800756 grpc_closure_sched(exec_ctx, &resource_user->allocate_closure,
757 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700758 }
759 } else {
Craig Tiller91031da2016-12-28 15:44:25 -0800760 grpc_closure_sched(exec_ctx, optional_on_done, GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700761 }
762 gpr_mu_unlock(&resource_user->mu);
763}
764
765void grpc_resource_user_free(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700766 grpc_resource_user *resource_user, size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700767 gpr_mu_lock(&resource_user->mu);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700768 bool was_zero_or_negative = resource_user->free_pool <= 0;
769 resource_user->free_pool += (int64_t)size;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700770 if (grpc_resource_quota_trace) {
Craig Tillera947f1c2016-11-04 13:53:17 -0700771 gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700772 resource_user->resource_quota->name, resource_user->name, size,
Craig Tillera947f1c2016-11-04 13:53:17 -0700773 resource_user->free_pool);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700774 }
775 bool is_bigger_than_zero = resource_user->free_pool > 0;
776 if (is_bigger_than_zero && was_zero_or_negative &&
777 !resource_user->added_to_free_pool) {
778 resource_user->added_to_free_pool = true;
Craig Tiller91031da2016-12-28 15:44:25 -0800779 grpc_closure_sched(exec_ctx, &resource_user->add_to_free_pool_closure,
780 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700781 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700782 gpr_mu_unlock(&resource_user->mu);
Craig Tillera947f1c2016-11-04 13:53:17 -0700783 ru_unref_by(exec_ctx, resource_user, (gpr_atm)size);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700784}
785
786void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700787 grpc_resource_user *resource_user,
788 bool destructive,
789 grpc_closure *closure) {
Craig Tillerc0c0dbc2016-11-29 10:10:21 -0800790 GPR_ASSERT(resource_user->new_reclaimers[destructive] == NULL);
791 resource_user->new_reclaimers[destructive] = closure;
Craig Tiller91031da2016-12-28 15:44:25 -0800792 grpc_closure_sched(exec_ctx,
793 &resource_user->post_reclaimer_closure[destructive],
794 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700795}
796
Craig Tiller3798e602016-10-21 14:39:27 -0700797void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
798 grpc_resource_user *resource_user) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700799 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700800 gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700801 resource_user->resource_quota->name, resource_user->name);
802 }
Craig Tiller91031da2016-12-28 15:44:25 -0800803 grpc_closure_sched(
804 exec_ctx, &resource_user->resource_quota->rq_reclamation_done_closure,
805 GRPC_ERROR_NONE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700806}
807
808void grpc_resource_user_slice_allocator_init(
809 grpc_resource_user_slice_allocator *slice_allocator,
810 grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) {
Craig Tiller5082d7e2017-01-26 11:21:11 -0800811 grpc_closure_init(&slice_allocator->on_allocated, ru_allocated_slices,
812 slice_allocator, grpc_schedule_on_exec_ctx);
813 grpc_closure_init(&slice_allocator->on_done, cb, p,
814 grpc_schedule_on_exec_ctx);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700815 slice_allocator->resource_user = resource_user;
816}
817
818void grpc_resource_user_alloc_slices(
Craig Tillerafcc8752016-10-18 16:10:06 -0700819 grpc_exec_ctx *exec_ctx,
820 grpc_resource_user_slice_allocator *slice_allocator, size_t length,
Craig Tillerd41a4a72016-10-26 16:16:06 -0700821 size_t count, grpc_slice_buffer *dest) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700822 slice_allocator->length = length;
823 slice_allocator->count = count;
824 slice_allocator->dest = dest;
Craig Tillerafcc8752016-10-18 16:10:06 -0700825 grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user,
826 count * length, &slice_allocator->on_allocated);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700827}
murgatroid9969259d42016-10-31 14:34:10 -0700828
Craig Tiller32df4672016-11-04 08:21:56 -0700829grpc_slice grpc_resource_user_slice_malloc(grpc_exec_ctx *exec_ctx,
830 grpc_resource_user *resource_user,
831 size_t size) {
murgatroid9969259d42016-10-31 14:34:10 -0700832 grpc_resource_user_alloc(exec_ctx, resource_user, size, NULL);
833 return ru_slice_create(resource_user, size);
834}