blob: 046253c525c5e4cf99c4790365eb08173870d684 [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;
Craig Tiller8abc7962016-10-26 21:31:29 -070092 *root = resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -070093 }
94}
95
Craig Tiller6b5d6822016-10-25 16:13:26 -070096static void rulist_add_tail(grpc_resource_user *resource_user,
Craig Tiller3798e602016-10-21 14:39:27 -070097 grpc_rulist list) {
Craig Tiller20afa3d2016-10-17 14:52:14 -070098 grpc_resource_quota *resource_quota = resource_user->resource_quota;
99 grpc_resource_user **root = &resource_quota->roots[list];
100 if (*root == NULL) {
101 *root = resource_user;
Craig Tillerafcc8752016-10-18 16:10:06 -0700102 resource_user->links[list].next = resource_user->links[list].prev =
103 resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700104 } else {
105 resource_user->links[list].next = (*root)->links[list].next;
106 resource_user->links[list].prev = *root;
107 resource_user->links[list].next->links[list].prev =
108 resource_user->links[list].prev->links[list].next = resource_user;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700109 }
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 Tiller8abc7962016-10-26 21:31:29 -0700117static grpc_resource_user *rulist_pop_head(grpc_resource_quota *resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700118 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));
Sree Kuchibhotla894db552016-10-28 18:25:24 -0700169
170 if (!rq_reclaim(exec_ctx, resource_quota, false)) {
171 rq_reclaim(exec_ctx, resource_quota, true);
172 }
173
Craig Tiller20afa3d2016-10-17 14:52:14 -0700174done:
175 grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
176}
177
Craig Tiller3798e602016-10-21 14:39:27 -0700178static void rq_step_sched(grpc_exec_ctx *exec_ctx,
179 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700180 if (resource_quota->step_scheduled) return;
181 resource_quota->step_scheduled = true;
182 grpc_resource_quota_internal_ref(resource_quota);
183 grpc_combiner_execute_finally(exec_ctx, resource_quota->combiner,
Craig Tiller3798e602016-10-21 14:39:27 -0700184 &resource_quota->rq_step_closure,
Craig Tillerafcc8752016-10-18 16:10:06 -0700185 GRPC_ERROR_NONE, false);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700186}
187
188/* returns true if all allocations are completed */
Craig Tiller3798e602016-10-21 14:39:27 -0700189static bool rq_alloc(grpc_exec_ctx *exec_ctx,
190 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700191 grpc_resource_user *resource_user;
Craig Tiller8abc7962016-10-26 21:31:29 -0700192 while ((resource_user = rulist_pop_head(resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700193 GRPC_RULIST_AWAITING_ALLOCATION))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700194 gpr_mu_lock(&resource_user->mu);
195 if (resource_user->free_pool < 0 &&
196 -resource_user->free_pool <= resource_quota->free_pool) {
197 int64_t amt = -resource_user->free_pool;
198 resource_user->free_pool = 0;
199 resource_quota->free_pool -= amt;
200 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700201 gpr_log(GPR_DEBUG, "RQ %s %s: grant alloc %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700202 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700203 resource_quota->name, resource_user->name, amt,
204 resource_quota->free_pool);
205 }
206 } else if (grpc_resource_quota_trace && resource_user->free_pool >= 0) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700207 gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700208 resource_quota->name, resource_user->name);
209 }
210 if (resource_user->free_pool >= 0) {
211 resource_user->allocating = false;
212 grpc_exec_ctx_enqueue_list(exec_ctx, &resource_user->on_allocated, NULL);
213 gpr_mu_unlock(&resource_user->mu);
214 } else {
Craig Tiller8abc7962016-10-26 21:31:29 -0700215 rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700216 gpr_mu_unlock(&resource_user->mu);
217 return false;
218 }
219 }
220 return true;
221}
222
223/* returns true if any memory could be reclaimed from buffers */
Craig Tiller6b5d6822016-10-25 16:13:26 -0700224static bool rq_reclaim_from_per_user_free_pool(
225 grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700226 grpc_resource_user *resource_user;
Craig Tiller8abc7962016-10-26 21:31:29 -0700227 while ((resource_user = rulist_pop_head(resource_quota,
Craig Tiller6b5d6822016-10-25 16:13:26 -0700228 GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700229 gpr_mu_lock(&resource_user->mu);
230 if (resource_user->free_pool > 0) {
231 int64_t amt = resource_user->free_pool;
232 resource_user->free_pool = 0;
233 resource_quota->free_pool += amt;
234 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700235 gpr_log(GPR_DEBUG, "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
Craig Tiller3798e602016-10-21 14:39:27 -0700236 " bytes; rq_free_pool -> %" PRId64,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700237 resource_quota->name, resource_user->name, amt,
238 resource_quota->free_pool);
239 }
240 gpr_mu_unlock(&resource_user->mu);
241 return true;
242 } else {
243 gpr_mu_unlock(&resource_user->mu);
244 }
245 }
246 return false;
247}
248
Craig Tiller3798e602016-10-21 14:39:27 -0700249/* returns true if reclamation is proceeding */
250static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
251 grpc_resource_quota *resource_quota, bool destructive) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700252 if (resource_quota->reclaiming) return true;
Craig Tiller3798e602016-10-21 14:39:27 -0700253 grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE
254 : GRPC_RULIST_RECLAIMER_BENIGN;
Craig Tiller8abc7962016-10-26 21:31:29 -0700255 grpc_resource_user *resource_user = rulist_pop_head(resource_quota, list);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700256 if (resource_user == NULL) return false;
257 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700258 gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation",
Craig Tillerafcc8752016-10-18 16:10:06 -0700259 resource_quota->name, resource_user->name,
260 destructive ? "destructive" : "benign");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700261 }
262 resource_quota->reclaiming = true;
263 grpc_resource_quota_internal_ref(resource_quota);
264 grpc_closure *c = resource_user->reclaimers[destructive];
265 resource_user->reclaimers[destructive] = NULL;
266 grpc_closure_run(exec_ctx, c, GRPC_ERROR_NONE);
267 return true;
268}
269
270/*******************************************************************************
Craig Tiller3798e602016-10-21 14:39:27 -0700271 * ru_slice: a slice implementation that is backed by a grpc_resource_user
Craig Tiller20afa3d2016-10-17 14:52:14 -0700272 */
273
274typedef struct {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700275 grpc_slice_refcount base;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700276 gpr_refcount refs;
277 grpc_resource_user *resource_user;
278 size_t size;
Craig Tiller3798e602016-10-21 14:39:27 -0700279} ru_slice_refcount;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700280
Craig Tiller3798e602016-10-21 14:39:27 -0700281static void ru_slice_ref(void *p) {
282 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700283 gpr_ref(&rc->refs);
284}
285
Craig Tiller3798e602016-10-21 14:39:27 -0700286static void ru_slice_unref(void *p) {
287 ru_slice_refcount *rc = p;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700288 if (gpr_unref(&rc->refs)) {
289 /* TODO(ctiller): this is dangerous, but I think safe for now:
290 we have no guarantee here that we're at a safe point for creating an
291 execution context, but we have no way of writing this code otherwise.
Craig Tillerd41a4a72016-10-26 16:16:06 -0700292 In the future: consider lifting grpc_slice to grpc, and offering an
Craig Tiller3798e602016-10-21 14:39:27 -0700293 internal_{ref,unref} pair that is execution context aware.
294 Alternatively,
295 make exec_ctx be thread local and 'do the right thing' (whatever that
296 is)
Craig Tiller20afa3d2016-10-17 14:52:14 -0700297 if NULL */
298 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
299 grpc_resource_user_free(&exec_ctx, rc->resource_user, rc->size);
300 grpc_exec_ctx_finish(&exec_ctx);
301 gpr_free(rc);
302 }
303}
304
Craig Tillerd41a4a72016-10-26 16:16:06 -0700305static grpc_slice ru_slice_create(grpc_resource_user *resource_user,
Craig Tiller28b72422016-10-26 21:15:29 -0700306 size_t size) {
Craig Tiller3798e602016-10-21 14:39:27 -0700307 ru_slice_refcount *rc = gpr_malloc(sizeof(ru_slice_refcount) + size);
308 rc->base.ref = ru_slice_ref;
309 rc->base.unref = ru_slice_unref;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700310 gpr_ref_init(&rc->refs, 1);
311 rc->resource_user = resource_user;
312 rc->size = size;
Craig Tillerd41a4a72016-10-26 16:16:06 -0700313 grpc_slice slice;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700314 slice.refcount = &rc->base;
315 slice.data.refcounted.bytes = (uint8_t *)(rc + 1);
316 slice.data.refcounted.length = size;
317 return slice;
318}
319
320/*******************************************************************************
Craig Tiller6b5d6822016-10-25 16:13:26 -0700321 * grpc_resource_quota internal implementation: resource user manipulation under
322 * the combiner
Craig Tiller20afa3d2016-10-17 14:52:14 -0700323 */
324
Craig Tiller6b5d6822016-10-25 16:13:26 -0700325static void ru_allocate(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
326 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700327 if (rulist_empty(resource_user->resource_quota,
328 GRPC_RULIST_AWAITING_ALLOCATION)) {
329 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700330 }
Craig Tiller8abc7962016-10-26 21:31:29 -0700331 rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700332}
333
Craig Tiller6b5d6822016-10-25 16:13:26 -0700334static void ru_add_to_free_pool(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700335 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700336 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700337 if (!rulist_empty(resource_user->resource_quota,
338 GRPC_RULIST_AWAITING_ALLOCATION) &&
339 rulist_empty(resource_user->resource_quota,
340 GRPC_RULIST_NON_EMPTY_FREE_POOL)) {
341 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700342 }
Craig Tiller8abc7962016-10-26 21:31:29 -0700343 rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700344}
345
Craig Tiller6b5d6822016-10-25 16:13:26 -0700346static void ru_post_benign_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700347 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700348 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700349 if (!rulist_empty(resource_user->resource_quota,
350 GRPC_RULIST_AWAITING_ALLOCATION) &&
351 rulist_empty(resource_user->resource_quota,
352 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
353 rulist_empty(resource_user->resource_quota,
354 GRPC_RULIST_RECLAIMER_BENIGN)) {
355 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700356 }
Craig Tiller8abc7962016-10-26 21:31:29 -0700357 rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700358}
359
Craig Tiller6b5d6822016-10-25 16:13:26 -0700360static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700361 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700362 grpc_resource_user *resource_user = ru;
Craig Tiller3798e602016-10-21 14:39:27 -0700363 if (!rulist_empty(resource_user->resource_quota,
364 GRPC_RULIST_AWAITING_ALLOCATION) &&
365 rulist_empty(resource_user->resource_quota,
366 GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
367 rulist_empty(resource_user->resource_quota,
368 GRPC_RULIST_RECLAIMER_BENIGN) &&
369 rulist_empty(resource_user->resource_quota,
370 GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) {
371 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700372 }
Craig Tiller8abc7962016-10-26 21:31:29 -0700373 rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700374}
375
Craig Tiller6b5d6822016-10-25 16:13:26 -0700376static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
377 grpc_resource_user *resource_user = ru;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700378 GPR_ASSERT(resource_user->allocated == 0);
Craig Tiller3798e602016-10-21 14:39:27 -0700379 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
380 rulist_remove(resource_user, (grpc_rulist)i);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700381 }
382 grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[0],
383 GRPC_ERROR_CANCELLED, NULL);
384 grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[1],
385 GRPC_ERROR_CANCELLED, NULL);
386 grpc_exec_ctx_sched(exec_ctx, (grpc_closure *)gpr_atm_no_barrier_load(
387 &resource_user->on_done_destroy_closure),
388 GRPC_ERROR_NONE, NULL);
389 if (resource_user->free_pool != 0) {
390 resource_user->resource_quota->free_pool += resource_user->free_pool;
Craig Tiller3798e602016-10-21 14:39:27 -0700391 rq_step_sched(exec_ctx, resource_user->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700392 }
393}
394
Craig Tiller6b5d6822016-10-25 16:13:26 -0700395static void ru_allocated_slices(grpc_exec_ctx *exec_ctx, void *arg,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700396 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700397 grpc_resource_user_slice_allocator *slice_allocator = arg;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700398 if (error == GRPC_ERROR_NONE) {
399 for (size_t i = 0; i < slice_allocator->count; i++) {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700400 grpc_slice_buffer_add_indexed(
Craig Tiller3798e602016-10-21 14:39:27 -0700401 slice_allocator->dest, ru_slice_create(slice_allocator->resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700402 slice_allocator->length));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700403 }
404 }
405 grpc_closure_run(exec_ctx, &slice_allocator->on_done, GRPC_ERROR_REF(error));
406}
407
Craig Tiller6b5d6822016-10-25 16:13:26 -0700408/*******************************************************************************
409 * grpc_resource_quota internal implementation: quota manipulation under the
410 * combiner
411 */
412
Craig Tiller20afa3d2016-10-17 14:52:14 -0700413typedef struct {
414 int64_t size;
415 grpc_resource_quota *resource_quota;
416 grpc_closure closure;
Craig Tiller3798e602016-10-21 14:39:27 -0700417} rq_resize_args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700418
Craig Tiller3798e602016-10-21 14:39:27 -0700419static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
420 rq_resize_args *a = args;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700421 int64_t delta = a->size - a->resource_quota->size;
422 a->resource_quota->size += delta;
423 a->resource_quota->free_pool += delta;
Craig Tiller6b5d6822016-10-25 16:13:26 -0700424 rq_step_sched(exec_ctx, a->resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700425 grpc_resource_quota_internal_unref(exec_ctx, a->resource_quota);
426 gpr_free(a);
427}
428
Craig Tiller6b5d6822016-10-25 16:13:26 -0700429static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *rq,
Craig Tiller3798e602016-10-21 14:39:27 -0700430 grpc_error *error) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700431 grpc_resource_quota *resource_quota = rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700432 resource_quota->reclaiming = false;
Craig Tiller3798e602016-10-21 14:39:27 -0700433 rq_step_sched(exec_ctx, resource_quota);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700434 grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
435}
436
437/*******************************************************************************
438 * grpc_resource_quota api
439 */
440
Craig Tiller6b5d6822016-10-25 16:13:26 -0700441/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700442grpc_resource_quota *grpc_resource_quota_create(const char *name) {
443 grpc_resource_quota *resource_quota = gpr_malloc(sizeof(*resource_quota));
444 gpr_ref_init(&resource_quota->refs, 1);
445 resource_quota->combiner = grpc_combiner_create(NULL);
446 resource_quota->free_pool = INT64_MAX;
447 resource_quota->size = INT64_MAX;
448 resource_quota->step_scheduled = false;
449 resource_quota->reclaiming = false;
450 if (name != NULL) {
451 resource_quota->name = gpr_strdup(name);
452 } else {
453 gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR,
454 (intptr_t)resource_quota);
455 }
Craig Tiller3798e602016-10-21 14:39:27 -0700456 grpc_closure_init(&resource_quota->rq_step_closure, rq_step, resource_quota);
457 grpc_closure_init(&resource_quota->rq_reclamation_done_closure,
458 rq_reclamation_done, resource_quota);
459 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700460 resource_quota->roots[i] = NULL;
461 }
462 return resource_quota;
463}
464
465void grpc_resource_quota_internal_unref(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700466 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700467 if (gpr_unref(&resource_quota->refs)) {
468 grpc_combiner_destroy(exec_ctx, resource_quota->combiner);
469 gpr_free(resource_quota->name);
470 gpr_free(resource_quota);
471 }
472}
473
Craig Tiller6b5d6822016-10-25 16:13:26 -0700474/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700475void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) {
476 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
477 grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
478 grpc_exec_ctx_finish(&exec_ctx);
479}
480
Craig Tillerafcc8752016-10-18 16:10:06 -0700481grpc_resource_quota *grpc_resource_quota_internal_ref(
482 grpc_resource_quota *resource_quota) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700483 gpr_ref(&resource_quota->refs);
484 return resource_quota;
485}
486
Craig Tiller6b5d6822016-10-25 16:13:26 -0700487/* Public API */
Craig Tiller20afa3d2016-10-17 14:52:14 -0700488void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) {
489 grpc_resource_quota_internal_ref(resource_quota);
490}
491
Craig Tiller6b5d6822016-10-25 16:13:26 -0700492/* Public API */
Craig Tillerafcc8752016-10-18 16:10:06 -0700493void grpc_resource_quota_resize(grpc_resource_quota *resource_quota,
494 size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700495 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
Craig Tiller3798e602016-10-21 14:39:27 -0700496 rq_resize_args *a = gpr_malloc(sizeof(*a));
Craig Tiller20afa3d2016-10-17 14:52:14 -0700497 a->resource_quota = grpc_resource_quota_internal_ref(resource_quota);
498 a->size = (int64_t)size;
Craig Tiller3798e602016-10-21 14:39:27 -0700499 grpc_closure_init(&a->closure, rq_resize, a);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700500 grpc_combiner_execute(&exec_ctx, resource_quota->combiner, &a->closure,
501 GRPC_ERROR_NONE, false);
502 grpc_exec_ctx_finish(&exec_ctx);
503}
504
505/*******************************************************************************
506 * grpc_resource_user channel args api
507 */
508
509grpc_resource_quota *grpc_resource_quota_from_channel_args(
510 const grpc_channel_args *channel_args) {
511 for (size_t i = 0; i < channel_args->num_args; i++) {
Craig Tiller153eaa72016-10-21 13:52:36 -0700512 if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700513 if (channel_args->args[i].type == GRPC_ARG_POINTER) {
514 return grpc_resource_quota_internal_ref(
515 channel_args->args[i].value.pointer.p);
516 } else {
Craig Tiller153eaa72016-10-21 13:52:36 -0700517 gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer");
Craig Tiller20afa3d2016-10-17 14:52:14 -0700518 }
519 }
520 }
521 return grpc_resource_quota_create(NULL);
522}
523
Craig Tiller6b5d6822016-10-25 16:13:26 -0700524static void *rq_copy(void *rq) {
525 grpc_resource_quota_ref(rq);
526 return rq;
Craig Tiller20afa3d2016-10-17 14:52:14 -0700527}
528
Craig Tiller6b5d6822016-10-25 16:13:26 -0700529static void rq_destroy(void *rq) { grpc_resource_quota_unref(rq); }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700530
Craig Tiller3798e602016-10-21 14:39:27 -0700531static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700532
533const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void) {
Craig Tiller3798e602016-10-21 14:39:27 -0700534 static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp};
Craig Tiller20afa3d2016-10-17 14:52:14 -0700535 return &vtable;
536}
537
538/*******************************************************************************
539 * grpc_resource_user api
540 */
541
542void grpc_resource_user_init(grpc_resource_user *resource_user,
Craig Tillerafcc8752016-10-18 16:10:06 -0700543 grpc_resource_quota *resource_quota,
544 const char *name) {
545 resource_user->resource_quota =
546 grpc_resource_quota_internal_ref(resource_quota);
Craig Tiller3798e602016-10-21 14:39:27 -0700547 grpc_closure_init(&resource_user->allocate_closure, &ru_allocate,
Craig Tillerafcc8752016-10-18 16:10:06 -0700548 resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700549 grpc_closure_init(&resource_user->add_to_free_pool_closure,
Craig Tiller3798e602016-10-21 14:39:27 -0700550 &ru_add_to_free_pool, resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700551 grpc_closure_init(&resource_user->post_reclaimer_closure[0],
Craig Tiller3798e602016-10-21 14:39:27 -0700552 &ru_post_benign_reclaimer, resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700553 grpc_closure_init(&resource_user->post_reclaimer_closure[1],
Craig Tiller3798e602016-10-21 14:39:27 -0700554 &ru_post_destructive_reclaimer, resource_user);
555 grpc_closure_init(&resource_user->destroy_closure, &ru_destroy,
Craig Tillerafcc8752016-10-18 16:10:06 -0700556 resource_user);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700557 gpr_mu_init(&resource_user->mu);
558 resource_user->allocated = 0;
559 resource_user->free_pool = 0;
560 grpc_closure_list_init(&resource_user->on_allocated);
561 resource_user->allocating = false;
562 resource_user->added_to_free_pool = false;
563 gpr_atm_no_barrier_store(&resource_user->on_done_destroy_closure, 0);
564 resource_user->reclaimers[0] = NULL;
565 resource_user->reclaimers[1] = NULL;
Craig Tiller3798e602016-10-21 14:39:27 -0700566 for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700567 resource_user->links[i].next = resource_user->links[i].prev = NULL;
568 }
Craig Tiller20afa3d2016-10-17 14:52:14 -0700569 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 grpc_resource_quota_internal_unref(exec_ctx, resource_user->resource_quota);
596 gpr_mu_destroy(&resource_user->mu);
597 gpr_free(resource_user->name);
598}
599
600void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700601 grpc_resource_user *resource_user, size_t size,
602 grpc_closure *optional_on_done) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700603 gpr_mu_lock(&resource_user->mu);
604 grpc_closure *on_done_destroy = (grpc_closure *)gpr_atm_no_barrier_load(
605 &resource_user->on_done_destroy_closure);
606 if (on_done_destroy != NULL) {
607 /* already shutdown */
608 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700609 gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR " after shutdown",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700610 resource_user->resource_quota->name, resource_user->name, size);
611 }
612 grpc_exec_ctx_sched(
613 exec_ctx, optional_on_done,
614 GRPC_ERROR_CREATE("Buffer pool user is already shutdown"), NULL);
615 gpr_mu_unlock(&resource_user->mu);
616 return;
617 }
618 resource_user->allocated += (int64_t)size;
619 resource_user->free_pool -= (int64_t)size;
620 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700621 gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; allocated -> %" PRId64
Craig Tiller20afa3d2016-10-17 14:52:14 -0700622 ", free_pool -> %" PRId64,
623 resource_user->resource_quota->name, resource_user->name, size,
624 resource_user->allocated, resource_user->free_pool);
625 }
626 if (resource_user->free_pool < 0) {
627 grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
628 GRPC_ERROR_NONE);
629 if (!resource_user->allocating) {
630 resource_user->allocating = true;
631 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
632 &resource_user->allocate_closure, GRPC_ERROR_NONE,
633 false);
634 }
635 } else {
636 grpc_exec_ctx_sched(exec_ctx, optional_on_done, GRPC_ERROR_NONE, NULL);
637 }
638 gpr_mu_unlock(&resource_user->mu);
639}
640
641void grpc_resource_user_free(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700642 grpc_resource_user *resource_user, size_t size) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700643 gpr_mu_lock(&resource_user->mu);
644 GPR_ASSERT(resource_user->allocated >= (int64_t)size);
645 bool was_zero_or_negative = resource_user->free_pool <= 0;
646 resource_user->free_pool += (int64_t)size;
647 resource_user->allocated -= (int64_t)size;
648 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700649 gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; allocated -> %" PRId64
Craig Tiller20afa3d2016-10-17 14:52:14 -0700650 ", free_pool -> %" PRId64,
651 resource_user->resource_quota->name, resource_user->name, size,
652 resource_user->allocated, resource_user->free_pool);
653 }
654 bool is_bigger_than_zero = resource_user->free_pool > 0;
655 if (is_bigger_than_zero && was_zero_or_negative &&
656 !resource_user->added_to_free_pool) {
657 resource_user->added_to_free_pool = true;
658 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
659 &resource_user->add_to_free_pool_closure,
660 GRPC_ERROR_NONE, false);
661 }
662 grpc_closure *on_done_destroy = (grpc_closure *)gpr_atm_no_barrier_load(
663 &resource_user->on_done_destroy_closure);
664 if (on_done_destroy != NULL && resource_user->allocated == 0) {
665 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
666 &resource_user->destroy_closure, GRPC_ERROR_NONE,
667 false);
668 }
669 gpr_mu_unlock(&resource_user->mu);
670}
671
672void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx,
Craig Tillerafcc8752016-10-18 16:10:06 -0700673 grpc_resource_user *resource_user,
674 bool destructive,
675 grpc_closure *closure) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700676 if (gpr_atm_acq_load(&resource_user->on_done_destroy_closure) == 0) {
677 GPR_ASSERT(resource_user->reclaimers[destructive] == NULL);
678 resource_user->reclaimers[destructive] = closure;
679 grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
680 &resource_user->post_reclaimer_closure[destructive],
681 GRPC_ERROR_NONE, false);
682 } else {
683 grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CANCELLED, NULL);
684 }
685}
686
Craig Tiller3798e602016-10-21 14:39:27 -0700687void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
688 grpc_resource_user *resource_user) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700689 if (grpc_resource_quota_trace) {
Craig Tiller6b5d6822016-10-25 16:13:26 -0700690 gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete",
Craig Tiller20afa3d2016-10-17 14:52:14 -0700691 resource_user->resource_quota->name, resource_user->name);
692 }
Craig Tillerafcc8752016-10-18 16:10:06 -0700693 grpc_combiner_execute(
694 exec_ctx, resource_user->resource_quota->combiner,
Craig Tiller3798e602016-10-21 14:39:27 -0700695 &resource_user->resource_quota->rq_reclamation_done_closure,
Craig Tillerafcc8752016-10-18 16:10:06 -0700696 GRPC_ERROR_NONE, false);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700697}
698
699void grpc_resource_user_slice_allocator_init(
700 grpc_resource_user_slice_allocator *slice_allocator,
701 grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) {
Craig Tiller3798e602016-10-21 14:39:27 -0700702 grpc_closure_init(&slice_allocator->on_allocated, ru_allocated_slices,
Craig Tiller20afa3d2016-10-17 14:52:14 -0700703 slice_allocator);
704 grpc_closure_init(&slice_allocator->on_done, cb, p);
705 slice_allocator->resource_user = resource_user;
706}
707
708void grpc_resource_user_alloc_slices(
Craig Tillerafcc8752016-10-18 16:10:06 -0700709 grpc_exec_ctx *exec_ctx,
710 grpc_resource_user_slice_allocator *slice_allocator, size_t length,
Craig Tillerd41a4a72016-10-26 16:16:06 -0700711 size_t count, grpc_slice_buffer *dest) {
Craig Tiller20afa3d2016-10-17 14:52:14 -0700712 slice_allocator->length = length;
713 slice_allocator->count = count;
714 slice_allocator->dest = dest;
Craig Tillerafcc8752016-10-18 16:10:06 -0700715 grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user,
716 count * length, &slice_allocator->on_allocated);
Craig Tiller20afa3d2016-10-17 14:52:14 -0700717}
murgatroid9969259d42016-10-31 14:34:10 -0700718
Craig Tiller32df4672016-11-04 08:21:56 -0700719grpc_slice grpc_resource_user_slice_malloc(grpc_exec_ctx *exec_ctx,
720 grpc_resource_user *resource_user,
721 size_t size) {
murgatroid9969259d42016-10-31 14:34:10 -0700722 grpc_resource_user_alloc(exec_ctx, resource_user, size, NULL);
723 return ru_slice_create(resource_user, size);
724}