com.gemstone.gemfire.internal.ScheduledThreadPoolExecutorWithKeepAlive Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gemfire-core Show documentation
Show all versions of gemfire-core Show documentation
SnappyData store based off Pivotal GemFireXD
/*
* 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;
}
}
}
}
}