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::testing::TestEnvironment env(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