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

com.gemstone.gemfire.internal.ScheduledThreadPoolExecutorWithKeepAlive 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.List;

import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * A ScheduledThreadPoolExecutor which allows threads to time out after the keep
 * alive time. With the normal ScheduledThreadPoolExecutor, there is no way to
 * configure it such that it only add threads as needed.
 * 
 * This executor is not very useful if you only want to have 1 thread. Use the
 * ScheduledThreadPoolExecutor in that case. This class with throw an exception
 * if you try to configure it with one thread.
 * 
 * @author dsmith
 * 
 */
@SuppressWarnings("synthetic-access")
public class ScheduledThreadPoolExecutorWithKeepAlive extends ThreadPoolExecutor implements ScheduledExecutorService {
  
  private final ScheduledThreadPoolExecutor timer;

  /**
   * @param corePoolSize
   * @param threadFactory
   */
  public ScheduledThreadPoolExecutorWithKeepAlive(int corePoolSize,
      long keepAlive, TimeUnit timeUnit, ThreadFactory threadFactory) {
    super(0, corePoolSize - 1, keepAlive,
        timeUnit, new SynchronousQueue(), threadFactory, new BlockCallerPolicy());
    timer = new ScheduledThreadPoolExecutor(1, threadFactory) {

      @Override
      protected void terminated() {
        super.terminated();
        ScheduledThreadPoolExecutorWithKeepAlive.super.shutdown();
      }
      
    };
  }
  
  @Override
  public void execute(Runnable command) {
    timer.execute(new HandOffTask(command));
  }

  @Override
  public Future submit(Callable task) {
    return schedule(task, 0, TimeUnit.NANOSECONDS);
  }

  @Override
  public Future submit(Runnable task, Object result) {
    return schedule(task, 0, TimeUnit.NANOSECONDS, result);
  }

  @Override
  public Future submit(Runnable task) {
    return schedule(task, 0, TimeUnit.NANOSECONDS);
  }

