/**
  * Adds a cluster state listener that will timeout after the provided timeout, and is executed
  * after the clusterstate has been successfully applied ie. is in state {@link
  * org.elasticsearch.cluster.ClusterState.ClusterStateStatus#APPLIED} NOTE: a {@code null} timeout
  * means that the listener will never be removed automatically
  */
 public void add(@Nullable final TimeValue timeout, final TimeoutClusterStateListener listener) {
   if (lifecycle.stoppedOrClosed()) {
     listener.onClose();
     return;
   }
   // call the post added notification on the same event thread
   try {
     updateTasksExecutor.execute(
         new SourcePrioritizedRunnable(Priority.HIGH, "_add_listener_") {
           @Override
           public void run() {
             if (timeout != null) {
               NotifyTimeout notifyTimeout = new NotifyTimeout(listener, timeout);
               notifyTimeout.future =
                   threadPool.schedule(timeout, ThreadPool.Names.GENERIC, notifyTimeout);
               onGoingTimeouts.add(notifyTimeout);
             }
             postAppliedListeners.add(listener);
             listener.postAdded();
           }
         });
   } catch (EsRejectedExecutionException e) {
     if (lifecycle.stoppedOrClosed()) {
       listener.onClose();
     } else {
       throw e;
     }
   }
 }
 @Override
 public void run(Timeout timeout) throws Exception {
   if (timeout.isCancelled()) {
     return;
   }
   if (lifecycle.stoppedOrClosed()) {
     listener.onClose();
   } else {
     listener.onTimeout(this.timeout);
   }
   // note, we rely on the listener to remove itself in case of timeout if needed
 }
 @Override
 public void run() {
   if (future != null && future.isCancelled()) {
     return;
   }
   if (lifecycle.stoppedOrClosed()) {
     listener.onClose();
   } else {
     listener.onTimeout(this.timeout);
   }
   // note, we rely on the listener to remove itself in case of timeout if needed
 }
 public void add(TimeValue timeout, final TimeoutClusterStateListener listener) {
   if (lifecycle.stoppedOrClosed()) {
     listener.onClose();
     return;
   }
   NotifyTimeout notifyTimeout = new NotifyTimeout(listener, timeout);
   Timeout timerTimeout =
       timerService.newTimeout(notifyTimeout, timeout, TimerService.ExecutionType.THREADED);
   onGoingTimeouts.add(new Tuple<Timeout, NotifyTimeout>(timerTimeout, notifyTimeout));
   clusterStateListeners.add(listener);
   // call the post added notification on the same event thread
   updateTasksExecutor.execute(
       new Runnable() {
         @Override
         public void run() {
           listener.postAdded();
         }
       });
 }