All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.OneTaskOnlyExecutor Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.internal;

import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * A decorator for a ScheduledExecutorService which tries to make sure that
 * there is only one task in the queue for the executor service that has been
 * submitted through this decorator.
 * 
 * This class is useful if you have a task that you want to make sure runs at
 * least once after an event, but if the event happens repeatedly before the
 * task runs, the task should still only run once.
 * 
 * In many cases it might make sense to have multiple decorators, or to submit
 * one kind of task through the decorator and other tasks directly through the
 * executor.
 * 
 * For example, a task that recovers redundancy for all buckets of a PR when a
 * member crashes only needs to be run once, no matter how many members crash
 * before the task starts. But after the task has started, we want to make sure
 * we schedule another execution for the next crash.
 * 
 * If the a new task is scheduled to run sooner than the task that is currently in the queue,
 * the currently queued task will be canceled and the new task will be submitted to the queue
 * with the new time. 
 * 
 * @author dsmith
 * @since 6.0
 */
@SuppressWarnings("synthetic-access")
public class OneTaskOnlyExecutor {
  
  private final ScheduledExecutorService ex;
  private ScheduledFuture future = null;
  private ConflatedTaskListener listener;

  public OneTaskOnlyExecutor(ScheduledExecutorService ex) {
    this(ex, new ConflatedTaskListenerAdapter());
  }
  
  public OneTaskOnlyExecutor(ScheduledExecutorService ex, ConflatedTaskListener listener) {
    this.ex = ex;
    this.listener = listener;
  }

  /**
   * Schedule an execution of a task. This will either add the task to the
   * execution service, or if a task has already been scheduled through this
   * decorator and is still pending execution it will return the future
   * associated with the previously scheduled task.
   * 
   * @param runnable
   *          a runnable to execution
   * @param delay
   *          the time to delay before execution
   * @param unit
   *          the time unit
   * @return The future associated with this task, or with a previously
   *         scheduled task if that task has not yet been run.
   * @see ScheduledExecutorService#schedule(Runnable, long, TimeUnit)
   */
  public ScheduledFuture schedule(Runnable runnable, long delay, TimeUnit unit) {
    synchronized(this) {
      if (future == null || future.isCancelled() || future.isDone()
          || future.getDelay(unit) > delay) {
        if(future != null && !future.isDone()) {
          future.cancel(false);
          listener.taskDropped();
        }
        future = ex.schedule(new DelegatingRunnable(runnable), delay, unit);
      } else {
        listener.taskDropped();
      }
    }
    return future;
  }
  
  /**
   * Schedule an execution of a task. This will either add the task to the
   * execution service, or if a task has already been scheduled through this
   * decorator and is still pending execution it will return the future
   * associated with the previously scheduled task.
   * 
   * @param callable
   *          a callable to execute
   * @param delay
   *          the time to delay before execution
   * @param unit
   *          the time unit
   * @return The future associated with this task, or with a previously
   *         scheduled task if that task has not yet been run.
   * @see ScheduledExecutorService#schedule(Runnable, long, TimeUnit)
   */
  public  ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) {
    synchronized(this) {
      if(future == null || future.isCancelled() || future.isDone() || future.getDelay(unit) > delay) {
        if(future != null && !future.isDone()) {
          future.cancel(false);
          listener.taskDropped();
        }
          future = ex.schedule(new DelegatingCallable(callable), delay, unit);
      } else {
        listener.taskDropped();
      }
    }
    return future;
  }

  /**
   * Removes the canceled tasks from the executor queue.
   */
  public void purge() {
    ((ScheduledThreadPoolExecutor)ex).purge();
  }

  private class DelegatingRunnable implements Runnable {
    private final Runnable runnable;
    
    public DelegatingRunnable(Runnable runnable) {
      this.runnable = runnable;
    }

    public void run() {
      synchronized(OneTaskOnlyExecutor.this) {
        future = null;
      }
      runnable.run();
    }
  }
  
  private class DelegatingCallable implements Callable {
    private final Callable callable;
    
    public DelegatingCallable(Callable callable) {
      this.callable = callable;
    }

    public T call() throws Exception {
      synchronized(OneTaskOnlyExecutor.this) {
        future = null;
      }
      return callable.call();
    }
  }
  
  public static interface ConflatedTaskListener {
    void taskDropped();
  }
  
  public static class ConflatedTaskListenerAdapter implements ConflatedTaskListener {
    public void taskDropped() {
      
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy