blob: 54669734081453466b5c9b50cd6008414858fa3a [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
52 */
Craig Tiller20afa3d2016-10-17 14:52:14 -070053 grpc_combiner *combiner;
Craig Tiller3798e602016-10-21 14:39:27 -070054 /* Size of the resource quota */
Craig Tiller20afa3d2016-10-17 14:52:14 -070055 int64_t size;
Craig Tiller3798e602016-10-21 14:39:27 -070056 /* Amount of free memory in the resource quota */
Craig Tiller20afa3d2016-10-17 14:52:14 -070057 int64_t free_pool;
58
Craig Tiller3798e602016-10-21 14:39:27 -070059 /* Has rq_step been scheduled to occur? */
Craig Tiller20afa3d2016-10-17 14:52:14 -070060 bool step_scheduled;
Craig Tiller3798e602016-10-21 14:39:27 -070061 /* Are we currently reclaiming memory */
Craig Tiller20afa3d2016-10-17 14:52:14 -070062 bool reclaiming;
Craig Tiller3798e602016-10-21 14:39:27 -070063 /* Closure around rq_step */
64 grpc_closure rq_step_closure;
65 /* Closure around rq_reclamation_done */
66 grpc_closure rq_reclamation_done_closure;
Craig Tiller20afa3d2016-10-17 14:52:14 -070067
Craig Tiller3798e602016-10-21 14:39:27 -070068 /* Roots of all resource user lists */
69 grpc_resource_user *roots[GRPC_RULIST_COUNT];
Craig Tiller20afa3d2016-10-17 14:52:14 -070070
71 char *name;
72};
73
74/*******************************************************************************
75 * list management
76 */
77
Craig Tiller3798e602016-10-21 14:39:27 -070078static void rulist_add_tail(grpc_resource_user *resource_user,
79 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -070080 grpc_resource_quota *resource_quota = resource_user->resource_quota;
81 grpc_resource_user **root = &resource_quota->roots[list];
82 if (*root == NULL) {
83 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -070084 resource_user->links[list].next = resource_user->links[list].prev =
85 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -070086 } else {
87 resource_user->links[list].next = *root;
88 resource_user->links[list].prev = (*root)->links[list].prev;
89 resource_user->links[list].next->links[list].prev =
90 resource_user->links[list].prev->links[list].next = resource_user;
91 }
92}
93
Craig Tiller3798e602016-10-21 14:39:27 -070094static void rulist_add_head(grpc_resource_user *resource_user,
95 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -070096 grpc_resource_quota *resource_quota = resource_user->resource_quota;
97 grpc_resource_user **root = &resource_quota->roots[list];
98 if (*root == NULL) {
99 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -0700100 resource_user->links[list].next = resource_user->links[list].prev =
101 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700102 } else {
103 resource_user->links[list].next = (*root)->links[list].next;
104 resource_user->links[list].prev = *root;
105 resource_user->links[list].next->links[list].prev =
106 resource_user->links[list].prev->links[list].next = resource_user;
107 *root = resource_user;
108 }
109}
110
Craig Tiller3798e602016-10-21 14:39:27 -0700111static bool rulist_empty(grpc_resource_quota *resource_quota,
112 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700113 return resource_quota->roots[list] == NULL;
114}
115
Craig Tiller3798e602016-10-21 14:39:27 -0700116static grpc_resource_user *rulist_pop(grpc_resource_quota *resource_quota,
117 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700118 grpc_resource_user **root = &resource_quota->roots[list];
119 grpc_resource_user *resource_user = *root;
120 if (resource_user == NULL) {
121 return NULL;
122 }
123 if (resource_user->links[list].next == resource_user) {
124 *root = NULL;
125 } else {
126 resource_user->links[list].next->links[list].prev =
127 resource_user->links[list].prev;
128 resource_user->links[list].prev->links[list].next =
129 resource_user->links[list].next;
130 *root = resource_user->links[list].next;
131 }
132 resource_user->links[list].next = resource_user->links[list].prev = NULL;
133 return resource_user;
134}
135
Craig Tiller3798e602016-10-21 14:39:27 -0700136static void rulist_remove(grpc_resource_user *resource_user, grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700137 if (resource_user->links[list].next == NULL) return;
138 grpc_resource_quota *resource_quota = resource_user->resource_quota;
139 if (resource_quota->roots[list] == resource_user) {
140 resource_quota->roots[list] = resource_user->links[list].next;
141 if (resource_quota->roots[list] == resource_user) {
142 resource_quota->roots[list] = NULL;
143 }
144 }
145 resource_user->links[list].next->links[list].prev =
146 resource_user->links[list].prev;
147 resource_user->links[list].prev->links[list].next =
148 resource_user->links[list].next;
149}
150
151/*******************************************************************************
152 * buffer pool state machine
153 */
154
Craig Tiller3798e602016-10-21 14:39:27 -0700155static bool rq_alloc(grpc_exec_ctx *exec_ctx,
156 grpc_resource_quota *resource_quota);
157static bool rq_scavenge(grpc_exec_ctx *exec_ctx,
158 grpc_resource_quota *resource_quota);
159static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
160 grpc_resource_quota *resource_quota, bool destructive);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700161
Craig Tiller3798e602016-10-21 14:39:27 -0700162static void rq_step(grpc_exec_ctx *exec_ctx, void *bp, grpc_error *error) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700163 grpc_resource_quota *resource_quota = bp;
164 resource_quota->step_scheduled = false;
165 do {
Craig Tiller3798e602016-10-21 14:39:27 -0700166 if (rq_alloc(exec_ctx, resource_quota)) goto done;
167 } while (rq_scavenge(exec_ctx, resource_quota));
168 rq_reclaim(exec_ctx, resource_quota, false) ||
169 rq_reclaim(exec_ctx, resource_quota, true);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700170done:
171 grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
172}
173
Craig Tiller3798e602016-10-21 14:39:27 -0700174static void rq_step_sched(grpc_exec_ctx *exec_ctx,
175 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700176 if (resource_quota->step_scheduled) return;
177 resource_quota->step_scheduled = true;
178 grpc_resource_quota_internal_ref(resource_quota);
179 grpc_combiner_execute_finally(exec_ctx, resource_quota->combiner,
Craig Tiller3798e602016-10-21 14:39:27 -0700180 &resource_quota->rq_step_closure,
Craig Tillerafcc8752016-10-18 16:10:06 -0700181 GRPC_ERROR_NONE, false);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700182}
183
184/* returns true if all allocations are completed */
Craig Tiller3798e602016-10-21 14:39:27 -0700185static bool rq_alloc(grpc_exec_ctx *exec_ctx,
186 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700187 grpc_resource_user *resource_user;
188 while ((resource_user =
Craig Tiller3798e602016-10-21 14:39:27 -0700189 rulist_pop(resource_quota, GRPC_RULIST_AWAITING_ALLOCATION))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700190 gpr_mu_lock(&resource_user->mu);
191 if (resource_user->free_pool < 0 &&
192 -resource_user->free_pool <= resource_quota->free_pool) {
193 int64_t amt = -resource_user->free_pool;
194 resource_user->free_pool = 0;
195 resource_quota->free_pool -= amt;
196 if (grpc_resource_quota_trace) {
197 gpr_log(GPR_DEBUG, "BP %s %s: grant alloc %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700198 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700199 resource_quota->name, resource_user->name, amt,
200 resource_quota->free_pool);
201 }
202 } else if (grpc_resource_quota_trace && resource_user->free_pool >= 0) {
203 gpr_log(GPR_DEBUG, "BP %s %s: discard already satisfied alloc request",
204 resource_quota->name, resource_user->name);
205 }
206 if (resource_user->free_pool >= 0) {
207 resource_user->allocating = false;
208 grpc_exec_ctx_enqueue_list(exec_ctx, &resource_user->on_allocated, NULL);
209 gpr_mu_unlock(&resource_user->mu);
210 } else {
Craig Tiller3798e602016-10-21 14:39:27 -0700211 rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700212 gpr_mu_unlock(&resource_user->mu);
213 return false;
214 }
215 }
216 return true;
217}
218
219/* returns true if any memory could be reclaimed from buffers */
Craig Tiller3798e602016-10-21 14:39:27 -0700220static bool rq_scavenge(grpc_exec_ctx *exec_ctx,
221 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700222 grpc_resource_user *resource_user;
223 while ((resource_user =
Craig Tiller3798e602016-10-21 14:39:27 -0700224 rulist_pop(resource_quota, GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700225 gpr_mu_lock(&resource_user->mu);
226 if (resource_user->free_pool > 0) {
227 int64_t amt = resource_user->free_pool;
228 resource_user->free_pool = 0;
229 resource_quota->free_pool += amt;
230 if (grpc_resource_quota_trace) {
231 gpr_log(GPR_DEBUG, "BP %s %s: scavenge %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700232 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700233 resource_quota->name, resource_user->name, amt,
234 resource_quota->free_pool);
235 }
236 gpr_mu_unlock(&resource_user->mu);
237 return true;
238 } else {
239 gpr_mu_unlock(&resource_user->mu);
240 }
241 }
242 return false;
243}
244
Craig Tiller3798e602016-10-21 14:39:27 -0700245/* returns true if reclamation is proceeding */
246static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
247 grpc_resource_quota *resource_quota, bool destructive) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700248 if (resource_quota->reclaiming) return true;
Craig Tiller3798e602016-10-21 14:39:27 -0700249 grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE
250 : GRPC_RULIST_RECLAIMER_BENIGN;
251 grpc_resource_user *resource_user = rulist_pop(resource_quota, list);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700252 if (resource_user == NULL) return false;
253 if (grpc_resource_quota_trace) {
Craig Tiller3798e602016-10-21 14:39:27 -0700254 gpr_log(GPR_DEBUG, "BP %s %s: initiate %s reclamation",
Craig Tillerafcc8752016-10-18 16:10:06 -0700255 resource_quota->name, resource_user->name,
256 destructive ? "destructive" : "benign");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700257 }
258 resource_quota->reclaiming = true;
259 grpc_resource_quota_internal_ref(resource_quota);
260 grpc_closure *c = resource_user->reclaimers[destructive];
261 resource_user->reclaimers[destructive] = NULL;
262 grpc_closure_run(exec_ctx, c, GRPC_ERROR_NONE);
263 return true;
264}
265
266/*******************************************************************************
Craig Tiller3798e602016-10-21 14:39:27 -0700267 * ru_slice: a slice implementation that is backed by a grpc_resource_user
Craig Tiller20afa3d2016-10-17 14:52:14 -0700268 */
269
270typedef struct {
271 gpr_slice_refcount base;
272 gpr_refcount refs;
273 grpc_resource_user *resource_user;
274 size_t size;
Craig Tiller3798e602016-10-21 14:39:27 -0700275} ru_slice_refcount;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700276
Craig Tiller3798e602016-10-21 14:39:27 -0700277static void ru_slice_ref(void *p) {
278 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700279 gpr_ref(&rc->refs);
280}
281
Craig Tiller3798e602016-10-21 14:39:27 -0700282static void ru_slice_unref(void *p) {
283 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700284 if (gpr_unref(&rc->refs)) {
285 /* TODO(ctiller): this is dangerous, but I think safe for now:
286 we have no guarantee here that we're at a safe point for creating an
287 execution context, but we have no way of writing this code otherwise.
288 In the future: consider lifting gpr_slice to grpc, and offering an
Craig Tiller3798e602016-10-21 14:39:27 -0700289 internal_{ref,unref} pair that is execution context aware.
290 Alternatively,
291 make exec_ctx be thread local and 'do the right thing' (whatever that
292 is)
Craig Tiller20afa3d2016-10-17 14:52:14 -0700293 if NULL */
294 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
295 grpc_resource_user_free(&exec_ctx, rc->resource_user, rc->size);
296 grpc_exec_ctx_finish(&exec_ctx);
297 gpr_free(rc);
298 }
299}
300
Craig Tiller3798e602016-10-21 14:39:27 -0700301static gpr_slice ru_slice_create(grpc_resource_user *resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700302 size_t size) {
Craig Tiller3798e602016-10-21 14:39:27 -0700303 ru_slice_refcount *rc = gpr_malloc(sizeof(ru_slice_refcount) + size);
304 rc->base.ref = ru_slice_ref;
305 rc->base.unref = ru_slice_unref;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700306 gpr_ref_init(&rc->refs, 1);
307 rc->resource_user = resource_user;
308 rc->size = size;
309 gpr_slice slice;
310 slice.refcount = &rc->base;
311 slice.data.refcounted.bytes = (uint8_t *)(rc + 1);
312 slice.data.refcounted.length = size;
313 return slice;
314}
315
316/*******************************************************************************
317 * grpc_resource_quota internal implementation
318 */
319
Craig Tiller3798e602016-10-21 14:39:27 -0700320static void ru_allocate(grpc_exec_ctx *exec_ctx, void *bu, grpc_error *error) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700321 grpc_resource_user *resource_user = bu;
Craig Tiller3798e602016-10-21 14:39:27 -0700322 if (rulist_empty(resource_user->resource_quota,
323 GRPC_RULIST_AWAITING_ALLOCATION)) {
324 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700325 }
Craig Tiller3798e602016-10-21 14:39:27 -0700326 rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700327}
328
Craig Tiller3798e602016-10-21 14:39:27 -0700329static void ru_add_to_free_pool(grpc_exec_ctx *exec_ctx, void *bu,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700330 grpc_error *error) {
331 grpc_resource_user *resource_user = bu;
Craig Tiller3798e602016-10-21 14:39:27 -0700332 if (!rulist_empty(resource_user->resource_quota,
333 GRPC_RULIST_AWAITING_ALLOCATION) &&
334 rulist_empty(resource_user->resource_quota,
335 GRPC_RULIST_NON_EMPTY_FREE_POOL)) {
336 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700337 }
Craig Tiller3798e602016-10-21 14:39:27 -0700338 rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700339}
340
Craig Tiller3798e602016-10-21 14:39:27 -0700341static void ru_post_benign_reclaimer(grpc_exec_ctx *exec_ctx, void *bu,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700342 grpc_error *error) {
343 grpc_resource_user *resource_user = bu;
Craig Tiller3798e602016-10-21 14:39:27 -0700344 if (!rulist_empty(resource_user->resource_quota,
345 GRPC_RULIST_AWAITING_ALLOCATION) &&
346 rulist_empty(resource_user->resource_quota,
347 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
348 rulist_empty(resource_user->resource_quota,
349 GRPC_RULIST_RECLAIMER_BENIGN)) {
350 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700351 }
Craig Tiller3798e602016-10-21 14:39:27 -0700352 rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700353}
354
Craig Tiller3798e602016-10-21 14:39:27 -0700355static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *bu,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700356 grpc_error *error) {
357 grpc_resource_user *resource_user = bu;
Craig Tiller3798e602016-10-21 14:39:27 -0700358 if (!rulist_empty(resource_user->resource_quota,
359 GRPC_RULIST_AWAITING_ALLOCATION) &&
360 rulist_empty(resource_user->resource_quota,
361 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
362 rulist_empty(resource_user->resource_quota,
363 GRPC_RULIST_RECLAIMER_BENIGN) &&
364 rulist_empty(resource_user->resource_quota,
365 GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) {
366 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700367 }
Craig Tiller3798e602016-10-21 14:39:27 -0700368 rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700369}
370
Craig Tiller3798e602016-10-21 14:39:27 -0700371static void ru_destroy(grpc_exec_ctx *exec_ctx, void *bu, grpc_error *error) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700372 grpc_resource_user *resource_user = bu;
373 GPR_ASSERT(resource_user->allocated == 0);
Craig Tiller3798e602016-10-21 14:39:27 -0700374 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
375 rulist_remove(resource_user, (grpc_rulist)i);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700376 }
377 grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[0],
378 GRPC_ERROR_CANCELLED, NULL);
379 grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[1],
380 GRPC_ERROR_CANCELLED, NULL);
381 grpc_exec_ctx_sched(exec_ctx, (grpc_closure *)gpr_atm_no_barrier_load(
382 &resource_user->on_done_destroy_closure),
383 GRPC_ERROR_NONE, NULL);
384 if (resource_user->free_pool != 0) {
385 resource_user->resource_quota->free_pool += resource_user->free_pool;
Craig Tiller3798e602016-10-21 14:39:27 -0700386 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700387 }
388}
389
Craig Tiller3798e602016-10-21 14:39:27 -0700390static void ru_allocated_slices(grpc_exec_ctx *exec_ctx, void *ts,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700391 grpc_error *error) {
392 grpc_resource_user_slice_allocator *slice_allocator = ts;
393 if (error == GRPC_ERROR_NONE) {
394 for (size_t i = 0; i < slice_allocator->count; i++) {
Craig Tillerafcc8752016-10-18 16:10:06 -0700395 gpr_slice_buffer_add_indexed(
Craig Tiller3798e602016-10-21 14:39:27 -0700396 slice_allocator->dest, ru_slice_create(slice_allocator->resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700397 slice_allocator->length));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700398 }
399 }
400 grpc_closure_run(exec_ctx, &slice_allocator->on_done, GRPC_ERROR_REF(error));
401}
402
403typedef struct {
404 int64_t size;
405 grpc_resource_quota *resource_quota;
406 grpc_closure closure;
Craig Tiller3798e602016-10-21 14:39:27 -0700407} rq_resize_args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700408
Craig Tiller3798e602016-10-21 14:39:27 -0700409static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
410 rq_resize_args *a = args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700411 int64_t delta = a->size - a->resource_quota->size;
412 a->resource_quota->size += delta;
413 a->resource_quota->free_pool += delta;
414 if (delta < 0 && a->resource_quota->free_pool < 0) {
Craig Tiller3798e602016-10-21 14:39:27 -0700415 rq_step_sched(exec_ctx, a->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700416 } else if (delta > 0 &&
Craig Tiller3798e602016-10-21 14:39:27 -0700417 !rulist_empty(a->resource_quota,
418 GRPC_RULIST_AWAITING_ALLOCATION)) {
419 rq_step_sched(exec_ctx, a->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700420 }
421 grpc_resource_quota_internal_unref(exec_ctx, a->resource_quota);
422 gpr_free(a);
423}
424
Craig Tiller3798e602016-10-21 14:39:27 -0700425static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *bp,
426 grpc_error *error) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700427 grpc_resource_quota *resource_quota = bp;
428 resource_quota->reclaiming = false;
Craig Tiller3798e602016-10-21 14:39:27 -0700429 rq_step_sched(exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700430 grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
431}
432
433/*******************************************************************************
434 * grpc_resource_quota api
435 */
436
437grpc_resource_quota *grpc_resource_quota_create(const char *name) {
438 grpc_resource_quota *resource_quota = gpr_malloc(sizeof(*resource_quota));
439 gpr_ref_init(&resource_quota->refs, 1);
440 resource_quota->combiner = grpc_combiner_create(NULL);
441 resource_quota->free_pool = INT64_MAX;
442 resource_quota->size = INT64_MAX;
443 resource_quota->step_scheduled = false;
444 resource_quota->reclaiming = false;
445 if (name != NULL) {
446 resource_quota->name = gpr_strdup(name);
447 } else {
448 gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR,
449 (intptr_t)resource_quota);
450 }
Craig Tiller3798e602016-10-21 14:39:27 -0700451 grpc_closure_init(&resource_quota->rq_step_closure, rq_step, resource_quota);
452 grpc_closure_init(&resource_quota->rq_reclamation_done_closure,
453 rq_reclamation_done, resource_quota);
454 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700455 resource_quota->roots[i] = NULL;
456 }
457 return resource_quota;
458}
459
460void grpc_resource_quota_internal_unref(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700461 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700462 if (gpr_unref(&resource_quota->refs)) {
463 grpc_combiner_destroy(exec_ctx, resource_quota->combiner);
464 gpr_free(resource_quota->name);
465 gpr_free(resource_quota);
466 }
467}
468
469void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) {
470 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
471 grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
472 grpc_exec_ctx_finish(&exec_ctx);
473}
474
Craig Tillerafcc8752016-10-18 16:10:06 -0700475grpc_resource_quota *grpc_resource_quota_internal_ref(
476 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700477 gpr_ref(&resource_quota->refs);
478 return resource_quota;
479}
480
481void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) {
482 grpc_resource_quota_internal_ref(resource_quota);
483}
484
Craig Tillerafcc8752016-10-18 16:10:06 -0700485void grpc_resource_quota_resize(grpc_resource_quota *resource_quota,
486 size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700487 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
Craig Tiller3798e602016-10-21 14:39:27 -0700488 rq_resize_args *a = gpr_malloc(sizeof(*a));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700489 a->resource_quota = grpc_resource_quota_internal_ref(resource_quota);
490 a->size = (int64_t)size;
Craig Tiller3798e602016-10-21 14:39:27 -0700491 grpc_closure_init(&a->closure, rq_resize, a);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700492 grpc_combiner_execute(&exec_ctx, resource_quota->combiner, &a->closure,
493 GRPC_ERROR_NONE, false);
494 grpc_exec_ctx_finish(&exec_ctx);
495}
496
497/*******************************************************************************
498 * grpc_resource_user channel args api
499 */
500
501grpc_resource_quota *grpc_resource_quota_from_channel_args(
502 const grpc_channel_args *channel_args) {
503 for (size_t i = 0; i < channel_args->num_args; i++) {
Craig Tiller153eaa72016-10-21 13:52:36 -0700504 if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700505 if (channel_args->args[i].type == GRPC_ARG_POINTER) {
506 return grpc_resource_quota_internal_ref(
507 channel_args->args[i].value.pointer.p);
508 } else {
Craig Tiller153eaa72016-10-21 13:52:36 -0700509 gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700510 }
511 }
512 }
513 return grpc_resource_quota_create(NULL);
514}
515
Craig Tiller3798e602016-10-21 14:39:27 -0700516static void *rq_copy(void *bp) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700517 grpc_resource_quota_ref(bp);
518 return bp;
519}
520
Craig Tiller3798e602016-10-21 14:39:27 -0700521static void rq_destroy(void *bp) { grpc_resource_quota_unref(bp); }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700522
Craig Tiller3798e602016-10-21 14:39:27 -0700523static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700524
525const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void) {
Craig Tiller3798e602016-10-21 14:39:27 -0700526 static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp};
Craig Tiller20afa3d2016-10-17 14:52:14 -0700527 return &vtable;
528}
529
530/*******************************************************************************
531 * grpc_resource_user api
532 */
533
534void grpc_resource_user_init(grpc_resource_user *resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700535 grpc_resource_quota *resource_quota,
536 const char *name) {
537 resource_user->resource_quota =
538 grpc_resource_quota_internal_ref(resource_quota);
Craig Tiller3798e602016-10-21 14:39:27 -0700539 grpc_closure_init(&resource_user->allocate_closure, &ru_allocate,
Craig Tillerafcc8752016-10-18 16:10:06 -0700540 resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700541 grpc_closure_init(&resource_user->add_to_free_pool_closure,
Craig Tiller3798e602016-10-21 14:39:27 -0700542 &ru_add_to_free_pool, resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700543 grpc_closure_init(&resource_user->post_reclaimer_closure[0],
Craig Tiller3798e602016-10-21 14:39:27 -0700544 &ru_post_benign_reclaimer, resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700545 grpc_closure_init(&resource_user->post_reclaimer_closure[1],
Craig Tiller3798e602016-10-21 14:39:27 -0700546 &ru_post_destructive_reclaimer, resource_user);
547 grpc_closure_init(&resource_user->destroy_closure, &ru_destroy,
Craig Tillerafcc8752016-10-18 16:10:06 -0700548 resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700549 gpr_mu_init(&resource_user->mu);
550 resource_user->allocated = 0;
551 resource_user->free_pool = 0;
552 grpc_closure_list_init(&resource_user->on_allocated);
553 resource_user->allocating = false;
554 resource_user->added_to_free_pool = false;
555 gpr_atm_no_barrier_store(&resource_user->on_done_destroy_closure, 0);
556 resource_user->reclaimers[0] = NULL;
557 resource_user->reclaimers[1] = NULL;
Craig Tiller3798e602016-10-21 14:39:27 -0700558 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700559 resource_user->links[i].next = resource_user->links[i].prev = NULL;
560 }
561#ifndef NDEBUG
562 resource_user->asan_canary = gpr_malloc(1);
563#endif
564 if (name != NULL) {
565 resource_user->name = gpr_strdup(name);
566 } else {
567 gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR,
568 (intptr_t)resource_user);
569 }
570}
571
572void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700573 grpc_resource_user *resource_user,
574 grpc_closure *on_done) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700575 gpr_mu_lock(&resource_user->mu);
576 GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->on_done_destroy_closure) ==
577 0);
578 gpr_atm_no_barrier_store(&resource_user->on_done_destroy_closure,
579 (gpr_atm)on_done);
580 if (resource_user->allocated == 0) {
581 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
582 &resource_user->destroy_closure, GRPC_ERROR_NONE,
583 false);
584 }
585 gpr_mu_unlock(&resource_user->mu);
586}
587
588void grpc_resource_user_destroy(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700589 grpc_resource_user *resource_user) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700590#ifndef NDEBUG
591 gpr_free(resource_user->asan_canary);
592#endif
593 grpc_resource_quota_internal_unref(exec_ctx, resource_user->resource_quota);
594 gpr_mu_destroy(&resource_user->mu);
595 gpr_free(resource_user->name);
596}
597
598void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700599 grpc_resource_user *resource_user, size_t size,
600 grpc_closure *optional_on_done) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700601 gpr_mu_lock(&resource_user->mu);
602 grpc_closure *on_done_destroy = (grpc_closure *)gpr_atm_no_barrier_load(
603 &resource_user->on_done_destroy_closure);
604 if (on_done_destroy != NULL) {
605 /* already shutdown */
606 if (grpc_resource_quota_trace) {
607 gpr_log(GPR_DEBUG, "BP %s %s: alloc %" PRIdPTR " after shutdown",
608 resource_user->resource_quota->name, resource_user->name, size);
609 }
610 grpc_exec_ctx_sched(
611 exec_ctx, optional_on_done,
612 GRPC_ERROR_CREATE("Buffer pool user is already shutdown"), NULL);
613 gpr_mu_unlock(&resource_user->mu);
614 return;
615 }
616 resource_user->allocated += (int64_t)size;
617 resource_user->free_pool -= (int64_t)size;
618 if (grpc_resource_quota_trace) {
619 gpr_log(GPR_DEBUG, "BP %s %s: alloc %" PRIdPTR "; allocated -> %" PRId64
620 ", free_pool -> %" PRId64,
621 resource_user->resource_quota->name, resource_user->name, size,
622 resource_user->allocated, resource_user->free_pool);
623 }
624 if (resource_user->free_pool < 0) {
625 grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
626 GRPC_ERROR_NONE);
627 if (!resource_user->allocating) {
628 resource_user->allocating = true;
629 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
630 &resource_user->allocate_closure, GRPC_ERROR_NONE,
631 false);
632 }
633 } else {
634 grpc_exec_ctx_sched(exec_ctx, optional_on_done, GRPC_ERROR_NONE, NULL);
635 }
636 gpr_mu_unlock(&resource_user->mu);
637}
638
639void grpc_resource_user_free(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700640 grpc_resource_user *resource_user, size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700641 gpr_mu_lock(&resource_user->mu);
642 GPR_ASSERT(resource_user->allocated >= (int64_t)size);
643 bool was_zero_or_negative = resource_user->free_pool <= 0;
644 resource_user->free_pool += (int64_t)size;
645 resource_user->allocated -= (int64_t)size;
646 if (grpc_resource_quota_trace) {
647 gpr_log(GPR_DEBUG, "BP %s %s: free %" PRIdPTR "; allocated -> %" PRId64
648 ", free_pool -> %" PRId64,
649 resource_user->resource_quota->name, resource_user->name, size,
650 resource_user->allocated, resource_user->free_pool);
651 }
652 bool is_bigger_than_zero = resource_user->free_pool > 0;
653 if (is_bigger_than_zero && was_zero_or_negative &&
654 !resource_user->added_to_free_pool) {
655 resource_user->added_to_free_pool = true;
656 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
657 &resource_user->add_to_free_pool_closure,
658 GRPC_ERROR_NONE, false);
659 }
660 grpc_closure *on_done_destroy = (grpc_closure *)gpr_atm_no_barrier_load(
661 &resource_user->on_done_destroy_closure);
662 if (on_done_destroy != NULL && resource_user->allocated == 0) {
663 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
664 &resource_user->destroy_closure, GRPC_ERROR_NONE,
665 false);
666 }
667 gpr_mu_unlock(&resource_user->mu);
668}
669
670void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700671 grpc_resource_user *resource_user,
672 bool destructive,
673 grpc_closure *closure) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700674 if (gpr_atm_acq_load(&resource_user->on_done_destroy_closure) == 0) {
675 GPR_ASSERT(resource_user->reclaimers[destructive] == NULL);
676 resource_user->reclaimers[destructive] = closure;
677 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
678 &resource_user->post_reclaimer_closure[destructive],
679 GRPC_ERROR_NONE, false);
680 } else {
681 grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CANCELLED, NULL);
682 }
683}
684
Craig Tiller3798e602016-10-21 14:39:27 -0700685void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
686 grpc_resource_user *resource_user) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700687 if (grpc_resource_quota_trace) {
Craig Tiller3798e602016-10-21 14:39:27 -0700688 gpr_log(GPR_DEBUG, "BP %s %s: reclamation complete",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700689 resource_user->resource_quota->name, resource_user->name);
690 }
Craig Tillerafcc8752016-10-18 16:10:06 -0700691 grpc_combiner_execute(
692 exec_ctx, resource_user->resource_quota->combiner,
Craig Tiller3798e602016-10-21 14:39:27 -0700693 &resource_user->resource_quota->rq_reclamation_done_closure,
Craig Tillerafcc8752016-10-18 16:10:06 -0700694 GRPC_ERROR_NONE, false);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700695}
696
697void grpc_resource_user_slice_allocator_init(
698 grpc_resource_user_slice_allocator *slice_allocator,
699 grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) {
Craig Tiller3798e602016-10-21 14:39:27 -0700700 grpc_closure_init(&slice_allocator->on_allocated, ru_allocated_slices,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700701 slice_allocator);
702 grpc_closure_init(&slice_allocator->on_done, cb, p);
703 slice_allocator->resource_user = resource_user;
704}
705
706void grpc_resource_user_alloc_slices(
Craig Tillerafcc8752016-10-18 16:10:06 -0700707 grpc_exec_ctx *exec_ctx,
708 grpc_resource_user_slice_allocator *slice_allocator, size_t length,
709 size_t count, gpr_slice_buffer *dest) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700710 slice_allocator->length = length;
711 slice_allocator->count = count;
712 slice_allocator->dest = dest;
Craig Tillerafcc8752016-10-18 16:10:06 -0700713 grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user,
714 count * length, &slice_allocator->on_allocated);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700715}