|  | @@ -50,6 +50,9 @@ static completed_thread *g_completed_threads;
 | 
	
		
			
				|  |  |  static bool g_kicked;
 | 
	
		
			
				|  |  |  // is there a thread waiting until the next timer should fire?
 | 
	
		
			
				|  |  |  static bool g_has_timed_waiter;
 | 
	
		
			
				|  |  | +// the deadline of the current timed waiter thread (only relevant if
 | 
	
		
			
				|  |  | +// g_has_timed_waiter is true)
 | 
	
		
			
				|  |  | +static gpr_timespec g_timed_waiter_deadline;
 | 
	
		
			
				|  |  |  // generation counter to track which thread is waiting for the next timer
 | 
	
		
			
				|  |  |  static uint64_t g_timed_waiter_generation;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -101,8 +104,7 @@ static void run_some_timers(grpc_exec_ctx *exec_ctx) {
 | 
	
		
			
				|  |  |      start_timer_thread_and_unlock();
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  |      // if there's no thread waiting with a timeout, kick an existing
 | 
	
		
			
				|  |  | -    // waiter
 | 
	
		
			
				|  |  | -    // so that the next deadline is not missed
 | 
	
		
			
				|  |  | +    // waiter so that the next deadline is not missed
 | 
	
		
			
				|  |  |      if (!g_has_timed_waiter) {
 | 
	
		
			
				|  |  |        if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
 | 
	
		
			
				|  |  |          gpr_log(GPR_DEBUG, "kick untimed waiter");
 | 
	
	
		
			
				|  | @@ -132,44 +134,79 @@ static bool wait_until(gpr_timespec next) {
 | 
	
		
			
				|  |  |      gpr_mu_unlock(&g_mu);
 | 
	
		
			
				|  |  |      return false;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  // if there's no timed waiter, we should become one: that waiter waits
 | 
	
		
			
				|  |  | -  // only until the next timer should expire
 | 
	
		
			
				|  |  | -  // all other timers wait forever
 | 
	
		
			
				|  |  | -  uint64_t my_timed_waiter_generation = g_timed_waiter_generation - 1;
 | 
	
		
			
				|  |  | -  if (!g_has_timed_waiter && gpr_time_cmp(next, inf_future) != 0) {
 | 
	
		
			
				|  |  | -    g_has_timed_waiter = true;
 | 
	
		
			
				|  |  | -    // we use a generation counter to track the timed waiter so we can
 | 
	
		
			
				|  |  | -    // cancel an existing one quickly (and when it actually times out it'll
 | 
	
		
			
				|  |  | -    // figure stuff out instead of incurring a wakeup)
 | 
	
		
			
				|  |  | -    my_timed_waiter_generation = ++g_timed_waiter_generation;
 | 
	
		
			
				|  |  | -    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
 | 
	
		
			
				|  |  | -      gpr_timespec wait_time = gpr_time_sub(next, gpr_now(GPR_CLOCK_MONOTONIC));
 | 
	
		
			
				|  |  | -      gpr_log(GPR_DEBUG, "sleep for a %" PRId64 ".%09d seconds",
 | 
	
		
			
				|  |  | -              wait_time.tv_sec, wait_time.tv_nsec);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // If g_kicked is true at this point, it means there was a kick from the timer
 | 
	
		
			
				|  |  | +  // system that the timer-manager threads here missed. We cannot trust 'next'
 | 
	
		
			
				|  |  | +  // here any longer (since there might be an earlier deadline). So if g_kicked
 | 
	
		
			
				|  |  | +  // is true at this point, we should quickly exit this and get the next
 | 
	
		
			
				|  |  | +  // deadline from the timer system
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (!g_kicked) {
 | 
	
		
			
				|  |  | +    // if there's no timed waiter, we should become one: that waiter waits
 | 
	
		
			
				|  |  | +    // only until the next timer should expire. All other timers wait forever
 | 
	
		
			
				|  |  | +    //
 | 
	
		
			
				|  |  | +    // 'g_timed_waiter_generation' is a global generation counter. The idea here
 | 
	
		
			
				|  |  | +    // is that the thread becoming a timed-waiter increments and stores this
 | 
	
		
			
				|  |  | +    // global counter locally in 'my_timed_waiter_generation' before going to
 | 
	
		
			
				|  |  | +    // sleep. After waking up, if my_timed_waiter_generation ==
 | 
	
		
			
				|  |  | +    // g_timed_waiter_generation, it can be sure that it was the timed_waiter
 | 
	
		
			
				|  |  | +    // thread (and that no other thread took over while this was asleep)
 | 
	
		
			
				|  |  | +    //
 | 
	
		
			
				|  |  | +    // Initialize my_timed_waiter_generation to some value that is NOT equal to
 | 
	
		
			
				|  |  | +    // g_timed_waiter_generation
 | 
	
		
			
				|  |  | +    uint64_t my_timed_waiter_generation = g_timed_waiter_generation - 1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* If there's no timed waiter, we should become one: that waiter waits only
 | 
	
		
			
				|  |  | +       until the next timer should expire. All other timer threads wait forever
 | 
	
		
			
				|  |  | +       unless their 'next' is earlier than the current timed-waiter's deadline
 | 
	
		
			
				|  |  | +       (in which case the thread with earlier 'next' takes over as the new timed
 | 
	
		
			
				|  |  | +       waiter) */
 | 
	
		
			
				|  |  | +    if (gpr_time_cmp(next, inf_future) != 0) {
 | 
	
		
			
				|  |  | +      if (!g_has_timed_waiter ||
 | 
	
		
			
				|  |  | +          (gpr_time_cmp(next, g_timed_waiter_deadline) < 0)) {
 | 
	
		
			
				|  |  | +        my_timed_waiter_generation = ++g_timed_waiter_generation;
 | 
	
		
			
				|  |  | +        g_has_timed_waiter = true;
 | 
	
		
			
				|  |  | +        g_timed_waiter_deadline = next;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
 | 
	
		
			
				|  |  | +          gpr_timespec wait_time =
 | 
	
		
			
				|  |  | +              gpr_time_sub(next, gpr_now(GPR_CLOCK_MONOTONIC));
 | 
	
		
			
				|  |  | +          gpr_log(GPR_DEBUG, "sleep for a %" PRId64 ".%09d seconds",
 | 
	
		
			
				|  |  | +                  wait_time.tv_sec, wait_time.tv_nsec);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {  // g_timed_waiter == true && next >= g_timed_waiter_deadline
 | 
	
		
			
				|  |  | +        next = inf_future;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    next = inf_future;
 | 
	
		
			
				|  |  | -    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (GRPC_TRACER_ON(grpc_timer_check_trace) &&
 | 
	
		
			
				|  |  | +        gpr_time_cmp(next, inf_future) == 0) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_DEBUG, "sleep until kicked");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    gpr_cv_wait(&g_cv_wait, &g_mu, next);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
 | 
	
		
			
				|  |  | +      gpr_log(GPR_DEBUG, "wait ended: was_timed:%d kicked:%d",
 | 
	
		
			
				|  |  | +              my_timed_waiter_generation == g_timed_waiter_generation,
 | 
	
		
			
				|  |  | +              g_kicked);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // if this was the timed waiter, then we need to check timers, and flag
 | 
	
		
			
				|  |  | +    // that there's now no timed waiter... we'll look for a replacement if
 | 
	
		
			
				|  |  | +    // there's work to do after checking timers (code above)
 | 
	
		
			
				|  |  | +    if (my_timed_waiter_generation == g_timed_waiter_generation) {
 | 
	
		
			
				|  |  | +      g_has_timed_waiter = false;
 | 
	
		
			
				|  |  | +      g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  gpr_cv_wait(&g_cv_wait, &g_mu, next);
 | 
	
		
			
				|  |  | -  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
 | 
	
		
			
				|  |  | -    gpr_log(GPR_DEBUG, "wait ended: was_timed:%d kicked:%d",
 | 
	
		
			
				|  |  | -            my_timed_waiter_generation == g_timed_waiter_generation, g_kicked);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  // if this was the timed waiter, then we need to check timers, and flag
 | 
	
		
			
				|  |  | -  // that there's now no timed waiter... we'll look for a replacement if
 | 
	
		
			
				|  |  | -  // there's work to do after checking timers (code above)
 | 
	
		
			
				|  |  | -  if (my_timed_waiter_generation == g_timed_waiter_generation) {
 | 
	
		
			
				|  |  | -    g_has_timed_waiter = false;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    // if this was a kick from the timer system, consume it (and don't stop
 | 
	
		
			
				|  |  |    // this thread yet)
 | 
	
		
			
				|  |  |    if (g_kicked) {
 | 
	
		
			
				|  |  |      grpc_timer_consume_kick();
 | 
	
		
			
				|  |  |      g_kicked = false;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&g_mu);
 | 
	
		
			
				|  |  |    return true;
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -257,6 +294,9 @@ void grpc_timer_manager_init(void) {
 | 
	
		
			
				|  |  |    g_waiter_count = 0;
 | 
	
		
			
				|  |  |    g_completed_threads = NULL;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  g_has_timed_waiter = false;
 | 
	
		
			
				|  |  | +  g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    start_threads();
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -302,6 +342,7 @@ void grpc_kick_poller(void) {
 | 
	
		
			
				|  |  |    gpr_mu_lock(&g_mu);
 | 
	
		
			
				|  |  |    g_kicked = true;
 | 
	
		
			
				|  |  |    g_has_timed_waiter = false;
 | 
	
		
			
				|  |  | +  g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
 | 
	
		
			
				|  |  |    ++g_timed_waiter_generation;
 | 
	
		
			
				|  |  |    gpr_cv_signal(&g_cv_wait);
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&g_mu);
 |