|  | @@ -37,6 +37,7 @@
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "src/core/iomgr/iomgr_internal.h"
 | 
	
		
			
				|  |  |  #include "src/core/iomgr/alarm_internal.h"
 | 
	
		
			
				|  |  | +#include "src/core/support/string.h"
 | 
	
		
			
				|  |  |  #include <grpc/support/alloc.h>
 | 
	
		
			
				|  |  |  #include <grpc/support/log.h>
 | 
	
		
			
				|  |  |  #include <grpc/support/thd.h>
 | 
	
	
		
			
				|  | @@ -54,8 +55,8 @@ static gpr_cv g_rcv;
 | 
	
		
			
				|  |  |  static delayed_callback *g_cbs_head = NULL;
 | 
	
		
			
				|  |  |  static delayed_callback *g_cbs_tail = NULL;
 | 
	
		
			
				|  |  |  static int g_shutdown;
 | 
	
		
			
				|  |  | -static int g_refs;
 | 
	
		
			
				|  |  |  static gpr_event g_background_callback_executor_done;
 | 
	
		
			
				|  |  | +static grpc_iomgr_object g_root_object;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Execute followup callbacks continuously.
 | 
	
		
			
				|  |  |     Other threads may check in and help during pollset_work() */
 | 
	
	
		
			
				|  | @@ -96,40 +97,60 @@ void grpc_iomgr_init(void) {
 | 
	
		
			
				|  |  |    gpr_mu_init(&g_mu);
 | 
	
		
			
				|  |  |    gpr_cv_init(&g_rcv);
 | 
	
		
			
				|  |  |    grpc_alarm_list_init(gpr_now());
 | 
	
		
			
				|  |  | -  g_refs = 0;
 | 
	
		
			
				|  |  | +  g_root_object.next = g_root_object.prev = &g_root_object;
 | 
	
		
			
				|  |  | +  g_root_object.name = "root";
 | 
	
		
			
				|  |  |    grpc_iomgr_platform_init();
 | 
	
		
			
				|  |  |    gpr_event_init(&g_background_callback_executor_done);
 | 
	
		
			
				|  |  |    gpr_thd_new(&id, background_callback_executor, NULL, NULL);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static size_t count_objects(void) {
 | 
	
		
			
				|  |  | +  grpc_iomgr_object *obj;
 | 
	
		
			
				|  |  | +  size_t n = 0;
 | 
	
		
			
				|  |  | +  for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) {
 | 
	
		
			
				|  |  | +    n++;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return n;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void grpc_iomgr_shutdown(void) {
 | 
	
		
			
				|  |  |    delayed_callback *cb;
 | 
	
		
			
				|  |  | +  grpc_iomgr_object *obj;
 | 
	
		
			
				|  |  |    gpr_timespec shutdown_deadline =
 | 
	
		
			
				|  |  |        gpr_time_add(gpr_now(), gpr_time_from_seconds(10));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  grpc_alarm_list_shutdown();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    gpr_mu_lock(&g_mu);
 | 
	
		
			
				|  |  |    g_shutdown = 1;
 | 
	
		
			
				|  |  | -  while (g_cbs_head != NULL || g_refs > 0) {
 | 
	
		
			
				|  |  | -    if (g_cbs_head != NULL && g_refs > 0) {
 | 
	
		
			
				|  |  | -      gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed and executing final callbacks", g_refs);
 | 
	
		
			
				|  |  | +  while (g_cbs_head != NULL || g_root_object.next != &g_root_object) {
 | 
	
		
			
				|  |  | +    if (g_cbs_head != NULL && g_root_object.next != &g_root_object) {
 | 
	
		
			
				|  |  | +      gpr_log(GPR_DEBUG,
 | 
	
		
			
				|  |  | +              "Waiting for %d iomgr objects to be destroyed and executing "
 | 
	
		
			
				|  |  | +              "final callbacks",
 | 
	
		
			
				|  |  | +              count_objects());
 | 
	
		
			
				|  |  |      } else if (g_cbs_head != NULL) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_DEBUG, "Executing final iomgr callbacks");
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  | -      gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed", g_refs);
 | 
	
		
			
				|  |  | +      gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed",
 | 
	
		
			
				|  |  | +              count_objects());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    while (g_cbs_head) {
 | 
	
		
			
				|  |  | -      cb = g_cbs_head;
 | 
	
		
			
				|  |  | -      g_cbs_head = cb->next;
 | 
	
		
			
				|  |  | -      if (!g_cbs_head) g_cbs_tail = NULL;
 | 
	
		
			
				|  |  | -      gpr_mu_unlock(&g_mu);
 | 
	
		
			
				|  |  | +    if (g_cbs_head) {
 | 
	
		
			
				|  |  | +      do {
 | 
	
		
			
				|  |  | +        cb = g_cbs_head;
 | 
	
		
			
				|  |  | +        g_cbs_head = cb->next;
 | 
	
		
			
				|  |  | +        if (!g_cbs_head) g_cbs_tail = NULL;
 | 
	
		
			
				|  |  | +        gpr_mu_unlock(&g_mu);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      cb->cb(cb->cb_arg, 0);
 | 
	
		
			
				|  |  | -      gpr_free(cb);
 | 
	
		
			
				|  |  | -      gpr_mu_lock(&g_mu);
 | 
	
		
			
				|  |  | +        cb->cb(cb->cb_arg, 0);
 | 
	
		
			
				|  |  | +        gpr_free(cb);
 | 
	
		
			
				|  |  | +        gpr_mu_lock(&g_mu);
 | 
	
		
			
				|  |  | +      } while (g_cbs_head);
 | 
	
		
			
				|  |  | +      continue;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    if (g_refs) {
 | 
	
		
			
				|  |  | +    if (grpc_alarm_check(&g_mu, gpr_inf_future, NULL)) {
 | 
	
		
			
				|  |  | +      gpr_log(GPR_DEBUG, "got late alarm");
 | 
	
		
			
				|  |  | +      continue;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (g_root_object.next != &g_root_object) {
 | 
	
		
			
				|  |  |        int timeout = 0;
 | 
	
		
			
				|  |  |        gpr_timespec short_deadline = gpr_time_add(gpr_now(),
 | 
	
		
			
				|  |  |                                                   gpr_time_from_millis(100));
 | 
	
	
		
			
				|  | @@ -143,7 +164,10 @@ void grpc_iomgr_shutdown(void) {
 | 
	
		
			
				|  |  |          gpr_log(GPR_DEBUG,
 | 
	
		
			
				|  |  |                  "Failed to free %d iomgr objects before shutdown deadline: "
 | 
	
		
			
				|  |  |                  "memory leaks are likely",
 | 
	
		
			
				|  |  | -                g_refs);
 | 
	
		
			
				|  |  | +                count_objects());
 | 
	
		
			
				|  |  | +        for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) {
 | 
	
		
			
				|  |  | +          gpr_log(GPR_DEBUG, "LEAKED OBJECT: %s", obj->name);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -153,22 +177,28 @@ void grpc_iomgr_shutdown(void) {
 | 
	
		
			
				|  |  |    grpc_kick_poller();
 | 
	
		
			
				|  |  |    gpr_event_wait(&g_background_callback_executor_done, gpr_inf_future);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  grpc_alarm_list_shutdown();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    grpc_iomgr_platform_shutdown();
 | 
	
		
			
				|  |  |    gpr_mu_destroy(&g_mu);
 | 
	
		
			
				|  |  |    gpr_cv_destroy(&g_rcv);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_iomgr_ref(void) {
 | 
	
		
			
				|  |  | +void grpc_iomgr_register_object(grpc_iomgr_object *obj, const char *name) {
 | 
	
		
			
				|  |  |    gpr_mu_lock(&g_mu);
 | 
	
		
			
				|  |  | -  ++g_refs;
 | 
	
		
			
				|  |  | +  obj->name = gpr_strdup(name);
 | 
	
		
			
				|  |  | +  obj->next = &g_root_object;
 | 
	
		
			
				|  |  | +  obj->prev = obj->next->prev;
 | 
	
		
			
				|  |  | +  obj->next->prev = obj->prev->next = obj;
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&g_mu);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_iomgr_unref(void) {
 | 
	
		
			
				|  |  | +void grpc_iomgr_unregister_object(grpc_iomgr_object *obj) {
 | 
	
		
			
				|  |  |    gpr_mu_lock(&g_mu);
 | 
	
		
			
				|  |  | -  if (0 == --g_refs) {
 | 
	
		
			
				|  |  | -    gpr_cv_signal(&g_rcv);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  obj->next->prev = obj->prev;
 | 
	
		
			
				|  |  | +  obj->prev->next = obj->next;
 | 
	
		
			
				|  |  | +  gpr_free(obj->name);
 | 
	
		
			
				|  |  | +  gpr_cv_signal(&g_rcv);
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&g_mu);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 |