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