  public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) {
    DelegatingScheduledFuture future = new DelegatingScheduledFuture(callable);
    ScheduledFuture timerFuture = timer.schedule(new HandOffTask(future), delay, unit);
    future.setDelegate(timerFuture);
    return future;
  }

  public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) {
    return schedule(command, delay, unit, null);
  }

  private ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit, Object result) {
    DelegatingScheduledFuture future = new DelegatingScheduledFuture(command, result);
    ScheduledFuture timerFuture = timer.schedule(new HandOffTask(future), delay, unit);
    future.setDelegate(timerFuture);
    return future;
  }

  public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
    DelegatingScheduledFuture future = new DelegatingScheduledFuture(command, null, true);
    ScheduledFuture timerFuture = timer.scheduleAtFixedRate(new HandOffTask(future), initialDelay, period, unit);
    future.setDelegate(timerFuture);
    return future;
  }

  public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
    DelegatingScheduledFuture future =  new DelegatingScheduledFuture(command, null, true);
    ScheduledFuture timerFuture = timer.scheduleWithFixedDelay(new HandOffTask(future), initialDelay, delay, unit);
    future.setDelegate(timerFuture);
    return future;
  }

  @Override
  public void shutdown() {
    //note - the timer has a "hook" which will shutdown our
    //worker pool once the timer is shutdown.
    timer.shutdown();
  }

  /**
   * Shutdown the executor immediately, returning a list of tasks that haven't
   * been run. Like ScheduledThreadPoolExecutor, this returns a list of
   * RunnableScheduledFuture objects, instead of the actual tasks submitted.
   * However, these Future objects are even less useful than the ones returned
   * by ScheduledThreadPoolExecutor. In particular, they don't match the future
   * returned by the {{@link #submit(Runnable)} method, and the run method won't
   * do anything useful. This list should only be used as a count of the number
   * of tasks that didn't execute.
   * 
   * @see ScheduledThreadPoolExecutor#shutdownNow()
   */
  @Override
  public List shutdownNow() {
    List tasks = timer.shutdownNow();
    super.shutdownNow();
    return tasks;
  }

  @Override
  public boolean awaitTermination(long timeout, TimeUnit unit)
      throws InterruptedException {
    long start = System.nanoTime();
    if(!timer.awaitTermination(timeout, unit)) {
      return false;
    }
    long elapsed = System.nanoTime() - start;
    long remaining = unit.toNanos(timeout) - elapsed;
    if(remaining < 0) {
      return false;
    }
    return super.awaitTermination(remaining, TimeUnit.NANOSECONDS);
  }
  
  @Override
  public int getCorePoolSize() {
    return super.getCorePoolSize() + 1;
  }

  @Override
  public int getLargestPoolSize() {
    return super.getLargestPoolSize() + 1;
  }

  @Override
  public int getMaximumPoolSize() {
    return super.getMaximumPoolSize() + 1;
  }

  @Override
  public int getPoolSize() {
    return super.getPoolSize() + 1;
  }

  @Override
  public boolean isShutdown() {
    return timer.isShutdown();
  }

  @Override
  public boolean isTerminated() {
    return super.isTerminated() && timer.isTerminated();
  }
  
  //method that is in ScheduledThreadPoolExecutor that we should expose here
  public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean b) {
    timer.setContinueExistingPeriodicTasksAfterShutdownPolicy(b);
  }
  
  //method that is in ScheduledThreadPoolExecutor that we should expose here
  public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean b) {
    timer.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
  }
  
  /**
   * A Runnable which we put in the timer which
   * simply hands off the contain task for execution
   * in the thread pool when the timer fires.
   * @author dsmith
   *
   */
  private class HandOffTask implements Runnable {
    private final Runnable task;
    
    public HandOffTask(Runnable task) {
      this.task = task;
    }
    
    public void run() {
      try {
        ScheduledThreadPoolExecutorWithKeepAlive.super.execute(task);
      } catch(RejectedExecutionException e) {
        //do nothing, we'll only get this if we're shutting down.
      }
    }
  }

  
  /**
   * The future returned by the schedule* methods on this class. This future
   * will not return a value until the task has actually executed in the thread pool, 
   * but it allows us to cancel associated timer task. 
   * @author dsmith
   *
   */
  private static class DelegatingScheduledFuture extends FutureTask implements ScheduledFuture {
    
    private ScheduledFuture delegate;
    private final boolean periodic;
    
    public DelegatingScheduledFuture(Runnable runnable, V result) {
      this(runnable, result, false);
    }
    public DelegatingScheduledFuture(Callable callable) {
      this(callable, false);
    }

    public DelegatingScheduledFuture(Runnable runnable, V result, boolean periodic) {
      super(runnable, result);
      this.periodic = periodic;
    }
    
    public DelegatingScheduledFuture(Callable callable, boolean periodic) {
      super(callable);
      this.periodic = periodic;
    }
    
    @Override
    public void run() {
      if(periodic) {
        super.runAndReset();
      } else {
        super.run();
      }
    }
    public void setDelegate(ScheduledFuture future) {
      this.delegate = future;
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
      delegate.cancel(true);
      return super.cancel(mayInterruptIfRunning);
    }

    public long getDelay(TimeUnit unit) {
      return delegate.getDelay(unit);
    }

    public int compareTo(Delayed o) {
      return delegate.compareTo(o);
    }
    
    @Override
    public boolean equals(Object o) {
      return delegate.equals(o);
    }
    
    @Override
    public int hashCode() {
      return delegate.hashCode();
    }
  }

  /** A RejectedExecutionHandler which causes the caller to block until
   * there is space in the queue for the task.
   * @author dsmith
   */
  protected static class BlockCallerPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
      if (executor.isShutdown()) {
        throw new RejectedExecutionException("executor has been shutdown");
      } else {
        try {
          executor.getQueue().put(r);
        } catch (InterruptedException ie) {
          Thread.currentThread().interrupt();
          RejectedExecutionException e = new RejectedExecutionException("interrupted");
          e.initCause(ie);
          throw e;
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy