|  | @@ -79,10 +79,15 @@ struct grpc_udp_listener {
 | 
	
		
			
				|  |  |    grpc_resolved_address addr;
 | 
	
		
			
				|  |  |    grpc_closure read_closure;
 | 
	
		
			
				|  |  |    grpc_closure write_closure;
 | 
	
		
			
				|  |  | +  // To be called when corresponding QuicGrpcServer closes all active
 | 
	
		
			
				|  |  | +  // connections.
 | 
	
		
			
				|  |  | +  grpc_closure orphan_fd_closure;
 | 
	
		
			
				|  |  |    grpc_closure destroyed_closure;
 | 
	
		
			
				|  |  |    grpc_udp_server_read_cb read_cb;
 | 
	
		
			
				|  |  |    grpc_udp_server_write_cb write_cb;
 | 
	
		
			
				|  |  |    grpc_udp_server_orphan_cb orphan_cb;
 | 
	
		
			
				|  |  | +  // True if orphan_cb is trigered.
 | 
	
		
			
				|  |  | +  bool orphan_notified;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    struct grpc_udp_listener *next;
 | 
	
		
			
				|  |  |  };
 | 
	
	
		
			
				|  | @@ -146,6 +151,14 @@ grpc_udp_server *grpc_udp_server_create(const grpc_channel_args *args) {
 | 
	
		
			
				|  |  |    return s;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void shutdown_fd(grpc_exec_ctx *exec_ctx, void *fd, grpc_error *error) {
 | 
	
		
			
				|  |  | +  grpc_fd_shutdown(exec_ctx, (grpc_fd *)fd, GRPC_ERROR_REF(error));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void dummy_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
 | 
	
		
			
				|  |  | +  // No-op.
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) {
 | 
	
		
			
				|  |  |    if (s->shutdown_complete != NULL) {
 | 
	
		
			
				|  |  |      grpc_closure_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE);
 | 
	
	
		
			
				|  | @@ -195,12 +208,16 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        grpc_closure_init(&sp->destroyed_closure, destroyed_port, s,
 | 
	
		
			
				|  |  |                          grpc_schedule_on_exec_ctx);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      /* Call the orphan_cb to signal that the FD is about to be closed and
 | 
	
		
			
				|  |  | -       * should no longer be used. */
 | 
	
		
			
				|  |  | -      GPR_ASSERT(sp->orphan_cb);
 | 
	
		
			
				|  |  | -      sp->orphan_cb(exec_ctx, sp->emfd, sp->server->user_data);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +      if (!sp->orphan_notified) {
 | 
	
		
			
				|  |  | +        /* Call the orphan_cb to signal that the FD is about to be closed and
 | 
	
		
			
				|  |  | +         * should no longer be used. Because at this point, all listening ports
 | 
	
		
			
				|  |  | +         * have been shutdown already, no need to shutdown again.*/
 | 
	
		
			
				|  |  | +        grpc_closure_init(&sp->orphan_fd_closure, dummy_cb, sp->emfd,
 | 
	
		
			
				|  |  | +                          grpc_schedule_on_exec_ctx);
 | 
	
		
			
				|  |  | +        GPR_ASSERT(sp->orphan_cb);
 | 
	
		
			
				|  |  | +        sp->orphan_cb(exec_ctx, sp->emfd, &sp->orphan_fd_closure,
 | 
	
		
			
				|  |  | +                      sp->server->user_data);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |        grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL,
 | 
	
		
			
				|  |  |                       "udp_listener_shutdown");
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -225,9 +242,11 @@ void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *s,
 | 
	
		
			
				|  |  |    if (s->active_ports) {
 | 
	
		
			
				|  |  |      for (sp = s->head; sp; sp = sp->next) {
 | 
	
		
			
				|  |  |        GPR_ASSERT(sp->orphan_cb);
 | 
	
		
			
				|  |  | -      sp->orphan_cb(exec_ctx, sp->emfd, sp->server->user_data);
 | 
	
		
			
				|  |  | -      grpc_fd_shutdown(exec_ctx, sp->emfd, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -                                               "Server destroyed"));
 | 
	
		
			
				|  |  | +      grpc_closure_init(&sp->orphan_fd_closure, shutdown_fd, sp->emfd,
 | 
	
		
			
				|  |  | +                        grpc_schedule_on_exec_ctx);
 | 
	
		
			
				|  |  | +      sp->orphan_cb(exec_ctx, sp->emfd, &sp->orphan_fd_closure,
 | 
	
		
			
				|  |  | +                    sp->server->user_data);
 | 
	
		
			
				|  |  | +      sp->orphan_notified = true;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      gpr_mu_unlock(&s->mu);
 | 
	
		
			
				|  |  |    } else {
 | 
	
	
		
			
				|  | @@ -391,6 +410,7 @@ static int add_socket_to_server(grpc_udp_server *s, int fd,
 | 
	
		
			
				|  |  |      sp->read_cb = read_cb;
 | 
	
		
			
				|  |  |      sp->write_cb = write_cb;
 | 
	
		
			
				|  |  |      sp->orphan_cb = orphan_cb;
 | 
	
		
			
				|  |  | +    sp->orphan_notified = false;
 | 
	
		
			
				|  |  |      GPR_ASSERT(sp->emfd);
 | 
	
		
			
				|  |  |      gpr_mu_unlock(&s->mu);
 | 
	
		
			
				|  |  |      gpr_free(name);
 |