blob: 6d7e549bb7286cd6f3056f9f6f72eea4c38daf8c [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 Tiller20afa3d2016-10-17 14:52:14 -070047struct grpc_resource_quota {
Craig Tiller3798e602016-10-21 14:39:27 -070048 /* refcount */
Craig Tiller20afa3d2016-10-17 14:52:14 -070049 gpr_refcount refs;
50
Craig Tiller3798e602016-10-21 14:39:27 -070051 /* Master combiner lock: all activity on a quota executes under this combiner
Craig Tiller6b5d6822016-10-25 16:13:26 -070052 * (so no mutex is needed for this data structure)
Craig Tiller3798e602016-10-21 14:39:27 -070053 */
Craig Tiller20afa3d2016-10-17 14:52:14 -070054 grpc_combiner *combiner;
Craig Tiller3798e602016-10-21 14:39:27 -070055 /* Size of the resource quota */
Craig Tiller20afa3d2016-10-17 14:52:14 -070056 int64_t size;
Craig Tiller3798e602016-10-21 14:39:27 -070057 /* Amount of free memory in the resource quota */
Craig Tiller20afa3d2016-10-17 14:52:14 -070058 int64_t free_pool;
59
Craig Tiller3798e602016-10-21 14:39:27 -070060 /* Has rq_step been scheduled to occur? */
Craig Tiller20afa3d2016-10-17 14:52:14 -070061 bool step_scheduled;
Craig Tiller3798e602016-10-21 14:39:27 -070062 /* Are we currently reclaiming memory */
Craig Tiller20afa3d2016-10-17 14:52:14 -070063 bool reclaiming;
Craig Tiller3798e602016-10-21 14:39:27 -070064 /* Closure around rq_step */
65 grpc_closure rq_step_closure;
66 /* Closure around rq_reclamation_done */
67 grpc_closure rq_reclamation_done_closure;
Craig Tiller20afa3d2016-10-17 14:52:14 -070068
Craig Tiller3798e602016-10-21 14:39:27 -070069 /* Roots of all resource user lists */
70 grpc_resource_user *roots[GRPC_RULIST_COUNT];
Craig Tiller20afa3d2016-10-17 14:52:14 -070071
72 char *name;
73};
74
75/*******************************************************************************
76 * list management
77 */
78
Craig Tiller6b5d6822016-10-25 16:13:26 -070079static void rulist_add_head(grpc_resource_user *resource_user,
Craig Tiller3798e602016-10-21 14:39:27 -070080 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -070081 grpc_resource_quota *resource_quota = resource_user->resource_quota;
82 grpc_resource_user **root = &resource_quota->roots[list];
83 if (*root == NULL) {
84 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -070085 resource_user->links[list].next = resource_user->links[list].prev =
86 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -070087 } else {
88 resource_user->links[list].next = *root;
89 resource_user->links[list].prev = (*root)->links[list].prev;
90 resource_user->links[list].next->links[list].prev =
91 resource_user->links[list].prev->links[list].next = resource_user;
92 }
93}
94
Craig Tiller6b5d6822016-10-25 16:13:26 -070095static void rulist_add_tail(grpc_resource_user *resource_user,
Craig Tiller3798e602016-10-21 14:39:27 -070096 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -070097 grpc_resource_quota *resource_quota = resource_user->resource_quota;
98 grpc_resource_user **root = &resource_quota->roots[list];
99 if (*root == NULL) {
100 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -0700101 resource_user->links[list].next = resource_user->links[list].prev =
102 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700103 } else {
104 resource_user->links[list].next = (*root)->links[list].next;
105 resource_user->links[list].prev = *root;
106 resource_user->links[list].next->links[list].prev =
107 resource_user->links[list].prev->links[list].next = resource_user;
108 *root = resource_user;
109 }
110}
111
Craig Tiller3798e602016-10-21 14:39:27 -0700112static bool rulist_empty(grpc_resource_quota *resource_quota,
113 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700114 return resource_quota->roots[list] == NULL;
115}
116
Craig Tiller6b5d6822016-10-25 16:13:26 -0700117static grpc_resource_user *rulist_pop_tail(grpc_resource_quota *resource_quota,
118 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700119 grpc_resource_user **root = &resource_quota->roots[list];
120 grpc_resource_user *resource_user = *root;
121 if (resource_user == NULL) {
122 return NULL;
123 }
124 if (resource_user->links[list].next == resource_user) {
125 *root = NULL;
126 } else {
127 resource_user->links[list].next->links[list].prev =
128 resource_user->links[list].prev;
129 resource_user->links[list].prev->links[list].next =
130 resource_user->links[list].next;
131 *root = resource_user->links[list].next;
132 }
133 resource_user->links[list].next = resource_user->links[list].prev = NULL;
134 return resource_user;
135}
136
Craig Tiller3798e602016-10-21 14:39:27 -0700137static void rulist_remove(grpc_resource_user *resource_user, grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700138 if (resource_user->links[list].next == NULL) return;
139 grpc_resource_quota *resource_quota = resource_user->resource_quota;
140 if (resource_quota->roots[list] == resource_user) {
141 resource_quota->roots[list] = resource_user->links[list].next;
142 if (resource_quota->roots[list] == resource_user) {
143 resource_quota->roots[list] = NULL;
144 }
145 }
146 resource_user->links[list].next->links[list].prev =
147 resource_user->links[list].prev;
148 resource_user->links[list].prev->links[list].next =
149 resource_user->links[list].next;
150}
151
152/*******************************************************************************
Craig Tiller6b5d6822016-10-25 16:13:26 -0700153 * resource quota state machine
Craig Tiller20afa3d2016-10-17 14:52:14 -0700154 */
155
Craig Tiller3798e602016-10-21 14:39:27 -0700156static bool rq_alloc(grpc_exec_ctx *exec_ctx,
157 grpc_resource_quota *resource_quota);
Craig Tiller6b5d6822016-10-25 16:13:26 -0700158static bool rq_reclaim_from_per_user_free_pool(
159 grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota);
Craig Tiller3798e602016-10-21 14:39:27 -0700160static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
161 grpc_resource_quota *resource_quota, bool destructive);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700162
Craig Tiller6b5d6822016-10-25 16:13:26 -0700163static void rq_step(grpc_exec_ctx *exec_ctx, void *rq, grpc_error *error) {
164 grpc_resource_quota *resource_quota = rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700165 resource_quota->step_scheduled = false;
166 do {
Craig Tiller3798e602016-10-21 14:39:27 -0700167 if (rq_alloc(exec_ctx, resource_quota)) goto done;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700168 } while (rq_reclaim_from_per_user_free_pool(exec_ctx, resource_quota));
Craig Tiller3798e602016-10-21 14:39:27 -0700169 rq_reclaim(exec_ctx, resource_quota, false) ||
170 rq_reclaim(exec_ctx, resource_quota, true);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700171done:
172 grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
173}
174
Craig Tiller3798e602016-10-21 14:39:27 -0700175static void rq_step_sched(grpc_exec_ctx *exec_ctx,
176 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700177 if (resource_quota->step_scheduled) return;
178 resource_quota->step_scheduled = true;
179 grpc_resource_quota_internal_ref(resource_quota);
180 grpc_combiner_execute_finally(exec_ctx, resource_quota->combiner,
Craig Tiller3798e602016-10-21 14:39:27 -0700181 &resource_quota->rq_step_closure,
Craig Tillerafcc8752016-10-18 16:10:06 -0700182 GRPC_ERROR_NONE, false);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700183}
184
185/* returns true if all allocations are completed */
Craig Tiller3798e602016-10-21 14:39:27 -0700186static bool rq_alloc(grpc_exec_ctx *exec_ctx,
187 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700188 grpc_resource_user *resource_user;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700189 while ((resource_user = rulist_pop_tail(resource_quota,
190 GRPC_RULIST_AWAITING_ALLOCATION))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700191 gpr_mu_lock(&resource_user->mu);
192 if (resource_user->free_pool < 0 &&
193 -resource_user->free_pool <= resource_quota->free_pool) {
194 int64_t amt = -resource_user->free_pool;
195 resource_user->free_pool = 0;
196 resource_quota->free_pool -= amt;
197 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700198 gpr_log(GPR_DEBUG, "RQ %s %s: grant alloc %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700199 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700200 resource_quota->name, resource_user->name, amt,
201 resource_quota->free_pool);
202 }
203 } else if (grpc_resource_quota_trace && resource_user->free_pool >= 0) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700204 gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700205 resource_quota->name, resource_user->name);
206 }
207 if (resource_user->free_pool >= 0) {
208 resource_user->allocating = false;
209 grpc_exec_ctx_enqueue_list(exec_ctx, &resource_user->on_allocated, NULL);
210 gpr_mu_unlock(&resource_user->mu);
211 } else {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700212 rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700213 gpr_mu_unlock(&resource_user->mu);
214 return false;
215 }
216 }
217 return true;
218}
219
220/* returns true if any memory could be reclaimed from buffers */
Craig Tiller6b5d6822016-10-25 16:13:26 -0700221static bool rq_reclaim_from_per_user_free_pool(
222 grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700223 grpc_resource_user *resource_user;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700224 while ((resource_user = rulist_pop_tail(resource_quota,
225 GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700226 gpr_mu_lock(&resource_user->mu);
227 if (resource_user->free_pool > 0) {
228 int64_t amt = resource_user->free_pool;
229 resource_user->free_pool = 0;
230 resource_quota->free_pool += amt;
231 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700232 gpr_log(GPR_DEBUG, "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700233 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700234 resource_quota->name, resource_user->name, amt,
235 resource_quota->free_pool);
236 }
237 gpr_mu_unlock(&resource_user->mu);
238 return true;
239 } else {
240 gpr_mu_unlock(&resource_user->mu);
241 }
242 }
243 return false;
244}
245
Craig Tiller3798e602016-10-21 14:39:27 -0700246/* returns true if reclamation is proceeding */
247static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
248 grpc_resource_quota *resource_quota, bool destructive) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700249 if (resource_quota->reclaiming) return true;
Craig Tiller3798e602016-10-21 14:39:27 -0700250 grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE
251 : GRPC_RULIST_RECLAIMER_BENIGN;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700252 grpc_resource_user *resource_user = rulist_pop_tail(resource_quota, list);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700253 if (resource_user == NULL) return false;
254 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700255 gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation",
Craig Tillerafcc8752016-10-18 16:10:06 -0700256 resource_quota->name, resource_user->name,
257 destructive ? "destructive" : "benign");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700258 }
259 resource_quota->reclaiming = true;
260 grpc_resource_quota_internal_ref(resource_quota);
261 grpc_closure *c = resource_user->reclaimers[destructive];
262 resource_user->reclaimers[destructive] = NULL;
263 grpc_closure_run(exec_ctx, c, GRPC_ERROR_NONE);
264 return true;
265}
266
267/*******************************************************************************
Craig Tiller3798e602016-10-21 14:39:27 -0700268 * ru_slice: a slice implementation that is backed by a grpc_resource_user
Craig Tiller20afa3d2016-10-17 14:52:14 -0700269 */
270
271typedef struct {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700272 grpc_slice_refcount base;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700273 gpr_refcount refs;
274 grpc_resource_user *resource_user;
275 size_t size;
Craig Tiller3798e602016-10-21 14:39:27 -0700276} ru_slice_refcount;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700277
Craig Tiller3798e602016-10-21 14:39:27 -0700278static void ru_slice_ref(void *p) {
279 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700280 gpr_ref(&rc->refs);
281}
282
Craig Tiller3798e602016-10-21 14:39:27 -0700283static void ru_slice_unref(void *p) {
284 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700285 if (gpr_unref(&rc->refs)) {
286 /* TODO(ctiller): this is dangerous, but I think safe for now:
287 we have no guarantee here that we're at a safe point for creating an
288 execution context, but we have no way of writing this code otherwise.
Craig Tillerd41a4a72016-10-26 16:16:06 -0700289 In the future: consider lifting grpc_slice to grpc, and offering an
Craig Tiller3798e602016-10-21 14:39:27 -0700290 internal_{ref,unref} pair that is execution context aware.
291 Alternatively,
292 make exec_ctx be thread local and 'do the right thing' (whatever that
293 is)
Craig Tiller20afa3d2016-10-17 14:52:14 -0700294 if NULL */
295 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
296 grpc_resource_user_free(&exec_ctx, rc->resource_user, rc->size);
297 grpc_exec_ctx_finish(&exec_ctx);
298 gpr_free(rc);
299 }
300}
301
Craig Tillerd41a4a72016-10-26 16:16:06 -0700302static grpc_slice ru_slice_create(grpc_resource_user *resource_user,
Craig Tiller28b72422016-10-26 21:15:29 -0700303 size_t size) {
Craig Tiller3798e602016-10-21 14:39:27 -0700304 ru_slice_refcount *rc = gpr_malloc(sizeof(ru_slice_refcount) + size);
305 rc->base.ref = ru_slice_ref;
306 rc->base.unref = ru_slice_unref;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700307 gpr_ref_init(&rc->refs, 1);
308 rc->resource_user = resource_user;
309 rc->size = size;
Craig Tillerd41a4a72016-10-26 16:16:06 -0700310 grpc_slice slice;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700311 slice.refcount = &rc->base;
312 slice.data.refcounted.bytes = (uint8_t *)(rc + 1);
313 slice.data.refcounted.length = size;
314 return slice;
315}
316
317/*******************************************************************************
Craig Tiller6b5d6822016-10-25 16:13:26 -0700318 * grpc_resource_quota internal implementation: resource user manipulation under
319 * the combiner
Craig Tiller20afa3d2016-10-17 14:52:14 -0700320 */
321
Craig Tiller6b5d6822016-10-25 16:13:26 -0700322static void ru_allocate(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
323 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700324 if (rulist_empty(resource_user->resource_quota,
325 GRPC_RULIST_AWAITING_ALLOCATION)) {
326 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700327 }
Craig Tiller6b5d6822016-10-25 16:13:26 -0700328 rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700329}
330
Craig Tiller6b5d6822016-10-25 16:13:26 -0700331static void ru_add_to_free_pool(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700332 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700333 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700334 if (!rulist_empty(resource_user->resource_quota,
335 GRPC_RULIST_AWAITING_ALLOCATION) &&
336 rulist_empty(resource_user->resource_quota,
337 GRPC_RULIST_NON_EMPTY_FREE_POOL)) {
338 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700339 }
Craig Tiller6b5d6822016-10-25 16:13:26 -0700340 rulist_add_head(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700341}
342
Craig Tiller6b5d6822016-10-25 16:13:26 -0700343static void ru_post_benign_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700344 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700345 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700346 if (!rulist_empty(resource_user->resource_quota,
347 GRPC_RULIST_AWAITING_ALLOCATION) &&
348 rulist_empty(resource_user->resource_quota,
349 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
350 rulist_empty(resource_user->resource_quota,
351 GRPC_RULIST_RECLAIMER_BENIGN)) {
352 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700353 }
Craig Tiller6b5d6822016-10-25 16:13:26 -0700354 rulist_add_head(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700355}
356
Craig Tiller6b5d6822016-10-25 16:13:26 -0700357static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700358 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700359 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700360 if (!rulist_empty(resource_user->resource_quota,
361 GRPC_RULIST_AWAITING_ALLOCATION) &&
362 rulist_empty(resource_user->resource_quota,
363 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
364 rulist_empty(resource_user->resource_quota,
365 GRPC_RULIST_RECLAIMER_BENIGN) &&
366 rulist_empty(resource_user->resource_quota,
367 GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) {
368 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700369 }
Craig Tiller6b5d6822016-10-25 16:13:26 -0700370 rulist_add_head(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700371}
372
Craig Tiller6b5d6822016-10-25 16:13:26 -0700373static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
374 grpc_resource_user *resource_user = ru;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700375 GPR_ASSERT(resource_user->allocated == 0);
Craig Tiller3798e602016-10-21 14:39:27 -0700376 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
377 rulist_remove(resource_user, (grpc_rulist)i);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700378 }
379 grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[0],
380 GRPC_ERROR_CANCELLED, NULL);
381 grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[1],
382 GRPC_ERROR_CANCELLED, NULL);
383 grpc_exec_ctx_sched(exec_ctx, (grpc_closure *)gpr_atm_no_barrier_load(
384 &resource_user->on_done_destroy_closure),
385 GRPC_ERROR_NONE, NULL);
386 if (resource_user->free_pool != 0) {
387 resource_user->resource_quota->free_pool += resource_user->free_pool;
Craig Tiller3798e602016-10-21 14:39:27 -0700388 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700389 }
390}
391
Craig Tiller6b5d6822016-10-25 16:13:26 -0700392static void ru_allocated_slices(grpc_exec_ctx *exec_ctx, void *arg,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700393 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700394 grpc_resource_user_slice_allocator *slice_allocator = arg;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700395 if (error == GRPC_ERROR_NONE) {
396 for (size_t i = 0; i < slice_allocator->count; i++) {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700397 grpc_slice_buffer_add_indexed(
Craig Tiller3798e602016-10-21 14:39:27 -0700398 slice_allocator->dest, ru_slice_create(slice_allocator->resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700399 slice_allocator->length));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700400 }
401 }
402 grpc_closure_run(exec_ctx, &slice_allocator->on_done, GRPC_ERROR_REF(error));
403}
404
Craig Tiller6b5d6822016-10-25 16:13:26 -0700405/*******************************************************************************
406 * grpc_resource_quota internal implementation: quota manipulation under the
407 * combiner
408 */
409
Craig Tiller20afa3d2016-10-17 14:52:14 -0700410typedef struct {
411 int64_t size;
412 grpc_resource_quota *resource_quota;
413 grpc_closure closure;
Craig Tiller3798e602016-10-21 14:39:27 -0700414} rq_resize_args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700415
Craig Tiller3798e602016-10-21 14:39:27 -0700416static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
417 rq_resize_args *a = args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700418 int64_t delta = a->size - a->resource_quota->size;
419 a->resource_quota->size += delta;
420 a->resource_quota->free_pool += delta;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700421 rq_step_sched(exec_ctx, a->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700422 grpc_resource_quota_internal_unref(exec_ctx, a->resource_quota);
423 gpr_free(a);
424}
425
Craig Tiller6b5d6822016-10-25 16:13:26 -0700426static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *rq,
Craig Tiller3798e602016-10-21 14:39:27 -0700427 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700428 grpc_resource_quota *resource_quota = rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700429 resource_quota->reclaiming = false;
Craig Tiller3798e602016-10-21 14:39:27 -0700430 rq_step_sched(exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700431 grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
432}
433
434/*******************************************************************************
435 * grpc_resource_quota api
436 */
437
Craig Tiller6b5d6822016-10-25 16:13:26 -0700438/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700439grpc_resource_quota *grpc_resource_quota_create(const char *name) {
440 grpc_resource_quota *resource_quota = gpr_malloc(sizeof(*resource_quota));
441 gpr_ref_init(&resource_quota->refs, 1);
442 resource_quota->combiner = grpc_combiner_create(NULL);
443 resource_quota->free_pool = INT64_MAX;
444 resource_quota->size = INT64_MAX;
445 resource_quota->step_scheduled = false;
446 resource_quota->reclaiming = false;
447 if (name != NULL) {
448 resource_quota->name = gpr_strdup(name);
449 } else {
450 gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR,
451 (intptr_t)resource_quota);
452 }
Craig Tiller3798e602016-10-21 14:39:27 -0700453 grpc_closure_init(&resource_quota->rq_step_closure, rq_step, resource_quota);
454 grpc_closure_init(&resource_quota->rq_reclamation_done_closure,
455 rq_reclamation_done, resource_quota);
456 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700457 resource_quota->roots[i] = NULL;
458 }
459 return resource_quota;
460}
461
462void grpc_resource_quota_internal_unref(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700463 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700464 if (gpr_unref(&resource_quota->refs)) {
465 grpc_combiner_destroy(exec_ctx, resource_quota->combiner);
466 gpr_free(resource_quota->name);
467 gpr_free(resource_quota);
468 }
469}
470
Craig Tiller6b5d6822016-10-25 16:13:26 -0700471/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700472void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) {
473 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
474 grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
475 grpc_exec_ctx_finish(&exec_ctx);
476}
477
Craig Tillerafcc8752016-10-18 16:10:06 -0700478grpc_resource_quota *grpc_resource_quota_internal_ref(
479 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700480 gpr_ref(&resource_quota->refs);
481 return resource_quota;
482}
483
Craig Tiller6b5d6822016-10-25 16:13:26 -0700484/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700485void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) {
486 grpc_resource_quota_internal_ref(resource_quota);
487}
488
Craig Tiller6b5d6822016-10-25 16:13:26 -0700489/* Public API */
Craig Tillerafcc8752016-10-18 16:10:06 -0700490void grpc_resource_quota_resize(grpc_resource_quota *resource_quota,
491 size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700492 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
Craig Tiller3798e602016-10-21 14:39:27 -0700493 rq_resize_args *a = gpr_malloc(sizeof(*a));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700494 a->resource_quota = grpc_resource_quota_internal_ref(resource_quota);
495 a->size = (int64_t)size;
Craig Tiller3798e602016-10-21 14:39:27 -0700496 grpc_closure_init(&a->closure, rq_resize, a);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700497 grpc_combiner_execute(&exec_ctx, resource_quota->combiner, &a->closure,
498 GRPC_ERROR_NONE, false);
499 grpc_exec_ctx_finish(&exec_ctx);
500}
501
502/*******************************************************************************
503 * grpc_resource_user channel args api
504 */
505
506grpc_resource_quota *grpc_resource_quota_from_channel_args(
507 const grpc_channel_args *channel_args) {
508 for (size_t i = 0; i < channel_args->num_args; i++) {
Craig Tiller153eaa72016-10-21 13:52:36 -0700509 if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700510 if (channel_args->args[i].type == GRPC_ARG_POINTER) {
511 return grpc_resource_quota_internal_ref(
512 channel_args->args[i].value.pointer.p);
513 } else {
Craig Tiller153eaa72016-10-21 13:52:36 -0700514 gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700515 }
516 }
517 }
518 return grpc_resource_quota_create(NULL);
519}
520
Craig Tiller6b5d6822016-10-25 16:13:26 -0700521static void *rq_copy(void *rq) {
522 grpc_resource_quota_ref(rq);
523 return rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700524}
525
Craig Tiller6b5d6822016-10-25 16:13:26 -0700526static void rq_destroy(void *rq) { grpc_resource_quota_unref(rq); }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700527
Craig Tiller3798e602016-10-21 14:39:27 -0700528static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700529
530const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void) {
Craig Tiller3798e602016-10-21 14:39:27 -0700531 static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp};
Craig Tiller20afa3d2016-10-17 14:52:14 -0700532 return &vtable;
533}
534
535/*******************************************************************************
536 * grpc_resource_user api
537 */
538
539void grpc_resource_user_init(grpc_resource_user *resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700540 grpc_resource_quota *resource_quota,
541 const char *name) {
542 resource_user->resource_quota =
543 grpc_resource_quota_internal_ref(resource_quota);
Craig Tiller3798e602016-10-21 14:39:27 -0700544 grpc_closure_init(&resource_user->allocate_closure, &ru_allocate,
Craig Tillerafcc8752016-10-18 16:10:06 -0700545 resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700546 grpc_closure_init(&resource_user->add_to_free_pool_closure,
Craig Tiller3798e602016-10-21 14:39:27 -0700547 &ru_add_to_free_pool, resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700548 grpc_closure_init(&resource_user->post_reclaimer_closure[0],
Craig Tiller3798e602016-10-21 14:39:27 -0700549 &ru_post_benign_reclaimer, resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700550 grpc_closure_init(&resource_user->post_reclaimer_closure[1],
Craig Tiller3798e602016-10-21 14:39:27 -0700551 &ru_post_destructive_reclaimer, resource_user);
552 grpc_closure_init(&resource_user->destroy_closure, &ru_destroy,
Craig Tillerafcc8752016-10-18 16:10:06 -0700553 resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700554 gpr_mu_init(&resource_user->mu);
555 resource_user->allocated = 0;
556 resource_user->free_pool = 0;
557 grpc_closure_list_init(&resource_user->on_allocated);
558 resource_user->allocating = false;
559 resource_user->added_to_free_pool = false;
560 gpr_atm_no_barrier_store(&resource_user->on_done_destroy_closure, 0);
561 resource_user->reclaimers[0] = NULL;
562 resource_user->reclaimers[1] = NULL;
Craig Tiller3798e602016-10-21 14:39:27 -0700563 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700564 resource_user->links[i].next = resource_user->links[i].prev = NULL;
565 }
566#ifndef NDEBUG
567 resource_user->asan_canary = gpr_malloc(1);
568#endif
569 if (name != NULL) {
570 resource_user->name = gpr_strdup(name);
571 } else {
572 gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR,
573 (intptr_t)resource_user);
574 }
575}
576
577void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700578 grpc_resource_user *resource_user,
579 grpc_closure *on_done) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700580 gpr_mu_lock(&resource_user->mu);
581 GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->on_done_destroy_closure) ==
582 0);
583 gpr_atm_no_barrier_store(&resource_user->on_done_destroy_closure,
584 (gpr_atm)on_done);
585 if (resource_user->allocated == 0) {
586 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
587 &resource_user->destroy_closure, GRPC_ERROR_NONE,
588 false);
589 }
590 gpr_mu_unlock(&resource_user->mu);
591}
592
593void grpc_resource_user_destroy(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700594 grpc_resource_user *resource_user) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700595#ifndef NDEBUG
596 gpr_free(resource_user->asan_canary);
597#endif
598 grpc_resource_quota_internal_unref(exec_ctx, resource_user->resource_quota);
599 gpr_mu_destroy(&resource_user->mu);
600 gpr_free(resource_user->name);
601}
602
603void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700604 grpc_resource_user *resource_user, size_t size,
605 grpc_closure *optional_on_done) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700606 gpr_mu_lock(&resource_user->mu);
607 grpc_closure *on_done_destroy = (grpc_closure *)gpr_atm_no_barrier_load(
608 &resource_user->on_done_destroy_closure);
609 if (on_done_destroy != NULL) {
610 /* already shutdown */
611 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700612 gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR " after shutdown",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700613 resource_user->resource_quota->name, resource_user->name, size);
614 }
615 grpc_exec_ctx_sched(
616 exec_ctx, optional_on_done,
617 GRPC_ERROR_CREATE("Buffer pool user is already shutdown"), NULL);
618 gpr_mu_unlock(&resource_user->mu);
619 return;
620 }
621 resource_user->allocated += (int64_t)size;
622 resource_user->free_pool -= (int64_t)size;
623 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700624 gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; allocated -> %" PRId64
Craig Tiller20afa3d2016-10-17 14:52:14 -0700625 ", free_pool -> %" PRId64,
626 resource_user->resource_quota->name, resource_user->name, size,
627 resource_user->allocated, resource_user->free_pool);
628 }
629 if (resource_user->free_pool < 0) {
630 grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
631 GRPC_ERROR_NONE);
632 if (!resource_user->allocating) {
633 resource_user->allocating = true;
634 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
635 &resource_user->allocate_closure, GRPC_ERROR_NONE,
636 false);
637 }
638 } else {
639 grpc_exec_ctx_sched(exec_ctx, optional_on_done, GRPC_ERROR_NONE, NULL);
640 }
641 gpr_mu_unlock(&resource_user->mu);
642}
643
644void grpc_resource_user_free(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700645 grpc_resource_user *resource_user, size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700646 gpr_mu_lock(&resource_user->mu);
647 GPR_ASSERT(resource_user->allocated >= (int64_t)size);
648 bool was_zero_or_negative = resource_user->free_pool <= 0;
649 resource_user->free_pool += (int64_t)size;
650 resource_user->allocated -= (int64_t)size;
651 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700652 gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; allocated -> %" PRId64
Craig Tiller20afa3d2016-10-17 14:52:14 -0700653 ", free_pool -> %" PRId64,
654 resource_user->resource_quota->name, resource_user->name, size,
655 resource_user->allocated, resource_user->free_pool);
656 }
657 bool is_bigger_than_zero = resource_user->free_pool > 0;
658 if (is_bigger_than_zero && was_zero_or_negative &&
659 !resource_user->added_to_free_pool) {
660 resource_user->added_to_free_pool = true;
661 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
662 &resource_user->add_to_free_pool_closure,
663 GRPC_ERROR_NONE, false);
664 }
665 grpc_closure *on_done_destroy = (grpc_closure *)gpr_atm_no_barrier_load(
666 &resource_user->on_done_destroy_closure);
667 if (on_done_destroy != NULL && resource_user->allocated == 0) {
668 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
669 &resource_user->destroy_closure, GRPC_ERROR_NONE,
670 false);
671 }
672 gpr_mu_unlock(&resource_user->mu);
673}
674
675void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700676 grpc_resource_user *resource_user,
677 bool destructive,
678 grpc_closure *closure) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700679 if (gpr_atm_acq_load(&resource_user->on_done_destroy_closure) == 0) {
680 GPR_ASSERT(resource_user->reclaimers[destructive] == NULL);
681 resource_user->reclaimers[destructive] = closure;
682 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
683 &resource_user->post_reclaimer_closure[destructive],
684 GRPC_ERROR_NONE, false);
685 } else {
686 grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CANCELLED, NULL);
687 }
688}
689
Craig Tiller3798e602016-10-21 14:39:27 -0700690void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
691 grpc_resource_user *resource_user) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700692 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700693 gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700694 resource_user->resource_quota->name, resource_user->name);
695 }
Craig Tillerafcc8752016-10-18 16:10:06 -0700696 grpc_combiner_execute(
697 exec_ctx, resource_user->resource_quota->combiner,
Craig Tiller3798e602016-10-21 14:39:27 -0700698 &resource_user->resource_quota->rq_reclamation_done_closure,
Craig Tillerafcc8752016-10-18 16:10:06 -0700699 GRPC_ERROR_NONE, false);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700700}
701
702void grpc_resource_user_slice_allocator_init(
703 grpc_resource_user_slice_allocator *slice_allocator,
704 grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) {
Craig Tiller3798e602016-10-21 14:39:27 -0700705 grpc_closure_init(&slice_allocator->on_allocated, ru_allocated_slices,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700706 slice_allocator);
707 grpc_closure_init(&slice_allocator->on_done, cb, p);
708 slice_allocator->resource_user = resource_user;
709}
710
711void grpc_resource_user_alloc_slices(
Craig Tillerafcc8752016-10-18 16:10:06 -0700712 grpc_exec_ctx *exec_ctx,
713 grpc_resource_user_slice_allocator *slice_allocator, size_t length,
Craig Tillerd41a4a72016-10-26 16:16:06 -0700714 size_t count, grpc_slice_buffer *dest) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700715 slice_allocator->length = length;
716 slice_allocator->count = count;
717 slice_allocator->dest = dest;
Craig Tillerafcc8752016-10-18 16:10:06 -0700718 grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user,
719 count * length, &slice_allocator->on_allocated);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700720}