|  | @@ -38,6 +38,8 @@ extern grpc_core::TraceFlag grpc_timer_trace;
 | 
	
		
			
				|  |  |  extern grpc_core::TraceFlag grpc_timer_check_trace;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static int cb_called[MAX_CB][2];
 | 
	
		
			
				|  |  | +static const int64_t kMillisIn25Days = 2160000000;
 | 
	
		
			
				|  |  | +static const int64_t kHoursIn25Days = 600;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void cb(void* arg, grpc_error* error) {
 | 
	
		
			
				|  |  |    cb_called[(intptr_t)arg][error == GRPC_ERROR_NONE]++;
 | 
	
	
		
			
				|  | @@ -151,17 +153,112 @@ void destruction_test(void) {
 | 
	
		
			
				|  |  |    GPR_ASSERT(1 == cb_called[2][0]);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -int main(int argc, char** argv) {
 | 
	
		
			
				|  |  | -  grpc_test_init(argc, argv);
 | 
	
		
			
				|  |  | -  grpc_core::ExecCtx::GlobalInit();
 | 
	
		
			
				|  |  | +/* Cleans up a list with pending timers that simulate long-running-services.
 | 
	
		
			
				|  |  | +   This test does the following:
 | 
	
		
			
				|  |  | +    1) Simulates grpc server start time to 25 days in the past (completed in
 | 
	
		
			
				|  |  | +        `main` using TestOnlyGlobalInit())
 | 
	
		
			
				|  |  | +    2) Creates 4 timers - one with a deadline 25 days in the future, one just
 | 
	
		
			
				|  |  | +        3 milliseconds in future, one way out in the future, and one using the
 | 
	
		
			
				|  |  | +        grpc_timespec_to_millis_round_up function to compute a deadline of 25
 | 
	
		
			
				|  |  | +        days in the future
 | 
	
		
			
				|  |  | +    3) Simulates 4 milliseconds of elapsed time by changing `now` (cached at
 | 
	
		
			
				|  |  | +        step 1) to `now+4`
 | 
	
		
			
				|  |  | +    4) Shuts down the timer list
 | 
	
		
			
				|  |  | +   https://github.com/grpc/grpc/issues/15904 */
 | 
	
		
			
				|  |  | +void long_running_service_cleanup_test(void) {
 | 
	
		
			
				|  |  | +  grpc_timer timers[4];
 | 
	
		
			
				|  |  |    grpc_core::ExecCtx exec_ctx;
 | 
	
		
			
				|  |  | -  grpc_determine_iomgr_platform();
 | 
	
		
			
				|  |  | -  grpc_iomgr_platform_init();
 | 
	
		
			
				|  |  | -  gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG);
 | 
	
		
			
				|  |  | -  add_test();
 | 
	
		
			
				|  |  | -  destruction_test();
 | 
	
		
			
				|  |  | -  grpc_iomgr_platform_shutdown();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_log(GPR_INFO, "long_running_service_cleanup_test");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  grpc_millis now = grpc_core::ExecCtx::Get()->Now();
 | 
	
		
			
				|  |  | +  GPR_ASSERT(now >= kMillisIn25Days);
 | 
	
		
			
				|  |  | +  grpc_timer_list_init();
 | 
	
		
			
				|  |  | +  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_trace);
 | 
	
		
			
				|  |  | +  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_check_trace);
 | 
	
		
			
				|  |  | +  memset(cb_called, 0, sizeof(cb_called));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  grpc_timer_init(
 | 
	
		
			
				|  |  | +      &timers[0], now + kMillisIn25Days,
 | 
	
		
			
				|  |  | +      GRPC_CLOSURE_CREATE(cb, (void*)(intptr_t)0, grpc_schedule_on_exec_ctx));
 | 
	
		
			
				|  |  | +  grpc_timer_init(
 | 
	
		
			
				|  |  | +      &timers[1], now + 3,
 | 
	
		
			
				|  |  | +      GRPC_CLOSURE_CREATE(cb, (void*)(intptr_t)1, grpc_schedule_on_exec_ctx));
 | 
	
		
			
				|  |  | +  grpc_timer_init(
 | 
	
		
			
				|  |  | +      &timers[2], GRPC_MILLIS_INF_FUTURE - 1,
 | 
	
		
			
				|  |  | +      GRPC_CLOSURE_CREATE(cb, (void*)(intptr_t)2, grpc_schedule_on_exec_ctx));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_timespec deadline_spec = grpc_millis_to_timespec(
 | 
	
		
			
				|  |  | +      now + kMillisIn25Days, gpr_clock_type::GPR_CLOCK_MONOTONIC);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* grpc_timespec_to_millis_round_up is how users usually compute a millisecond
 | 
	
		
			
				|  |  | +    input value into grpc_timer_init, so we mimic that behavior here */
 | 
	
		
			
				|  |  | +  grpc_timer_init(
 | 
	
		
			
				|  |  | +      &timers[3], grpc_timespec_to_millis_round_up(deadline_spec),
 | 
	
		
			
				|  |  | +      GRPC_CLOSURE_CREATE(cb, (void*)(intptr_t)3, grpc_schedule_on_exec_ctx));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  grpc_core::ExecCtx::Get()->TestOnlySetNow(now + 4);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(grpc_timer_check(nullptr) == GRPC_TIMERS_FIRED);
 | 
	
		
			
				|  |  | +  grpc_core::ExecCtx::Get()->Flush();
 | 
	
		
			
				|  |  | +  GPR_ASSERT(0 == cb_called[0][0]);  // Timer 0 not called
 | 
	
		
			
				|  |  | +  GPR_ASSERT(0 == cb_called[0][1]);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(0 == cb_called[1][0]);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(1 == cb_called[1][1]);  // Timer 1 fired
 | 
	
		
			
				|  |  | +  GPR_ASSERT(0 == cb_called[2][0]);  // Timer 2 not called
 | 
	
		
			
				|  |  | +  GPR_ASSERT(0 == cb_called[2][1]);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(0 == cb_called[3][0]);  // Timer 3 not called
 | 
	
		
			
				|  |  | +  GPR_ASSERT(0 == cb_called[3][1]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  grpc_timer_list_shutdown();
 | 
	
		
			
				|  |  | +  grpc_core::ExecCtx::Get()->Flush();
 | 
	
		
			
				|  |  | +  /* Timers 0, 2, and 3 were fired with an error during cleanup */
 | 
	
		
			
				|  |  | +  GPR_ASSERT(1 == cb_called[0][0]);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(0 == cb_called[1][0]);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(1 == cb_called[2][0]);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(1 == cb_called[3][0]);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +int main(int argc, char** argv) {
 | 
	
		
			
				|  |  | +  /* Tests with default g_start_time */
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    grpc_test_init(argc, argv);
 | 
	
		
			
				|  |  | +    grpc_core::ExecCtx::GlobalInit();
 | 
	
		
			
				|  |  | +    grpc_core::ExecCtx exec_ctx;
 | 
	
		
			
				|  |  | +    grpc_determine_iomgr_platform();
 | 
	
		
			
				|  |  | +    grpc_iomgr_platform_init();
 | 
	
		
			
				|  |  | +    gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG);
 | 
	
		
			
				|  |  | +    add_test();
 | 
	
		
			
				|  |  | +    destruction_test();
 | 
	
		
			
				|  |  | +    grpc_iomgr_platform_shutdown();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    grpc_core::ExecCtx::GlobalShutdown();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Begin long running service tests */
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    grpc_test_init(argc, argv);
 | 
	
		
			
				|  |  | +    /* Set g_start_time back 25 days. */
 | 
	
		
			
				|  |  | +    /* We set g_start_time here in case there are any initialization
 | 
	
		
			
				|  |  | +        dependencies that use g_start_time. */
 | 
	
		
			
				|  |  | +    gpr_timespec new_start =
 | 
	
		
			
				|  |  | +        gpr_time_sub(gpr_now(gpr_clock_type::GPR_CLOCK_MONOTONIC),
 | 
	
		
			
				|  |  | +                     gpr_time_from_hours(kHoursIn25Days,
 | 
	
		
			
				|  |  | +                                         gpr_clock_type::GPR_CLOCK_MONOTONIC));
 | 
	
		
			
				|  |  | +    grpc_core::ExecCtx::TestOnlyGlobalInit(new_start);
 | 
	
		
			
				|  |  | +    grpc_core::ExecCtx exec_ctx;
 | 
	
		
			
				|  |  | +    grpc_determine_iomgr_platform();
 | 
	
		
			
				|  |  | +    grpc_iomgr_platform_init();
 | 
	
		
			
				|  |  | +    gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG);
 | 
	
		
			
				|  |  | +#ifndef GPR_WINDOWS
 | 
	
		
			
				|  |  | +    /* Skip this test on Windows until we figure out why it fails */
 | 
	
		
			
				|  |  | +    /* https://github.com/grpc/grpc/issues/16417 */
 | 
	
		
			
				|  |  | +    long_running_service_cleanup_test();
 | 
	
		
			
				|  |  | +#endif  // GPR_WINDOWS
 | 
	
		
			
				|  |  | +    add_test();
 | 
	
		
			
				|  |  | +    destruction_test();
 | 
	
		
			
				|  |  | +    grpc_iomgr_platform_shutdown();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  grpc_core::ExecCtx::GlobalShutdown();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    return 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 |