1 /*
2 *
3 * Copyright 2016 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include "src/core/lib/iomgr/resource_quota.h"
20
21 #include <grpc/support/alloc.h>
22 #include <grpc/support/log.h>
23
24 #include "src/core/lib/iomgr/exec_ctx.h"
25 #include "src/core/lib/slice/slice_internal.h"
26 #include "test/core/util/test_config.h"
27
28 gpr_mu g_mu;
29 gpr_cv g_cv;
30
inc_int_cb(void * a,grpc_error *)31 static void inc_int_cb(void* a, grpc_error* /*error*/) {
32 gpr_mu_lock(&g_mu);
33 ++*static_cast<int*>(a);
34 gpr_cv_signal(&g_cv);
35 gpr_mu_unlock(&g_mu);
36 }
37
assert_counter_becomes(int * ctr,int value)38 static void assert_counter_becomes(int* ctr, int value) {
39 gpr_mu_lock(&g_mu);
40 gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5);
41 while (*ctr != value) {
42 GPR_ASSERT(!gpr_cv_wait(&g_cv, &g_mu, deadline));
43 }
44 gpr_mu_unlock(&g_mu);
45 }
46
set_event_cb(void * a,grpc_error *)47 static void set_event_cb(void* a, grpc_error* /*error*/) {
48 gpr_event_set(static_cast<gpr_event*>(a), (void*)1);
49 }
set_event(gpr_event * ev)50 grpc_closure* set_event(gpr_event* ev) {
51 return GRPC_CLOSURE_CREATE(set_event_cb, ev, grpc_schedule_on_exec_ctx);
52 }
53
54 typedef struct {
55 size_t size;
56 grpc_resource_user* resource_user;
57 grpc_closure* then;
58 } reclaimer_args;
59
reclaimer_cb(void * args,grpc_error * error)60 static void reclaimer_cb(void* args, grpc_error* error) {
61 GPR_ASSERT(error == GRPC_ERROR_NONE);
62 reclaimer_args* a = static_cast<reclaimer_args*>(args);
63 grpc_resource_user_free(a->resource_user, a->size);
64 grpc_resource_user_finish_reclamation(a->resource_user);
65 grpc_core::Closure::Run(DEBUG_LOCATION, a->then, GRPC_ERROR_NONE);
66 gpr_free(a);
67 }
68
make_reclaimer(grpc_resource_user * resource_user,size_t size,grpc_closure * then)69 grpc_closure* make_reclaimer(grpc_resource_user* resource_user, size_t size,
70 grpc_closure* then) {
71 reclaimer_args* a = static_cast<reclaimer_args*>(gpr_malloc(sizeof(*a)));
72 a->size = size;
73 a->resource_user = resource_user;
74 a->then = then;
75 return GRPC_CLOSURE_CREATE(reclaimer_cb, a, grpc_schedule_on_exec_ctx);
76 }
77
unused_reclaimer_cb(void * arg,grpc_error * error)78 static void unused_reclaimer_cb(void* arg, grpc_error* error) {
79 GPR_ASSERT(error == GRPC_ERROR_CANCELLED);
80 grpc_core::Closure::Run(DEBUG_LOCATION, static_cast<grpc_closure*>(arg),
81 GRPC_ERROR_NONE);
82 }
make_unused_reclaimer(grpc_closure * then)83 grpc_closure* make_unused_reclaimer(grpc_closure* then) {
84 return GRPC_CLOSURE_CREATE(unused_reclaimer_cb, then,
85 grpc_schedule_on_exec_ctx);
86 }
87
destroy_user(grpc_resource_user * usr)88 static void destroy_user(grpc_resource_user* usr) {
89 grpc_core::ExecCtx exec_ctx;
90 grpc_resource_user_unref(usr);
91 }
92
test_no_op(void)93 static void test_no_op(void) {
94 gpr_log(GPR_INFO, "** test_no_op **");
95 grpc_resource_quota_unref(grpc_resource_quota_create("test_no_op"));
96 }
97
test_resize_then_destroy(void)98 static void test_resize_then_destroy(void) {
99 gpr_log(GPR_INFO, "** test_resize_then_destroy **");
100 grpc_resource_quota* q =
101 grpc_resource_quota_create("test_resize_then_destroy");
102 grpc_resource_quota_resize(q, 1024 * 1024);
103 grpc_resource_quota_unref(q);
104 }
105
test_resource_user_no_op(void)106 static void test_resource_user_no_op(void) {
107 gpr_log(GPR_INFO, "** test_resource_user_no_op **");
108 grpc_resource_quota* q =
109 grpc_resource_quota_create("test_resource_user_no_op");
110 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
111 grpc_resource_quota_unref(q);
112 destroy_user(usr);
113 }
114
test_instant_alloc_then_free(void)115 static void test_instant_alloc_then_free(void) {
116 gpr_log(GPR_INFO, "** test_instant_alloc_then_free **");
117 grpc_resource_quota* q =
118 grpc_resource_quota_create("test_instant_alloc_then_free");
119 grpc_resource_quota_resize(q, 1024 * 1024);
120 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
121 {
122 grpc_core::ExecCtx exec_ctx;
123 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, nullptr));
124 }
125 {
126 grpc_core::ExecCtx exec_ctx;
127 grpc_resource_user_free(usr, 1024);
128 }
129 grpc_resource_quota_unref(q);
130 destroy_user(usr);
131 }
132
test_instant_alloc_free_pair(void)133 static void test_instant_alloc_free_pair(void) {
134 gpr_log(GPR_INFO, "** test_instant_alloc_free_pair **");
135 grpc_resource_quota* q =
136 grpc_resource_quota_create("test_instant_alloc_free_pair");
137 grpc_resource_quota_resize(q, 1024 * 1024);
138 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
139 {
140 grpc_core::ExecCtx exec_ctx;
141 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, nullptr));
142 grpc_resource_user_free(usr, 1024);
143 }
144 grpc_resource_quota_unref(q);
145 destroy_user(usr);
146 }
147
test_simple_async_alloc(void)148 static void test_simple_async_alloc(void) {
149 gpr_log(GPR_INFO, "** test_simple_async_alloc **");
150 grpc_resource_quota* q =
151 grpc_resource_quota_create("test_simple_async_alloc");
152 grpc_resource_quota_resize(q, 1024 * 1024);
153 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
154 {
155 gpr_event ev;
156 gpr_event_init(&ev);
157 grpc_core::ExecCtx exec_ctx;
158 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
159 grpc_core::ExecCtx::Get()->Flush();
160 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
161 nullptr);
162 }
163 {
164 grpc_core::ExecCtx exec_ctx;
165 grpc_resource_user_free(usr, 1024);
166 }
167 {
168 // Now the allocation should be inline.
169 GPR_ASSERT(grpc_resource_user_alloc(usr, 1024, nullptr));
170 grpc_core::ExecCtx exec_ctx;
171 grpc_resource_user_free(usr, 1024);
172 }
173 grpc_resource_quota_unref(q);
174 destroy_user(usr);
175 }
176
test_async_alloc_blocked_by_size(void)177 static void test_async_alloc_blocked_by_size(void) {
178 gpr_log(GPR_INFO, "** test_async_alloc_blocked_by_size **");
179 grpc_resource_quota* q =
180 grpc_resource_quota_create("test_async_alloc_blocked_by_size");
181 grpc_resource_quota_resize(q, 1);
182 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
183 gpr_event ev;
184 gpr_event_init(&ev);
185 {
186 grpc_core::ExecCtx exec_ctx;
187 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
188 grpc_core::ExecCtx::Get()->Flush();
189 GPR_ASSERT(gpr_event_wait(
190 &ev, grpc_timeout_milliseconds_to_deadline(100)) == nullptr);
191 }
192 grpc_resource_quota_resize(q, 1024);
193 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
194 nullptr);
195 {
196 grpc_core::ExecCtx exec_ctx;
197 grpc_resource_user_free(usr, 1024);
198 }
199 grpc_resource_quota_unref(q);
200 destroy_user(usr);
201 }
202
test_scavenge(void)203 static void test_scavenge(void) {
204 gpr_log(GPR_INFO, "** test_scavenge **");
205 grpc_resource_quota* q = grpc_resource_quota_create("test_scavenge");
206 grpc_resource_quota_resize(q, 1024);
207 grpc_resource_user* usr1 = grpc_resource_user_create(q, "usr1");
208 grpc_resource_user* usr2 = grpc_resource_user_create(q, "usr2");
209 {
210 gpr_event ev;
211 gpr_event_init(&ev);
212 grpc_core::ExecCtx exec_ctx;
213 GPR_ASSERT(!grpc_resource_user_alloc(usr1, 1024, set_event(&ev)));
214 grpc_core::ExecCtx::Get()->Flush();
215 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
216 nullptr);
217 }
218 {
219 grpc_core::ExecCtx exec_ctx;
220 grpc_resource_user_free(usr1, 1024);
221 }
222 {
223 gpr_event ev;
224 gpr_event_init(&ev);
225 grpc_core::ExecCtx exec_ctx;
226 GPR_ASSERT(!grpc_resource_user_alloc(usr2, 1024, set_event(&ev)));
227 grpc_core::ExecCtx::Get()->Flush();
228 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
229 nullptr);
230 }
231 {
232 grpc_core::ExecCtx exec_ctx;
233 grpc_resource_user_free(usr2, 1024);
234 }
235 grpc_resource_quota_unref(q);
236 destroy_user(usr1);
237 destroy_user(usr2);
238 }
239
test_scavenge_blocked(void)240 static void test_scavenge_blocked(void) {
241 gpr_log(GPR_INFO, "** test_scavenge_blocked **");
242 grpc_resource_quota* q = grpc_resource_quota_create("test_scavenge_blocked");
243 grpc_resource_quota_resize(q, 1024);
244 grpc_resource_user* usr1 = grpc_resource_user_create(q, "usr1");
245 grpc_resource_user* usr2 = grpc_resource_user_create(q, "usr2");
246 gpr_event ev;
247 {
248 gpr_event_init(&ev);
249 grpc_core::ExecCtx exec_ctx;
250 GPR_ASSERT(!grpc_resource_user_alloc(usr1, 1024, set_event(&ev)));
251 grpc_core::ExecCtx::Get()->Flush();
252 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
253 nullptr);
254 }
255 {
256 gpr_event_init(&ev);
257 grpc_core::ExecCtx exec_ctx;
258 GPR_ASSERT(!grpc_resource_user_alloc(usr2, 1024, set_event(&ev)));
259 grpc_core::ExecCtx::Get()->Flush();
260 GPR_ASSERT(gpr_event_wait(
261 &ev, grpc_timeout_milliseconds_to_deadline(100)) == nullptr);
262 }
263 {
264 grpc_core::ExecCtx exec_ctx;
265 grpc_resource_user_free(usr1, 1024);
266 grpc_core::ExecCtx::Get()->Flush();
267 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
268 nullptr);
269 }
270 {
271 grpc_core::ExecCtx exec_ctx;
272 grpc_resource_user_free(usr2, 1024);
273 }
274 grpc_resource_quota_unref(q);
275 destroy_user(usr1);
276 destroy_user(usr2);
277 }
278
test_blocked_until_scheduled_reclaim(void)279 static void test_blocked_until_scheduled_reclaim(void) {
280 gpr_log(GPR_INFO, "** test_blocked_until_scheduled_reclaim **");
281 grpc_resource_quota* q =
282 grpc_resource_quota_create("test_blocked_until_scheduled_reclaim");
283 grpc_resource_quota_resize(q, 1024);
284 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
285 {
286 gpr_event ev;
287 gpr_event_init(&ev);
288 grpc_core::ExecCtx exec_ctx;
289 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
290 grpc_core::ExecCtx::Get()->Flush();
291 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
292 nullptr);
293 }
294 gpr_event reclaim_done;
295 gpr_event_init(&reclaim_done);
296 {
297 grpc_core::ExecCtx exec_ctx;
298 grpc_resource_user_post_reclaimer(
299 usr, false, make_reclaimer(usr, 1024, set_event(&reclaim_done)));
300 }
301 {
302 gpr_event ev;
303 gpr_event_init(&ev);
304 grpc_core::ExecCtx exec_ctx;
305 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
306 grpc_core::ExecCtx::Get()->Flush();
307 GPR_ASSERT(gpr_event_wait(&reclaim_done,
308 grpc_timeout_seconds_to_deadline(5)) != nullptr);
309 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
310 nullptr);
311 ;
312 }
313 {
314 grpc_core::ExecCtx exec_ctx;
315 grpc_resource_user_free(usr, 1024);
316 }
317 grpc_resource_quota_unref(q);
318 destroy_user(usr);
319 }
320
test_blocked_until_scheduled_reclaim_and_scavenge(void)321 static void test_blocked_until_scheduled_reclaim_and_scavenge(void) {
322 gpr_log(GPR_INFO, "** test_blocked_until_scheduled_reclaim_and_scavenge **");
323 grpc_resource_quota* q = grpc_resource_quota_create(
324 "test_blocked_until_scheduled_reclaim_and_scavenge");
325 grpc_resource_quota_resize(q, 1024);
326 grpc_resource_user* usr1 = grpc_resource_user_create(q, "usr1");
327 grpc_resource_user* usr2 = grpc_resource_user_create(q, "usr2");
328 {
329 gpr_event ev;
330 gpr_event_init(&ev);
331 grpc_core::ExecCtx exec_ctx;
332 GPR_ASSERT(!grpc_resource_user_alloc(usr1, 1024, set_event(&ev)));
333 grpc_core::ExecCtx::Get()->Flush();
334 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
335 nullptr);
336 ;
337 }
338 gpr_event reclaim_done;
339 gpr_event_init(&reclaim_done);
340 {
341 grpc_core::ExecCtx exec_ctx;
342 grpc_resource_user_post_reclaimer(
343 usr1, false, make_reclaimer(usr1, 1024, set_event(&reclaim_done)));
344 }
345 {
346 gpr_event ev;
347 gpr_event_init(&ev);
348 grpc_core::ExecCtx exec_ctx;
349 GPR_ASSERT(!grpc_resource_user_alloc(usr2, 1024, set_event(&ev)));
350 grpc_core::ExecCtx::Get()->Flush();
351 GPR_ASSERT(gpr_event_wait(&reclaim_done,
352 grpc_timeout_seconds_to_deadline(5)) != nullptr);
353 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
354 nullptr);
355 ;
356 }
357 {
358 grpc_core::ExecCtx exec_ctx;
359 grpc_resource_user_free(usr2, 1024);
360 }
361 grpc_resource_quota_unref(q);
362 destroy_user(usr1);
363 destroy_user(usr2);
364 }
365
test_blocked_until_scheduled_destructive_reclaim(void)366 static void test_blocked_until_scheduled_destructive_reclaim(void) {
367 gpr_log(GPR_INFO, "** test_blocked_until_scheduled_destructive_reclaim **");
368 grpc_resource_quota* q = grpc_resource_quota_create(
369 "test_blocked_until_scheduled_destructive_reclaim");
370 grpc_resource_quota_resize(q, 1024);
371 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
372 {
373 gpr_event ev;
374 gpr_event_init(&ev);
375 grpc_core::ExecCtx exec_ctx;
376 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
377 grpc_core::ExecCtx::Get()->Flush();
378 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
379 nullptr);
380 ;
381 }
382 gpr_event reclaim_done;
383 gpr_event_init(&reclaim_done);
384 {
385 grpc_core::ExecCtx exec_ctx;
386 grpc_resource_user_post_reclaimer(
387 usr, true, make_reclaimer(usr, 1024, set_event(&reclaim_done)));
388 }
389 {
390 gpr_event ev;
391 gpr_event_init(&ev);
392 grpc_core::ExecCtx exec_ctx;
393 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
394 grpc_core::ExecCtx::Get()->Flush();
395 GPR_ASSERT(gpr_event_wait(&reclaim_done,
396 grpc_timeout_seconds_to_deadline(5)) != nullptr);
397 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
398 nullptr);
399 ;
400 }
401 {
402 grpc_core::ExecCtx exec_ctx;
403 grpc_resource_user_free(usr, 1024);
404 }
405 grpc_resource_quota_unref(q);
406 destroy_user(usr);
407 }
408
test_unused_reclaim_is_cancelled(void)409 static void test_unused_reclaim_is_cancelled(void) {
410 gpr_log(GPR_INFO, "** test_unused_reclaim_is_cancelled **");
411 grpc_resource_quota* q =
412 grpc_resource_quota_create("test_unused_reclaim_is_cancelled");
413 grpc_resource_quota_resize(q, 1024);
414 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
415 gpr_event benign_done;
416 gpr_event_init(&benign_done);
417 gpr_event destructive_done;
418 gpr_event_init(&destructive_done);
419 {
420 grpc_core::ExecCtx exec_ctx;
421 grpc_resource_user_post_reclaimer(
422 usr, false, make_unused_reclaimer(set_event(&benign_done)));
423 grpc_resource_user_post_reclaimer(
424 usr, true, make_unused_reclaimer(set_event(&destructive_done)));
425 grpc_core::ExecCtx::Get()->Flush();
426 GPR_ASSERT(gpr_event_wait(&benign_done,
427 grpc_timeout_milliseconds_to_deadline(100)) ==
428 nullptr);
429 GPR_ASSERT(gpr_event_wait(&destructive_done,
430 grpc_timeout_milliseconds_to_deadline(100)) ==
431 nullptr);
432 }
433 grpc_resource_quota_unref(q);
434 destroy_user(usr);
435 GPR_ASSERT(gpr_event_wait(&benign_done,
436 grpc_timeout_seconds_to_deadline(5)) != nullptr);
437 GPR_ASSERT(gpr_event_wait(&destructive_done,
438 grpc_timeout_seconds_to_deadline(5)) != nullptr);
439 }
440
test_benign_reclaim_is_preferred(void)441 static void test_benign_reclaim_is_preferred(void) {
442 gpr_log(GPR_INFO, "** test_benign_reclaim_is_preferred **");
443 grpc_resource_quota* q =
444 grpc_resource_quota_create("test_benign_reclaim_is_preferred");
445 grpc_resource_quota_resize(q, 1024);
446 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
447 gpr_event benign_done;
448 gpr_event_init(&benign_done);
449 gpr_event destructive_done;
450 gpr_event_init(&destructive_done);
451 {
452 gpr_event ev;
453 gpr_event_init(&ev);
454 grpc_core::ExecCtx exec_ctx;
455 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
456 grpc_core::ExecCtx::Get()->Flush();
457 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
458 nullptr);
459 ;
460 }
461 {
462 grpc_core::ExecCtx exec_ctx;
463 grpc_resource_user_post_reclaimer(
464 usr, false, make_reclaimer(usr, 1024, set_event(&benign_done)));
465 grpc_resource_user_post_reclaimer(
466 usr, true, make_unused_reclaimer(set_event(&destructive_done)));
467 grpc_core::ExecCtx::Get()->Flush();
468 GPR_ASSERT(gpr_event_wait(&benign_done,
469 grpc_timeout_milliseconds_to_deadline(100)) ==
470 nullptr);
471 GPR_ASSERT(gpr_event_wait(&destructive_done,
472 grpc_timeout_milliseconds_to_deadline(100)) ==
473 nullptr);
474 }
475 {
476 gpr_event ev;
477 gpr_event_init(&ev);
478 grpc_core::ExecCtx exec_ctx;
479 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
480 grpc_core::ExecCtx::Get()->Flush();
481 GPR_ASSERT(gpr_event_wait(&benign_done,
482 grpc_timeout_seconds_to_deadline(5)) != nullptr);
483 GPR_ASSERT(gpr_event_wait(&destructive_done,
484 grpc_timeout_milliseconds_to_deadline(100)) ==
485 nullptr);
486 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
487 nullptr);
488 }
489 {
490 grpc_core::ExecCtx exec_ctx;
491 grpc_resource_user_free(usr, 1024);
492 }
493 grpc_resource_quota_unref(q);
494 destroy_user(usr);
495 GPR_ASSERT(gpr_event_wait(&benign_done,
496 grpc_timeout_seconds_to_deadline(5)) != nullptr);
497 GPR_ASSERT(gpr_event_wait(&destructive_done,
498 grpc_timeout_seconds_to_deadline(5)) != nullptr);
499 }
500
test_multiple_reclaims_can_be_triggered(void)501 static void test_multiple_reclaims_can_be_triggered(void) {
502 gpr_log(GPR_INFO, "** test_multiple_reclaims_can_be_triggered **");
503 grpc_resource_quota* q =
504 grpc_resource_quota_create("test_multiple_reclaims_can_be_triggered");
505 grpc_resource_quota_resize(q, 1024);
506 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
507 gpr_event benign_done;
508 gpr_event_init(&benign_done);
509 gpr_event destructive_done;
510 gpr_event_init(&destructive_done);
511 {
512 gpr_event ev;
513 gpr_event_init(&ev);
514 grpc_core::ExecCtx exec_ctx;
515 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
516 grpc_core::ExecCtx::Get()->Flush();
517 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
518 nullptr);
519 ;
520 }
521 {
522 grpc_core::ExecCtx exec_ctx;
523 grpc_resource_user_post_reclaimer(
524 usr, false, make_reclaimer(usr, 512, set_event(&benign_done)));
525 grpc_resource_user_post_reclaimer(
526 usr, true, make_reclaimer(usr, 512, set_event(&destructive_done)));
527 grpc_core::ExecCtx::Get()->Flush();
528 GPR_ASSERT(gpr_event_wait(&benign_done,
529 grpc_timeout_milliseconds_to_deadline(100)) ==
530 nullptr);
531 GPR_ASSERT(gpr_event_wait(&destructive_done,
532 grpc_timeout_milliseconds_to_deadline(100)) ==
533 nullptr);
534 }
535 {
536 gpr_event ev;
537 gpr_event_init(&ev);
538 grpc_core::ExecCtx exec_ctx;
539 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&ev)));
540 grpc_core::ExecCtx::Get()->Flush();
541 GPR_ASSERT(gpr_event_wait(&benign_done,
542 grpc_timeout_seconds_to_deadline(5)) != nullptr);
543 GPR_ASSERT(gpr_event_wait(&destructive_done,
544 grpc_timeout_seconds_to_deadline(5)) != nullptr);
545 GPR_ASSERT(gpr_event_wait(&ev, grpc_timeout_seconds_to_deadline(5)) !=
546 nullptr);
547 ;
548 }
549 {
550 grpc_core::ExecCtx exec_ctx;
551 grpc_resource_user_free(usr, 1024);
552 }
553 grpc_resource_quota_unref(q);
554 destroy_user(usr);
555 GPR_ASSERT(gpr_event_wait(&benign_done,
556 grpc_timeout_seconds_to_deadline(5)) != nullptr);
557 GPR_ASSERT(gpr_event_wait(&destructive_done,
558 grpc_timeout_seconds_to_deadline(5)) != nullptr);
559 }
560
test_resource_user_stays_allocated_until_memory_released(void)561 static void test_resource_user_stays_allocated_until_memory_released(void) {
562 gpr_log(GPR_INFO,
563 "** test_resource_user_stays_allocated_until_memory_released **");
564 grpc_resource_quota* q = grpc_resource_quota_create(
565 "test_resource_user_stays_allocated_until_memory_released");
566 grpc_resource_quota_resize(q, 1024 * 1024);
567 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
568 {
569 grpc_core::ExecCtx exec_ctx;
570 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, nullptr));
571 }
572 {
573 grpc_core::ExecCtx exec_ctx;
574 grpc_resource_quota_unref(q);
575 grpc_resource_user_unref(usr);
576 }
577 {
578 grpc_core::ExecCtx exec_ctx;
579 grpc_resource_user_free(usr, 1024);
580 }
581 }
582
583 static void
test_resource_user_stays_allocated_and_reclaimers_unrun_until_memory_released(void)584 test_resource_user_stays_allocated_and_reclaimers_unrun_until_memory_released(
585 void) {
586 gpr_log(GPR_INFO,
587 "** "
588 "test_resource_user_stays_allocated_and_reclaimers_unrun_until_"
589 "memory_released **");
590 grpc_resource_quota* q = grpc_resource_quota_create(
591 "test_resource_user_stays_allocated_and_reclaimers_unrun_until_memory_"
592 "released");
593 grpc_resource_quota_resize(q, 1024);
594 for (int i = 0; i < 10; i++) {
595 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
596 gpr_event reclaimer_cancelled;
597 gpr_event_init(&reclaimer_cancelled);
598 {
599 grpc_core::ExecCtx exec_ctx;
600 grpc_resource_user_post_reclaimer(
601 usr, false, make_unused_reclaimer(set_event(&reclaimer_cancelled)));
602 grpc_core::ExecCtx::Get()->Flush();
603 GPR_ASSERT(gpr_event_wait(&reclaimer_cancelled,
604 grpc_timeout_milliseconds_to_deadline(100)) ==
605 nullptr);
606 }
607 {
608 gpr_event allocated;
609 gpr_event_init(&allocated);
610 grpc_core::ExecCtx exec_ctx;
611 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&allocated)));
612 grpc_core::ExecCtx::Get()->Flush();
613 GPR_ASSERT(gpr_event_wait(&allocated, grpc_timeout_seconds_to_deadline(
614 5)) != nullptr);
615 GPR_ASSERT(gpr_event_wait(&reclaimer_cancelled,
616 grpc_timeout_milliseconds_to_deadline(100)) ==
617 nullptr);
618 }
619 {
620 grpc_core::ExecCtx exec_ctx;
621 grpc_resource_user_unref(usr);
622 grpc_core::ExecCtx::Get()->Flush();
623 GPR_ASSERT(gpr_event_wait(&reclaimer_cancelled,
624 grpc_timeout_milliseconds_to_deadline(100)) ==
625 nullptr);
626 }
627 {
628 grpc_core::ExecCtx exec_ctx;
629 grpc_resource_user_free(usr, 1024);
630 grpc_core::ExecCtx::Get()->Flush();
631 GPR_ASSERT(gpr_event_wait(&reclaimer_cancelled,
632 grpc_timeout_seconds_to_deadline(5)) !=
633 nullptr);
634 }
635 }
636 grpc_resource_quota_unref(q);
637 }
638
test_reclaimers_can_be_posted_repeatedly(void)639 static void test_reclaimers_can_be_posted_repeatedly(void) {
640 gpr_log(GPR_INFO, "** test_reclaimers_can_be_posted_repeatedly **");
641 grpc_resource_quota* q =
642 grpc_resource_quota_create("test_reclaimers_can_be_posted_repeatedly");
643 grpc_resource_quota_resize(q, 1024);
644 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
645 {
646 gpr_event allocated;
647 gpr_event_init(&allocated);
648 grpc_core::ExecCtx exec_ctx;
649 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&allocated)));
650 grpc_core::ExecCtx::Get()->Flush();
651 GPR_ASSERT(gpr_event_wait(&allocated,
652 grpc_timeout_seconds_to_deadline(5)) != nullptr);
653 }
654 for (int i = 0; i < 10; i++) {
655 gpr_event reclaimer_done;
656 gpr_event_init(&reclaimer_done);
657 {
658 grpc_core::ExecCtx exec_ctx;
659 grpc_resource_user_post_reclaimer(
660 usr, false, make_reclaimer(usr, 1024, set_event(&reclaimer_done)));
661 grpc_core::ExecCtx::Get()->Flush();
662 GPR_ASSERT(gpr_event_wait(&reclaimer_done,
663 grpc_timeout_milliseconds_to_deadline(100)) ==
664 nullptr);
665 }
666 {
667 gpr_event allocated;
668 gpr_event_init(&allocated);
669 grpc_core::ExecCtx exec_ctx;
670 GPR_ASSERT(!grpc_resource_user_alloc(usr, 1024, set_event(&allocated)));
671 grpc_core::ExecCtx::Get()->Flush();
672 GPR_ASSERT(gpr_event_wait(&allocated, grpc_timeout_seconds_to_deadline(
673 5)) != nullptr);
674 GPR_ASSERT(gpr_event_wait(&reclaimer_done,
675 grpc_timeout_seconds_to_deadline(5)) !=
676 nullptr);
677 }
678 }
679 {
680 grpc_core::ExecCtx exec_ctx;
681 grpc_resource_user_free(usr, 1024);
682 }
683 destroy_user(usr);
684 grpc_resource_quota_unref(q);
685 }
686
test_one_slice(void)687 static void test_one_slice(void) {
688 gpr_log(GPR_INFO, "** test_one_slice **");
689
690 grpc_resource_quota* q = grpc_resource_quota_create("test_one_slice");
691 grpc_resource_quota_resize(q, 1024);
692
693 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
694
695 grpc_resource_user_slice_allocator alloc;
696 int num_allocs = 0;
697 grpc_resource_user_slice_allocator_init(&alloc, usr, inc_int_cb, &num_allocs);
698
699 grpc_slice_buffer buffer;
700 grpc_slice_buffer_init(&buffer);
701
702 {
703 const int start_allocs = num_allocs;
704 grpc_core::ExecCtx exec_ctx;
705 GPR_ASSERT(!grpc_resource_user_alloc_slices(&alloc, 1024, 1, &buffer));
706 grpc_core::ExecCtx::Get()->Flush();
707 assert_counter_becomes(&num_allocs, start_allocs + 1);
708 }
709
710 {
711 grpc_core::ExecCtx exec_ctx;
712 grpc_slice_buffer_destroy_internal(&buffer);
713 }
714 destroy_user(usr);
715 grpc_resource_quota_unref(q);
716 }
717
test_one_slice_deleted_late(void)718 static void test_one_slice_deleted_late(void) {
719 gpr_log(GPR_INFO, "** test_one_slice_deleted_late **");
720
721 grpc_resource_quota* q =
722 grpc_resource_quota_create("test_one_slice_deleted_late");
723 grpc_resource_quota_resize(q, 1024);
724
725 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
726
727 grpc_resource_user_slice_allocator alloc;
728 int num_allocs = 0;
729 grpc_resource_user_slice_allocator_init(&alloc, usr, inc_int_cb, &num_allocs);
730
731 grpc_slice_buffer buffer;
732 grpc_slice_buffer_init(&buffer);
733
734 {
735 const int start_allocs = num_allocs;
736 grpc_core::ExecCtx exec_ctx;
737 GPR_ASSERT(!grpc_resource_user_alloc_slices(&alloc, 1024, 1, &buffer));
738 grpc_core::ExecCtx::Get()->Flush();
739 assert_counter_becomes(&num_allocs, start_allocs + 1);
740 }
741
742 {
743 grpc_core::ExecCtx exec_ctx;
744 grpc_resource_user_unref(usr);
745 }
746
747 grpc_resource_quota_unref(q);
748 {
749 grpc_core::ExecCtx exec_ctx;
750 grpc_slice_buffer_destroy_internal(&buffer);
751 }
752 }
753
test_resize_to_zero(void)754 static void test_resize_to_zero(void) {
755 gpr_log(GPR_INFO, "** test_resize_to_zero **");
756 grpc_resource_quota* q = grpc_resource_quota_create("test_resize_to_zero");
757 grpc_resource_quota_resize(q, 0);
758 grpc_resource_quota_unref(q);
759 }
760
test_negative_rq_free_pool(void)761 static void test_negative_rq_free_pool(void) {
762 gpr_log(GPR_INFO, "** test_negative_rq_free_pool **");
763 grpc_resource_quota* q =
764 grpc_resource_quota_create("test_negative_rq_free_pool");
765 grpc_resource_quota_resize(q, 1024);
766
767 grpc_resource_user* usr = grpc_resource_user_create(q, "usr");
768
769 grpc_resource_user_slice_allocator alloc;
770 int num_allocs = 0;
771 grpc_resource_user_slice_allocator_init(&alloc, usr, inc_int_cb, &num_allocs);
772
773 grpc_slice_buffer buffer;
774 grpc_slice_buffer_init(&buffer);
775
776 {
777 const int start_allocs = num_allocs;
778 grpc_core::ExecCtx exec_ctx;
779 GPR_ASSERT(!grpc_resource_user_alloc_slices(&alloc, 1024, 1, &buffer));
780 grpc_core::ExecCtx::Get()->Flush();
781 assert_counter_becomes(&num_allocs, start_allocs + 1);
782 }
783
784 grpc_resource_quota_resize(q, 512);
785
786 double eps = 0.0001;
787 GPR_ASSERT(grpc_resource_quota_get_memory_pressure(q) < 1 + eps);
788 GPR_ASSERT(grpc_resource_quota_get_memory_pressure(q) > 1 - eps);
789
790 {
791 grpc_core::ExecCtx exec_ctx;
792 grpc_resource_user_unref(usr);
793 }
794
795 grpc_resource_quota_unref(q);
796 {
797 grpc_core::ExecCtx exec_ctx;
798 grpc_slice_buffer_destroy_internal(&buffer);
799 }
800 }
801
802 // Simple test to check resource quota thread limits
test_thread_limit()803 static void test_thread_limit() {
804 grpc_core::ExecCtx exec_ctx;
805
806 grpc_resource_quota* rq = grpc_resource_quota_create("test_thread_limit");
807 grpc_resource_user* ru1 = grpc_resource_user_create(rq, "ru1");
808 grpc_resource_user* ru2 = grpc_resource_user_create(rq, "ru2");
809
810 // Max threads = 100
811 grpc_resource_quota_set_max_threads(rq, 100);
812
813 // Request quota for 100 threads (50 for ru1, 50 for ru2)
814 GPR_ASSERT(grpc_resource_user_allocate_threads(ru1, 10));
815 GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 10));
816 GPR_ASSERT(grpc_resource_user_allocate_threads(ru1, 40));
817 GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 40));
818
819 // Threads exhausted. Next request must fail
820 GPR_ASSERT(!grpc_resource_user_allocate_threads(ru2, 20));
821
822 // Free 20 threads from two different users
823 grpc_resource_user_free_threads(ru1, 10);
824 grpc_resource_user_free_threads(ru2, 10);
825
826 // Next request to 20 threads must succeed
827 GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 20));
828
829 // No more thread quota again
830 GPR_ASSERT(!grpc_resource_user_allocate_threads(ru1, 20));
831
832 // Free 10 more
833 grpc_resource_user_free_threads(ru1, 10);
834
835 GPR_ASSERT(grpc_resource_user_allocate_threads(ru1, 5));
836 GPR_ASSERT(
837 !grpc_resource_user_allocate_threads(ru2, 10)); // Only 5 available
838 GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 5));
839
840 // Teardown (ru1 and ru2 release all the quota back to rq)
841 grpc_resource_user_unref(ru1);
842 grpc_resource_user_unref(ru2);
843 grpc_resource_quota_unref(rq);
844 }
845
846 // Change max quota in either direction dynamically
test_thread_maxquota_change()847 static void test_thread_maxquota_change() {
848 grpc_core::ExecCtx exec_ctx;
849
850 grpc_resource_quota* rq =
851 grpc_resource_quota_create("test_thread_maxquota_change");
852 grpc_resource_user* ru1 = grpc_resource_user_create(rq, "ru1");
853 grpc_resource_user* ru2 = grpc_resource_user_create(rq, "ru2");
854
855 // Max threads = 100
856 grpc_resource_quota_set_max_threads(rq, 100);
857
858 // Request quota for 100 threads (50 for ru1, 50 for ru2)
859 GPR_ASSERT(grpc_resource_user_allocate_threads(ru1, 50));
860 GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 50));
861
862 // Threads exhausted. Next request must fail
863 GPR_ASSERT(!grpc_resource_user_allocate_threads(ru2, 20));
864
865 // Increase maxquota and retry
866 // Max threads = 150;
867 grpc_resource_quota_set_max_threads(rq, 150);
868 GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 20)); // ru2=70, ru1=50
869
870 // Decrease maxquota (Note: Quota already given to ru1 and ru2 is unaffected)
871 // Max threads = 10;
872 grpc_resource_quota_set_max_threads(rq, 10);
873
874 // New requests will fail until quota is available
875 GPR_ASSERT(!grpc_resource_user_allocate_threads(ru1, 10));
876
877 // Make quota available
878 grpc_resource_user_free_threads(ru1, 50); // ru1 now has 0
879 GPR_ASSERT(!grpc_resource_user_allocate_threads(ru1, 10)); // not enough
880
881 grpc_resource_user_free_threads(ru2, 70); // ru2 now has 0
882
883 // Now we can get quota up-to 10, the current max
884 GPR_ASSERT(grpc_resource_user_allocate_threads(ru2, 10));
885 // No more thread quota again
886 GPR_ASSERT(!grpc_resource_user_allocate_threads(ru1, 10));
887
888 // Teardown (ru1 and ru2 release all the quota back to rq)
889 grpc_resource_user_unref(ru1);
890 grpc_resource_user_unref(ru2);
891 grpc_resource_quota_unref(rq);
892 }
893
main(int argc,char ** argv)894 int main(int argc, char** argv) {
895 grpc::testing::TestEnvironment env(argc, argv);
896 grpc_init();
897 gpr_mu_init(&g_mu);
898 gpr_cv_init(&g_cv);
899 test_no_op();
900 test_resize_then_destroy();
901 test_resource_user_no_op();
902 test_instant_alloc_then_free();
903 test_instant_alloc_free_pair();
904 test_simple_async_alloc();
905 test_async_alloc_blocked_by_size();
906 test_scavenge();
907 test_scavenge_blocked();
908 test_blocked_until_scheduled_reclaim();
909 test_blocked_until_scheduled_reclaim_and_scavenge();
910 test_blocked_until_scheduled_destructive_reclaim();
911 test_unused_reclaim_is_cancelled();
912 test_benign_reclaim_is_preferred();
913 test_multiple_reclaims_can_be_triggered();
914 test_resource_user_stays_allocated_until_memory_released();
915 test_resource_user_stays_allocated_and_reclaimers_unrun_until_memory_released();
916 test_reclaimers_can_be_posted_repeatedly();
917 test_one_slice();
918 test_one_slice_deleted_late();
919 test_resize_to_zero();
920 test_negative_rq_free_pool();
921 gpr_mu_destroy(&g_mu);
922 gpr_cv_destroy(&g_cv);
923
924 // Resource quota thread related
925 test_thread_limit();
926 test_thread_maxquota_change();
927
928 grpc_shutdown();
929 return 0;
930 }
931