3 * Copyright 2017 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <grpc/support/port_platform.h>
21 #include "src/core/ext/filters/max_age/max_age_filter.h"
26 #include "src/core/lib/channel/channel_args.h"
27 #include "src/core/lib/channel/channel_stack_builder.h"
28 #include "src/core/lib/iomgr/timer.h"
29 #include "src/core/lib/surface/channel_init.h"
30 #include "src/core/lib/transport/http2_errors.h"
32 #define DEFAULT_MAX_CONNECTION_AGE_MS INT_MAX
33 #define DEFAULT_MAX_CONNECTION_AGE_GRACE_MS INT_MAX
34 #define DEFAULT_MAX_CONNECTION_IDLE_MS INT_MAX
35 #define MAX_CONNECTION_AGE_JITTER 0.1
37 #define MAX_CONNECTION_AGE_INTEGER_OPTIONS \
38 { DEFAULT_MAX_CONNECTION_AGE_MS, 1, INT_MAX }
39 #define MAX_CONNECTION_IDLE_INTEGER_OPTIONS \
40 { DEFAULT_MAX_CONNECTION_IDLE_MS, 1, INT_MAX }
42 /* States for idle_state in channel_data */
43 #define MAX_IDLE_STATE_INIT ((gpr_atm)0)
44 #define MAX_IDLE_STATE_SEEN_EXIT_IDLE ((gpr_atm)1)
45 #define MAX_IDLE_STATE_SEEN_ENTER_IDLE ((gpr_atm)2)
46 #define MAX_IDLE_STATE_TIMER_SET ((gpr_atm)3)
50 /* The channel stack to which we take refs for pending callbacks. */
51 grpc_channel_stack* channel_stack;
52 /* Guards access to max_age_timer, max_age_timer_pending, max_age_grace_timer
53 and max_age_grace_timer_pending */
54 gpr_mu max_age_timer_mu;
55 /* True if the max_age timer callback is currently pending */
56 bool max_age_timer_pending;
57 /* True if the max_age_grace timer callback is currently pending */
58 bool max_age_grace_timer_pending;
59 /* The timer for checking if the channel has reached its max age */
60 grpc_timer max_age_timer;
61 /* The timer for checking if the max-aged channel has uesed up the grace
63 grpc_timer max_age_grace_timer;
64 /* The timer for checking if the channel's idle duration reaches
65 max_connection_idle */
66 grpc_timer max_idle_timer;
67 /* Allowed max time a channel may have no outstanding rpcs */
68 grpc_millis max_connection_idle;
69 /* Allowed max time a channel may exist */
70 grpc_millis max_connection_age;
71 /* Allowed grace period after the channel reaches its max age */
72 grpc_millis max_connection_age_grace;
73 /* Closure to run when the channel's idle duration reaches max_connection_idle
74 and should be closed gracefully */
75 grpc_closure max_idle_timer_cb;
76 /* Closure to run when the channel reaches its max age and should be closed
78 grpc_closure close_max_age_channel;
79 /* Closure to run the channel uses up its max age grace time and should be
81 grpc_closure force_close_max_age_channel;
82 /* Closure to run when the init fo channel stack is done and the max_idle
83 timer should be started */
84 grpc_closure start_max_idle_timer_after_init;
85 /* Closure to run when the init fo channel stack is done and the max_age timer
87 grpc_closure start_max_age_timer_after_init;
88 /* Closure to run when the goaway op is finished and the max_age_timer */
89 grpc_closure start_max_age_grace_timer_after_goaway_op;
90 /* Closure to run when the channel connectivity state changes */
91 grpc_closure channel_connectivity_changed;
92 /* Records the current connectivity state */
93 grpc_connectivity_state connectivity_state;
94 /* Number of active calls */
96 /* TODO(zyc): C++lize this state machine */
97 /* 'idle_state' holds the states of max_idle_timer and channel idleness.
98 It can contain one of the following values:
99 +--------------------------------+----------------+---------+
100 | idle_state | max_idle_timer | channel |
101 +--------------------------------+----------------+---------+
102 | MAX_IDLE_STATE_INIT | unset | busy |
103 | MAX_IDLE_STATE_TIMER_SET | set, valid | idle |
104 | MAX_IDLE_STATE_SEEN_EXIT_IDLE | set, invalid | busy |
105 | MAX_IDLE_STATE_SEEN_ENTER_IDLE | set, invalid | idle |
106 +--------------------------------+----------------+---------+
108 MAX_IDLE_STATE_INIT: The initial and final state of 'idle_state'. The
109 channel has 1 or 1+ active calls, and the timer is not set. Note that
110 we may put a virtual call to hold this state at channel initialization or
111 shutdown, so that the channel won't enter other states.
113 MAX_IDLE_STATE_TIMER_SET: The state after the timer is set and no calls
114 have arrived after the timer is set. The channel must have 0 active call in
115 this state. If the timer is fired in this state, we will close the channel
118 MAX_IDLE_STATE_SEEN_EXIT_IDLE: The state after the timer is set and at
119 least one call has arrived after the timer is set. The channel must have 1
120 or 1+ active calls in this state. If the timer is fired in this state, we
123 MAX_IDLE_STATE_SEEN_ENTER_IDLE: The state after the timer is set and the at
124 least one call has arrived after the timer is set, BUT the channel
125 currently has 0 active calls. If the timer is fired in this state, we will
128 max_idle_timer will not be cancelled (unless the channel is shutting down).
129 If the timer callback is called when the max_idle_timer is valid (i.e.
130 idle_state is MAX_IDLE_STATE_TIMER_SET), the channel will be closed due to
131 idleness, otherwise the channel won't be changed.
134 MAX_IDLE_STATE_INIT <-------3------ MAX_IDLE_STATE_SEEN_EXIT_IDLE
137 1 2 +-----------4------------+ 6 7
140 MAX_IDLE_STATE_TIMER_SET <----5------ MAX_IDLE_STATE_SEEN_ENTER_IDLE
142 For 1, 3, 5 : See max_idle_timer_cb() function
143 For 2, 7 : See decrease_call_count() function
144 For 4, 6 : See increase_call_count() function */
146 /* Time when the channel finished its last outstanding call, in grpc_millis */
147 gpr_atm last_enter_idle_time_millis;
151 /* Increase the nubmer of active calls. Before the increasement, if there are no
152 calls, the max_idle_timer should be cancelled. */
153 static void increase_call_count(channel_data* chand) {
155 if (gpr_atm_full_fetch_add(&chand->call_count, 1) == 0) {
157 gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state);
158 switch (idle_state) {
159 case MAX_IDLE_STATE_TIMER_SET:
160 /* max_idle_timer_cb may have already set idle_state to
161 MAX_IDLE_STATE_INIT, in this case, we don't need to set it to
162 MAX_IDLE_STATE_SEEN_EXIT_IDLE */
163 gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_TIMER_SET,
164 MAX_IDLE_STATE_SEEN_EXIT_IDLE);
166 case MAX_IDLE_STATE_SEEN_ENTER_IDLE:
167 gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE);
177 /* Decrease the nubmer of active calls. After the decrement, if there are no
178 calls, the max_idle_timer should be started. */
179 static void decrease_call_count(channel_data* chand) {
181 if (gpr_atm_full_fetch_add(&chand->call_count, -1) == 1) {
182 gpr_atm_no_barrier_store(&chand->last_enter_idle_time_millis,
183 (gpr_atm)grpc_core::ExecCtx::Get()->Now());
185 gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state);
186 switch (idle_state) {
187 case MAX_IDLE_STATE_INIT:
188 GRPC_CHANNEL_STACK_REF(chand->channel_stack,
189 "max_age max_idle_timer");
191 &chand->max_idle_timer,
192 grpc_core::ExecCtx::Get()->Now() + chand->max_connection_idle,
193 &chand->max_idle_timer_cb);
194 gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_TIMER_SET);
196 case MAX_IDLE_STATE_SEEN_EXIT_IDLE:
197 if (gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE,
198 MAX_IDLE_STATE_SEEN_ENTER_IDLE)) {
210 static void start_max_idle_timer_after_init(void* arg, grpc_error* error) {
211 channel_data* chand = static_cast<channel_data*>(arg);
212 /* Decrease call_count. If there are no active calls at this time,
213 max_idle_timer will start here. If the number of active calls is not 0,
214 max_idle_timer will start after all the active calls end. */
215 decrease_call_count(chand);
216 GRPC_CHANNEL_STACK_UNREF(chand->channel_stack,
217 "max_age start_max_idle_timer_after_init");
220 static void start_max_age_timer_after_init(void* arg, grpc_error* error) {
221 channel_data* chand = static_cast<channel_data*>(arg);
222 gpr_mu_lock(&chand->max_age_timer_mu);
223 chand->max_age_timer_pending = true;
224 GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_timer");
225 grpc_timer_init(&chand->max_age_timer,
226 grpc_core::ExecCtx::Get()->Now() + chand->max_connection_age,
227 &chand->close_max_age_channel);
228 gpr_mu_unlock(&chand->max_age_timer_mu);
229 grpc_transport_op* op = grpc_make_transport_op(nullptr);
230 op->on_connectivity_state_change = &chand->channel_connectivity_changed;
231 op->connectivity_state = &chand->connectivity_state;
232 grpc_channel_next_op(grpc_channel_stack_element(chand->channel_stack, 0), op);
233 GRPC_CHANNEL_STACK_UNREF(chand->channel_stack,
234 "max_age start_max_age_timer_after_init");
237 static void start_max_age_grace_timer_after_goaway_op(void* arg,
239 channel_data* chand = static_cast<channel_data*>(arg);
240 gpr_mu_lock(&chand->max_age_timer_mu);
241 chand->max_age_grace_timer_pending = true;
242 GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_grace_timer");
244 &chand->max_age_grace_timer,
245 chand->max_connection_age_grace == GRPC_MILLIS_INF_FUTURE
246 ? GRPC_MILLIS_INF_FUTURE
247 : grpc_core::ExecCtx::Get()->Now() + chand->max_connection_age_grace,
248 &chand->force_close_max_age_channel);
249 gpr_mu_unlock(&chand->max_age_timer_mu);
250 GRPC_CHANNEL_STACK_UNREF(chand->channel_stack,
251 "max_age start_max_age_grace_timer_after_goaway_op");
254 static void close_max_idle_channel(channel_data* chand) {
255 /* Prevent the max idle timer from being set again */
256 gpr_atm_no_barrier_fetch_add(&chand->call_count, 1);
257 grpc_transport_op* op = grpc_make_transport_op(nullptr);
259 grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_idle"),
260 GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR);
261 grpc_channel_element* elem =
262 grpc_channel_stack_element(chand->channel_stack, 0);
263 elem->filter->start_transport_op(elem, op);
266 static void max_idle_timer_cb(void* arg, grpc_error* error) {
267 channel_data* chand = static_cast<channel_data*>(arg);
268 if (error == GRPC_ERROR_NONE) {
269 bool try_again = true;
271 gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state);
272 switch (idle_state) {
273 case MAX_IDLE_STATE_TIMER_SET:
274 close_max_idle_channel(chand);
275 /* This MAX_IDLE_STATE_INIT is a final state, we don't have to check
276 * if idle_state has been changed */
277 gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_INIT);
280 case MAX_IDLE_STATE_SEEN_EXIT_IDLE:
281 if (gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE,
282 MAX_IDLE_STATE_INIT)) {
286 case MAX_IDLE_STATE_SEEN_ENTER_IDLE:
287 GRPC_CHANNEL_STACK_REF(chand->channel_stack,
288 "max_age max_idle_timer");
289 grpc_timer_init(&chand->max_idle_timer,
290 static_cast<grpc_millis>(gpr_atm_no_barrier_load(
291 &chand->last_enter_idle_time_millis)) +
292 chand->max_connection_idle,
293 &chand->max_idle_timer_cb);
294 /* idle_state may have already been set to
295 MAX_IDLE_STATE_SEEN_EXIT_IDLE by increase_call_count(), in this
296 case, we don't need to set it to MAX_IDLE_STATE_TIMER_SET */
297 gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_ENTER_IDLE,
298 MAX_IDLE_STATE_TIMER_SET);
307 GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_idle_timer");
310 static void close_max_age_channel(void* arg, grpc_error* error) {
311 channel_data* chand = static_cast<channel_data*>(arg);
312 gpr_mu_lock(&chand->max_age_timer_mu);
313 chand->max_age_timer_pending = false;
314 gpr_mu_unlock(&chand->max_age_timer_mu);
315 if (error == GRPC_ERROR_NONE) {
316 GRPC_CHANNEL_STACK_REF(chand->channel_stack,
317 "max_age start_max_age_grace_timer_after_goaway_op");
318 grpc_transport_op* op = grpc_make_transport_op(
319 &chand->start_max_age_grace_timer_after_goaway_op);
321 grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_age"),
322 GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR);
323 grpc_channel_element* elem =
324 grpc_channel_stack_element(chand->channel_stack, 0);
325 elem->filter->start_transport_op(elem, op);
326 } else if (error != GRPC_ERROR_CANCELLED) {
327 GRPC_LOG_IF_ERROR("close_max_age_channel", error);
329 GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_age_timer");
332 static void force_close_max_age_channel(void* arg, grpc_error* error) {
333 channel_data* chand = static_cast<channel_data*>(arg);
334 gpr_mu_lock(&chand->max_age_timer_mu);
335 chand->max_age_grace_timer_pending = false;
336 gpr_mu_unlock(&chand->max_age_timer_mu);
337 if (error == GRPC_ERROR_NONE) {
338 grpc_transport_op* op = grpc_make_transport_op(nullptr);
339 op->disconnect_with_error =
340 GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel reaches max age");
341 grpc_channel_element* elem =
342 grpc_channel_stack_element(chand->channel_stack, 0);
343 elem->filter->start_transport_op(elem, op);
344 } else if (error != GRPC_ERROR_CANCELLED) {
345 GRPC_LOG_IF_ERROR("force_close_max_age_channel", error);
347 GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_age_grace_timer");
350 static void channel_connectivity_changed(void* arg, grpc_error* error) {
351 channel_data* chand = static_cast<channel_data*>(arg);
352 if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) {
353 grpc_transport_op* op = grpc_make_transport_op(nullptr);
354 op->on_connectivity_state_change = &chand->channel_connectivity_changed;
355 op->connectivity_state = &chand->connectivity_state;
356 grpc_channel_next_op(grpc_channel_stack_element(chand->channel_stack, 0),
359 gpr_mu_lock(&chand->max_age_timer_mu);
360 if (chand->max_age_timer_pending) {
361 grpc_timer_cancel(&chand->max_age_timer);
362 chand->max_age_timer_pending = false;
364 if (chand->max_age_grace_timer_pending) {
365 grpc_timer_cancel(&chand->max_age_grace_timer);
366 chand->max_age_grace_timer_pending = false;
368 gpr_mu_unlock(&chand->max_age_timer_mu);
369 /* If there are no active calls, this increasement will cancel
370 max_idle_timer, and prevent max_idle_timer from being started in the
372 increase_call_count(chand);
373 if (gpr_atm_acq_load(&chand->idle_state) == MAX_IDLE_STATE_SEEN_EXIT_IDLE) {
374 grpc_timer_cancel(&chand->max_idle_timer);
379 /* A random jitter of +/-10% will be added to MAX_CONNECTION_AGE to spread out
380 connection storms. Note that the MAX_CONNECTION_AGE option without jitter
381 would not create connection storms by itself, but if there happened to be a
382 connection storm it could cause it to repeat at a fixed period. */
384 add_random_max_connection_age_jitter_and_convert_to_grpc_millis(int value) {
385 /* generate a random number between 1 - MAX_CONNECTION_AGE_JITTER and
386 1 + MAX_CONNECTION_AGE_JITTER */
387 double multiplier = rand() * MAX_CONNECTION_AGE_JITTER * 2.0 / RAND_MAX +
388 1.0 - MAX_CONNECTION_AGE_JITTER;
389 double result = multiplier * value;
390 /* INT_MAX - 0.5 converts the value to float, so that result will not be
391 cast to int implicitly before the comparison. */
392 return result > (static_cast<double>(GRPC_MILLIS_INF_FUTURE)) - 0.5
393 ? GRPC_MILLIS_INF_FUTURE
394 : static_cast<grpc_millis>(result);
397 /* Constructor for call_data. */
398 static grpc_error* init_call_elem(grpc_call_element* elem,
399 const grpc_call_element_args* args) {
400 channel_data* chand = static_cast<channel_data*>(elem->channel_data);
401 increase_call_count(chand);
402 return GRPC_ERROR_NONE;
405 /* Destructor for call_data. */
406 static void destroy_call_elem(grpc_call_element* elem,
407 const grpc_call_final_info* final_info,
408 grpc_closure* ignored) {
409 channel_data* chand = static_cast<channel_data*>(elem->channel_data);
410 decrease_call_count(chand);
413 /* Constructor for channel_data. */
414 static grpc_error* init_channel_elem(grpc_channel_element* elem,
415 grpc_channel_element_args* args) {
416 channel_data* chand = static_cast<channel_data*>(elem->channel_data);
417 gpr_mu_init(&chand->max_age_timer_mu);
418 chand->max_age_timer_pending = false;
419 chand->max_age_grace_timer_pending = false;
420 chand->channel_stack = args->channel_stack;
421 chand->max_connection_age =
422 add_random_max_connection_age_jitter_and_convert_to_grpc_millis(
423 DEFAULT_MAX_CONNECTION_AGE_MS);
424 chand->max_connection_age_grace =
425 DEFAULT_MAX_CONNECTION_AGE_GRACE_MS == INT_MAX
426 ? GRPC_MILLIS_INF_FUTURE
427 : DEFAULT_MAX_CONNECTION_AGE_GRACE_MS;
428 chand->max_connection_idle = DEFAULT_MAX_CONNECTION_IDLE_MS == INT_MAX
429 ? GRPC_MILLIS_INF_FUTURE
430 : DEFAULT_MAX_CONNECTION_IDLE_MS;
431 chand->idle_state = MAX_IDLE_STATE_INIT;
432 gpr_atm_no_barrier_store(&chand->last_enter_idle_time_millis, GPR_ATM_MIN);
433 for (size_t i = 0; i < args->channel_args->num_args; ++i) {
434 if (0 == strcmp(args->channel_args->args[i].key,
435 GRPC_ARG_MAX_CONNECTION_AGE_MS)) {
436 const int value = grpc_channel_arg_get_integer(
437 &args->channel_args->args[i], MAX_CONNECTION_AGE_INTEGER_OPTIONS);
438 chand->max_connection_age =
439 add_random_max_connection_age_jitter_and_convert_to_grpc_millis(
441 } else if (0 == strcmp(args->channel_args->args[i].key,
442 GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS)) {
443 const int value = grpc_channel_arg_get_integer(
444 &args->channel_args->args[i],
445 {DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, 0, INT_MAX});
446 chand->max_connection_age_grace =
447 value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
448 } else if (0 == strcmp(args->channel_args->args[i].key,
449 GRPC_ARG_MAX_CONNECTION_IDLE_MS)) {
450 const int value = grpc_channel_arg_get_integer(
451 &args->channel_args->args[i], MAX_CONNECTION_IDLE_INTEGER_OPTIONS);
452 chand->max_connection_idle =
453 value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
456 GRPC_CLOSURE_INIT(&chand->max_idle_timer_cb, max_idle_timer_cb, chand,
457 grpc_schedule_on_exec_ctx);
458 GRPC_CLOSURE_INIT(&chand->close_max_age_channel, close_max_age_channel, chand,
459 grpc_schedule_on_exec_ctx);
460 GRPC_CLOSURE_INIT(&chand->force_close_max_age_channel,
461 force_close_max_age_channel, chand,
462 grpc_schedule_on_exec_ctx);
463 GRPC_CLOSURE_INIT(&chand->start_max_idle_timer_after_init,
464 start_max_idle_timer_after_init, chand,
465 grpc_schedule_on_exec_ctx);
466 GRPC_CLOSURE_INIT(&chand->start_max_age_timer_after_init,
467 start_max_age_timer_after_init, chand,
468 grpc_schedule_on_exec_ctx);
469 GRPC_CLOSURE_INIT(&chand->start_max_age_grace_timer_after_goaway_op,
470 start_max_age_grace_timer_after_goaway_op, chand,
471 grpc_schedule_on_exec_ctx);
472 GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed,
473 channel_connectivity_changed, chand,
474 grpc_schedule_on_exec_ctx);
476 if (chand->max_connection_age != GRPC_MILLIS_INF_FUTURE) {
477 /* When the channel reaches its max age, we send down an op with
478 goaway_error set. However, we can't send down any ops until after the
479 channel stack is fully initialized. If we start the timer here, we have
480 no guarantee that the timer won't pop before channel stack initialization
481 is finished. To avoid that problem, we create a closure to start the
482 timer, and we schedule that closure to be run after call stack
483 initialization is done. */
484 GRPC_CHANNEL_STACK_REF(chand->channel_stack,
485 "max_age start_max_age_timer_after_init");
486 GRPC_CLOSURE_SCHED(&chand->start_max_age_timer_after_init, GRPC_ERROR_NONE);
489 /* Initialize the number of calls as 1, so that the max_idle_timer will not
490 start until start_max_idle_timer_after_init is invoked. */
491 gpr_atm_rel_store(&chand->call_count, 1);
492 if (chand->max_connection_idle != GRPC_MILLIS_INF_FUTURE) {
493 GRPC_CHANNEL_STACK_REF(chand->channel_stack,
494 "max_age start_max_idle_timer_after_init");
495 GRPC_CLOSURE_SCHED(&chand->start_max_idle_timer_after_init,
498 return GRPC_ERROR_NONE;
501 /* Destructor for channel_data. */
502 static void destroy_channel_elem(grpc_channel_element* elem) {
503 channel_data* chand = static_cast<channel_data*>(elem->channel_data);
504 gpr_mu_destroy(&chand->max_age_timer_mu);
507 const grpc_channel_filter grpc_max_age_filter = {
509 grpc_channel_next_op,
510 0, /* sizeof_call_data */
512 grpc_call_stack_ignore_set_pollset_or_pollset_set,
514 sizeof(channel_data),
516 destroy_channel_elem,
517 grpc_channel_next_get_info,
520 static bool maybe_add_max_age_filter(grpc_channel_stack_builder* builder,
522 const grpc_channel_args* channel_args =
523 grpc_channel_stack_builder_get_channel_arguments(builder);
525 grpc_channel_arg_get_integer(
526 grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_AGE_MS),
527 MAX_CONNECTION_AGE_INTEGER_OPTIONS) != INT_MAX ||
528 grpc_channel_arg_get_integer(
529 grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_IDLE_MS),
530 MAX_CONNECTION_IDLE_INTEGER_OPTIONS) != INT_MAX;
532 return grpc_channel_stack_builder_prepend_filter(
533 builder, &grpc_max_age_filter, nullptr, nullptr);
539 void grpc_max_age_filter_init(void) {
540 grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL,
541 GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
542 maybe_add_max_age_filter, nullptr);
545 void grpc_max_age_filter_shutdown(void) {}