• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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