remove Windows support, revert new guava to small version
This commit is contained in:
parent
fd84384f10
commit
920405d8c5
264 changed files with 22715 additions and 377 deletions
392
java/src/game/future/AbstractFuture.java
Normal file
392
java/src/game/future/AbstractFuture.java
Normal file
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
||||
|
||||
/**
|
||||
* An abstract implementation of the {@link ListenableFuture} interface. This
|
||||
* class is preferable to {@link java.util.concurrent.FutureTask} for two
|
||||
* reasons: It implements {@code ListenableFuture}, and it does not implement
|
||||
* {@code Runnable}. (If you want a {@code Runnable} implementation of {@code
|
||||
* ListenableFuture}, create a {@link ListenableFutureTask}, or submit your
|
||||
* tasks to a {@link ListeningExecutorService}.)
|
||||
*
|
||||
* <p>This class implements all methods in {@code ListenableFuture}.
|
||||
* Subclasses should provide a way to set the result of the computation through
|
||||
* the protected methods {@link #set(Object)} and
|
||||
* {@link #setException(Throwable)}. Subclasses may also override {@link
|
||||
* #interruptTask()}, which will be invoked automatically if a call to {@link
|
||||
* #cancel(boolean) cancel(true)} succeeds in canceling the future.
|
||||
*
|
||||
* <p>{@code AbstractFuture} uses an {@link AbstractQueuedSynchronizer} to deal
|
||||
* with concurrency issues and guarantee thread safety.
|
||||
*
|
||||
* <p>The state changing methods all return a boolean indicating success or
|
||||
* failure in changing the future's state. Valid states are running,
|
||||
* completed, failed, or cancelled.
|
||||
*
|
||||
* <p>This class uses an {@link ExecutionList} to guarantee that all registered
|
||||
* listeners will be executed, either when the future finishes or, for listeners
|
||||
* that are added after the future completes, immediately.
|
||||
* {@code Runnable}-{@code Executor} pairs are stored in the execution list but
|
||||
* are not necessarily executed in the order in which they were added. (If a
|
||||
* listener is added after the Future is complete, it will be executed
|
||||
* immediately, even if earlier listeners have not been executed. Additionally,
|
||||
* executors need not guarantee FIFO execution, or different listeners may run
|
||||
* in different executors.)
|
||||
*
|
||||
* @author Sven Mawson
|
||||
* @since 1.0
|
||||
*/
|
||||
abstract class AbstractFuture<V> implements ListenableFuture<V> {
|
||||
|
||||
/** Synchronization control for AbstractFutures. */
|
||||
private final Sync<V> sync = new Sync<V>();
|
||||
|
||||
// The execution list to hold our executors.
|
||||
private final ExecutionList executionList = new ExecutionList();
|
||||
|
||||
/**
|
||||
* Constructor for use by subclasses.
|
||||
*/
|
||||
protected AbstractFuture() {}
|
||||
|
||||
/*
|
||||
* Improve the documentation of when InterruptedException is thrown. Our
|
||||
* behavior matches the JDK's, but the JDK's documentation is misleading.
|
||||
*/
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default {@link AbstractFuture} implementation throws {@code
|
||||
* InterruptedException} if the current thread is interrupted before or during
|
||||
* the call, even if the value is already available.
|
||||
*
|
||||
* @throws InterruptedException if the current thread was interrupted before
|
||||
* or during the call (optional but recommended).
|
||||
* @throws CancellationException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public V get(long timeout, TimeUnit unit) throws InterruptedException,
|
||||
TimeoutException, ExecutionException {
|
||||
return sync.get(unit.toNanos(timeout));
|
||||
}
|
||||
|
||||
/*
|
||||
* Improve the documentation of when InterruptedException is thrown. Our
|
||||
* behavior matches the JDK's, but the JDK's documentation is misleading.
|
||||
*/
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default {@link AbstractFuture} implementation throws {@code
|
||||
* InterruptedException} if the current thread is interrupted before or during
|
||||
* the call, even if the value is already available.
|
||||
*
|
||||
* @throws InterruptedException if the current thread was interrupted before
|
||||
* or during the call (optional but recommended).
|
||||
* @throws CancellationException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public V get() throws InterruptedException, ExecutionException {
|
||||
return sync.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return sync.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return sync.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
if (!sync.cancel(mayInterruptIfRunning)) {
|
||||
return false;
|
||||
}
|
||||
executionList.execute();
|
||||
if (mayInterruptIfRunning) {
|
||||
interruptTask();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to implement interruption of the
|
||||
* future's computation. The method is invoked automatically by a successful
|
||||
* call to {@link #cancel(boolean) cancel(true)}.
|
||||
*
|
||||
* <p>The default implementation does nothing.
|
||||
*
|
||||
* @since 10.0
|
||||
*/
|
||||
protected void interruptTask() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this future was cancelled with {@code
|
||||
* mayInterruptIfRunning} set to {@code true}.
|
||||
*
|
||||
* @since 14.0
|
||||
*/
|
||||
protected final boolean wasInterrupted() {
|
||||
return sync.wasInterrupted();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @since 10.0
|
||||
*/
|
||||
@Override
|
||||
public void addListener(Runnable listener, Executor exec) {
|
||||
executionList.add(listener, exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should invoke this method to set the result of the computation
|
||||
* to {@code value}. This will set the state of the future to
|
||||
* {@link AbstractFuture.Sync#COMPLETED} and invoke the listeners if the
|
||||
* state was successfully changed.
|
||||
*
|
||||
* @param value the value that was the result of the task.
|
||||
* @return true if the state was successfully changed.
|
||||
*/
|
||||
protected boolean set(V value) {
|
||||
boolean result = sync.set(value);
|
||||
if (result) {
|
||||
executionList.execute();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should invoke this method to set the result of the computation
|
||||
* to an error, {@code throwable}. This will set the state of the future to
|
||||
* {@link AbstractFuture.Sync#COMPLETED} and invoke the listeners if the
|
||||
* state was successfully changed.
|
||||
*
|
||||
* @param throwable the exception that the task failed with.
|
||||
* @return true if the state was successfully changed.
|
||||
*/
|
||||
protected boolean setException(Throwable throwable) {
|
||||
boolean result = sync.setException(throwable);
|
||||
if (result) {
|
||||
executionList.execute();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Following the contract of {@link AbstractQueuedSynchronizer} we create a
|
||||
* private subclass to hold the synchronizer. This synchronizer is used to
|
||||
* implement the blocking and waiting calls as well as to handle state changes
|
||||
* in a thread-safe manner. The current state of the future is held in the
|
||||
* Sync state, and the lock is released whenever the state changes to
|
||||
* {@link #COMPLETED}, {@link #CANCELLED}, or {@link #INTERRUPTED}
|
||||
*
|
||||
* <p>To avoid races between threads doing release and acquire, we transition
|
||||
* to the final state in two steps. One thread will successfully CAS from
|
||||
* RUNNING to COMPLETING, that thread will then set the result of the
|
||||
* computation, and only then transition to COMPLETED, CANCELLED, or
|
||||
* INTERRUPTED.
|
||||
*
|
||||
* <p>We don't use the integer argument passed between acquire methods so we
|
||||
* pass around a -1 everywhere.
|
||||
*/
|
||||
static final class Sync<V> extends AbstractQueuedSynchronizer {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/* Valid states. */
|
||||
static final int RUNNING = 0;
|
||||
static final int COMPLETING = 1;
|
||||
static final int COMPLETED = 2;
|
||||
static final int CANCELLED = 4;
|
||||
static final int INTERRUPTED = 8;
|
||||
|
||||
private V value;
|
||||
private Throwable exception;
|
||||
|
||||
/*
|
||||
* Acquisition succeeds if the future is done, otherwise it fails.
|
||||
*/
|
||||
@Override
|
||||
protected int tryAcquireShared(int ignored) {
|
||||
if (isDone()) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We always allow a release to go through, this means the state has been
|
||||
* successfully changed and the result is available.
|
||||
*/
|
||||
@Override
|
||||
protected boolean tryReleaseShared(int finalState) {
|
||||
setState(finalState);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until the task is complete or the timeout expires. Throws a
|
||||
* {@link TimeoutException} if the timer expires, otherwise behaves like
|
||||
* {@link #get()}.
|
||||
*/
|
||||
V get(long nanos) throws TimeoutException, CancellationException,
|
||||
ExecutionException, InterruptedException {
|
||||
|
||||
// Attempt to acquire the shared lock with a timeout.
|
||||
if (!tryAcquireSharedNanos(-1, nanos)) {
|
||||
throw new TimeoutException("Timeout waiting for task.");
|
||||
}
|
||||
|
||||
return getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until {@link #complete(Object, Throwable, int)} has been
|
||||
* successfully called. Throws a {@link CancellationException} if the task
|
||||
* was cancelled, or a {@link ExecutionException} if the task completed with
|
||||
* an error.
|
||||
*/
|
||||
V get() throws CancellationException, ExecutionException,
|
||||
InterruptedException {
|
||||
|
||||
// Acquire the shared lock allowing interruption.
|
||||
acquireSharedInterruptibly(-1);
|
||||
return getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the actual value retrieval. Will return the value
|
||||
* on success, an exception on failure, a cancellation on cancellation, or
|
||||
* an illegal state if the synchronizer is in an invalid state.
|
||||
*/
|
||||
private V getValue() throws CancellationException, ExecutionException {
|
||||
int state = getState();
|
||||
switch (state) {
|
||||
case COMPLETED:
|
||||
if (exception != null) {
|
||||
throw new ExecutionException(exception);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
||||
case CANCELLED:
|
||||
case INTERRUPTED:
|
||||
throw cancellationExceptionWithCause(
|
||||
"Task was cancelled.", exception);
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"Error, synchronizer in invalid state: " + state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the state is {@link #COMPLETED}, {@link #CANCELLED}, or {@link
|
||||
* INTERRUPTED}.
|
||||
*/
|
||||
boolean isDone() {
|
||||
return (getState() & (COMPLETED | CANCELLED | INTERRUPTED)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the state is {@link #CANCELLED} or {@link #INTERRUPTED}.
|
||||
*/
|
||||
boolean isCancelled() {
|
||||
return (getState() & (CANCELLED | INTERRUPTED)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the state is {@link #INTERRUPTED}.
|
||||
*/
|
||||
boolean wasInterrupted() {
|
||||
return getState() == INTERRUPTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition to the COMPLETED state and set the value.
|
||||
*/
|
||||
boolean set(V v) {
|
||||
return complete(v, null, COMPLETED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition to the COMPLETED state and set the exception.
|
||||
*/
|
||||
boolean setException(Throwable t) {
|
||||
return complete(null, t, COMPLETED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition to the CANCELLED or INTERRUPTED state.
|
||||
*/
|
||||
boolean cancel(boolean interrupt) {
|
||||
return complete(null, null, interrupt ? INTERRUPTED : CANCELLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of completing a task. Either {@code v} or {@code t} will
|
||||
* be set but not both. The {@code finalState} is the state to change to
|
||||
* from {@link #RUNNING}. If the state is not in the RUNNING state we
|
||||
* return {@code false} after waiting for the state to be set to a valid
|
||||
* final state ({@link #COMPLETED}, {@link #CANCELLED}, or {@link
|
||||
* #INTERRUPTED}).
|
||||
*
|
||||
* @param v the value to set as the result of the computation.
|
||||
* @param t the exception to set as the result of the computation.
|
||||
* @param finalState the state to transition to.
|
||||
*/
|
||||
private boolean complete(V v, Throwable t,
|
||||
int finalState) {
|
||||
boolean doCompletion = compareAndSetState(RUNNING, COMPLETING);
|
||||
if (doCompletion) {
|
||||
// If this thread successfully transitioned to COMPLETING, set the value
|
||||
// and exception and then release to the final state.
|
||||
this.value = v;
|
||||
// Don't actually construct a CancellationException until necessary.
|
||||
this.exception = ((finalState & (CANCELLED | INTERRUPTED)) != 0)
|
||||
? new CancellationException("Future.cancel() was called.") : t;
|
||||
releaseShared(finalState);
|
||||
} else if (getState() == COMPLETING) {
|
||||
// If some other thread is currently completing the future, block until
|
||||
// they are done so we can guarantee completion.
|
||||
acquireShared(-1);
|
||||
}
|
||||
return doCompletion;
|
||||
}
|
||||
}
|
||||
|
||||
static final CancellationException cancellationExceptionWithCause(
|
||||
String message, Throwable cause) {
|
||||
CancellationException exception = new CancellationException(message);
|
||||
exception.initCause(cause);
|
||||
return exception;
|
||||
}
|
||||
}
|
60
java/src/game/future/ExecutionError.java
Normal file
60
java/src/game/future/ExecutionError.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
/**
|
||||
* {@link Error} variant of {@link java.util.concurrent.ExecutionException}. As
|
||||
* with {@code ExecutionException}, the error's {@linkplain #getCause() cause}
|
||||
* comes from a failed task, possibly run in another thread. That cause should
|
||||
* itself be an {@code Error}; if not, use {@code ExecutionException} or {@link
|
||||
* UncheckedExecutionException}. This allows the client code to continue to
|
||||
* distinguish between exceptions and errors, even when they come from other
|
||||
* threads.
|
||||
*
|
||||
* @author Chris Povirk
|
||||
* @since 10.0
|
||||
*/
|
||||
|
||||
class ExecutionError extends Error {
|
||||
/**
|
||||
* Creates a new instance with {@code null} as its detail message.
|
||||
*/
|
||||
protected ExecutionError() {}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given detail message.
|
||||
*/
|
||||
protected ExecutionError(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given detail message and cause.
|
||||
*/
|
||||
public ExecutionError(String message, Error cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given cause.
|
||||
*/
|
||||
public ExecutionError(Error cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
170
java/src/game/future/ExecutionList.java
Normal file
170
java/src/game/future/ExecutionList.java
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* <p>A list of listeners, each with an associated {@code Executor}, that
|
||||
* guarantees that every {@code Runnable} that is {@linkplain #add added} will
|
||||
* be executed after {@link #execute()} is called. Any {@code Runnable} added
|
||||
* after the call to {@code execute} is still guaranteed to execute. There is no
|
||||
* guarantee, however, that listeners will be executed in the order that they
|
||||
* are added.
|
||||
*
|
||||
* <p>Exceptions thrown by a listener will be propagated up to the executor.
|
||||
* Any exception thrown during {@code Executor.execute} (e.g., a {@code
|
||||
* RejectedExecutionException} or an exception thrown by {@linkplain
|
||||
* MoreExecutors#sameThreadExecutor inline execution}) will be caught and
|
||||
* logged.
|
||||
*
|
||||
* @author Nishant Thakkar
|
||||
* @author Sven Mawson
|
||||
* @since 1.0
|
||||
*/
|
||||
final class ExecutionList {
|
||||
// Logger to log exceptions caught when running runnables.
|
||||
// @VisibleForTesting
|
||||
static final Logger log = Logger.getLogger(ExecutionList.class.getName());
|
||||
|
||||
/**
|
||||
* The runnable, executor pairs to execute. This acts as a stack threaded through the
|
||||
* {@link RunnableExecutorPair#next} field.
|
||||
*/
|
||||
// @GuardedBy("this")
|
||||
private RunnableExecutorPair runnables;
|
||||
// @GuardedBy("this")
|
||||
private boolean executed;
|
||||
|
||||
/** Creates a new, empty {@link ExecutionList}. */
|
||||
public ExecutionList() {}
|
||||
|
||||
/**
|
||||
* Adds the {@code Runnable} and accompanying {@code Executor} to the list of
|
||||
* listeners to execute. If execution has already begun, the listener is
|
||||
* executed immediately.
|
||||
*
|
||||
* <p>Note: For fast, lightweight listeners that would be safe to execute in
|
||||
* any thread, consider {@link MoreExecutors#sameThreadExecutor}. For heavier
|
||||
* listeners, {@code sameThreadExecutor()} carries some caveats: First, the
|
||||
* thread that the listener runs in depends on whether the {@code
|
||||
* ExecutionList} has been executed at the time it is added. In particular,
|
||||
* listeners may run in the thread that calls {@code add}. Second, the thread
|
||||
* that calls {@link #execute} may be an internal implementation thread, such
|
||||
* as an RPC network thread, and {@code sameThreadExecutor()} listeners may
|
||||
* run in this thread. Finally, during the execution of a {@code
|
||||
* sameThreadExecutor} listener, all other registered but unexecuted
|
||||
* listeners are prevented from running, even if those listeners are to run
|
||||
* in other executors.
|
||||
*/
|
||||
public void add(Runnable runnable, Executor executor) {
|
||||
// Fail fast on a null. We throw NPE here because the contract of
|
||||
// Executor states that it throws NPE on null listener, so we propagate
|
||||
// that contract up into the add method as well.
|
||||
|
||||
// Lock while we check state. We must maintain the lock while adding the
|
||||
// new pair so that another thread can't run the list out from under us.
|
||||
// We only add to the list if we have not yet started execution.
|
||||
synchronized (this) {
|
||||
if (!executed) {
|
||||
runnables = new RunnableExecutorPair(runnable, executor, runnables);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Execute the runnable immediately. Because of scheduling this may end up
|
||||
// getting called before some of the previously added runnables, but we're
|
||||
// OK with that. If we want to change the contract to guarantee ordering
|
||||
// among runnables we'd have to modify the logic here to allow it.
|
||||
executeListener(runnable, executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs this execution list, executing all existing pairs in the order they
|
||||
* were added. However, note that listeners added after this point may be
|
||||
* executed before those previously added, and note that the execution order
|
||||
* of all listeners is ultimately chosen by the implementations of the
|
||||
* supplied executors.
|
||||
*
|
||||
* <p>This method is idempotent. Calling it several times in parallel is
|
||||
* semantically equivalent to calling it exactly once.
|
||||
*
|
||||
* @since 10.0 (present in 1.0 as {@code run})
|
||||
*/
|
||||
public void execute() {
|
||||
// Lock while we update our state so the add method above will finish adding
|
||||
// any listeners before we start to run them.
|
||||
RunnableExecutorPair list;
|
||||
synchronized (this) {
|
||||
if (executed) {
|
||||
return;
|
||||
}
|
||||
executed = true;
|
||||
list = runnables;
|
||||
runnables = null; // allow GC to free listeners even if this stays around for a while.
|
||||
}
|
||||
// If we succeeded then list holds all the runnables we to execute. The pairs in the stack are
|
||||
// in the opposite order from how they were added so we need to reverse the list to fulfill our
|
||||
// contract.
|
||||
// This is somewhat annoying, but turns out to be very fast in practice. Alternatively, we
|
||||
// could drop the contract on the method that enforces this queue like behavior since depending
|
||||
// on it is likely to be a bug anyway.
|
||||
|
||||
// N.B. All writes to the list and the next pointers must have happened before the above
|
||||
// synchronized block, so we can iterate the list without the lock held here.
|
||||
RunnableExecutorPair reversedList = null;
|
||||
while (list != null) {
|
||||
RunnableExecutorPair tmp = list;
|
||||
list = list.next;
|
||||
tmp.next = reversedList;
|
||||
reversedList = tmp;
|
||||
}
|
||||
while (reversedList != null) {
|
||||
executeListener(reversedList.runnable, reversedList.executor);
|
||||
reversedList = reversedList.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the given runnable to the given {@link Executor} catching and logging all
|
||||
* {@linkplain RuntimeException runtime exceptions} thrown by the executor.
|
||||
*/
|
||||
private static void executeListener(Runnable runnable, Executor executor) {
|
||||
try {
|
||||
executor.execute(runnable);
|
||||
} catch (RuntimeException e) {
|
||||
// Log it and keep going, bad runnable and/or executor. Don't
|
||||
// punish the other runnables if we're given a bad one. We only
|
||||
// catch RuntimeException because we want Errors to propagate up.
|
||||
log.log(Level.SEVERE, "RuntimeException while executing runnable "
|
||||
+ runnable + " with executor " + executor, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RunnableExecutorPair {
|
||||
final Runnable runnable;
|
||||
final Executor executor;
|
||||
RunnableExecutorPair next;
|
||||
|
||||
RunnableExecutorPair(Runnable runnable, Executor executor, RunnableExecutorPair next) {
|
||||
this.runnable = runnable;
|
||||
this.executor = executor;
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
}
|
43
java/src/game/future/FutureCallback.java
Normal file
43
java/src/game/future/FutureCallback.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
/**
|
||||
* A callback for accepting the results of a {@link java.util.concurrent.Future}
|
||||
* computation asynchronously.
|
||||
*
|
||||
* <p>To attach to a {@link ListenableFuture} use {@link Futures#addCallback}.
|
||||
*
|
||||
* @author Anthony Zana
|
||||
* @since 10.0
|
||||
*/
|
||||
public interface FutureCallback<V> {
|
||||
/**
|
||||
* Invoked with the result of the {@code Future} computation when it is
|
||||
* successful.
|
||||
*/
|
||||
void onSuccess(V result);
|
||||
|
||||
/**
|
||||
* Invoked when a {@code Future} computation fails or is canceled.
|
||||
*
|
||||
* <p>If the future's {@link Future#get() get} method throws an {@link
|
||||
* ExecutionException}, then the cause is passed to this method. Any other
|
||||
* thrown object is passed unaltered.
|
||||
*/
|
||||
void onFailure(Throwable t);
|
||||
}
|
1723
java/src/game/future/Futures.java
Normal file
1723
java/src/game/future/Futures.java
Normal file
File diff suppressed because it is too large
Load diff
132
java/src/game/future/ListenableFuture.java
Normal file
132
java/src/game/future/ListenableFuture.java
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* A {@link Future} that accepts completion listeners. Each listener has an
|
||||
* associated executor, and it is invoked using this executor once the future's
|
||||
* computation is {@linkplain Future#isDone() complete}. If the computation has
|
||||
* already completed when the listener is added, the listener will execute
|
||||
* immediately.
|
||||
*
|
||||
* <p>See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained">
|
||||
* {@code ListenableFuture}</a>.
|
||||
*
|
||||
* <h3>Purpose</h3>
|
||||
*
|
||||
* <p>Most commonly, {@code ListenableFuture} is used as an input to another
|
||||
* derived {@code Future}, as in {@link Futures#allAsList(Iterable)
|
||||
* Futures.allAsList}. Many such methods are impossible to implement efficiently
|
||||
* without listener support.
|
||||
*
|
||||
* <p>It is possible to call {@link #addListener addListener} directly, but this
|
||||
* is uncommon because the {@code Runnable} interface does not provide direct
|
||||
* access to the {@code Future} result. (Users who want such access may prefer
|
||||
* {@link Futures#addCallback Futures.addCallback}.) Still, direct {@code
|
||||
* addListener} calls are occasionally useful:<pre> {@code
|
||||
* final String name = ...;
|
||||
* inFlight.add(name);
|
||||
* ListenableFuture<Result> future = service.query(name);
|
||||
* future.addListener(new Runnable() {
|
||||
* public void run() {
|
||||
* processedCount.incrementAndGet();
|
||||
* inFlight.remove(name);
|
||||
* lastProcessed.set(name);
|
||||
* logger.info("Done with {0}", name);
|
||||
* }
|
||||
* }, executor);}</pre>
|
||||
*
|
||||
* <h3>How to get an instance</h3>
|
||||
*
|
||||
* <p>Developers are encouraged to return {@code ListenableFuture} from their
|
||||
* methods so that users can take advantages of the utilities built atop the
|
||||
* class. The way that they will create {@code ListenableFuture} instances
|
||||
* depends on how they currently create {@code Future} instances:
|
||||
* <ul>
|
||||
* <li>If they are returned from an {@code ExecutorService}, convert that
|
||||
* service to a {@link ListeningExecutorService}, usually by calling {@link
|
||||
* MoreExecutors#listeningDecorator(ExecutorService)
|
||||
* MoreExecutors.listeningDecorator}. (Custom executors may find it more
|
||||
* convenient to use {@link ListenableFutureTask} directly.)
|
||||
* <li>If they are manually filled in by a call to {@link FutureTask#set} or a
|
||||
* similar method, create a {@link SettableFuture} instead. (Users with more
|
||||
* complex needs may prefer {@link AbstractFuture}.)
|
||||
* </ul>
|
||||
*
|
||||
* <p>Occasionally, an API will return a plain {@code Future} and it will be
|
||||
* impossible to change the return type. For this case, we provide a more
|
||||
* expensive workaround in {@code JdkFutureAdapters}. However, when possible, it
|
||||
* is more efficient and reliable to create a {@code ListenableFuture} directly.
|
||||
*
|
||||
* @author Sven Mawson
|
||||
* @author Nishant Thakkar
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface ListenableFuture<V> extends Future<V> {
|
||||
/**
|
||||
* Registers a listener to be {@linkplain Executor#execute(Runnable) run} on
|
||||
* the given executor. The listener will run when the {@code Future}'s
|
||||
* computation is {@linkplain Future#isDone() complete} or, if the computation
|
||||
* is already complete, immediately.
|
||||
*
|
||||
* <p>There is no guaranteed ordering of execution of listeners, but any
|
||||
* listener added through this method is guaranteed to be called once the
|
||||
* computation is complete.
|
||||
*
|
||||
* <p>Exceptions thrown by a listener will be propagated up to the executor.
|
||||
* Any exception thrown during {@code Executor.execute} (e.g., a {@code
|
||||
* RejectedExecutionException} or an exception thrown by {@linkplain
|
||||
* MoreExecutors#sameThreadExecutor inline execution}) will be caught and
|
||||
* logged.
|
||||
*
|
||||
* <p>Note: For fast, lightweight listeners that would be safe to execute in
|
||||
* any thread, consider {@link MoreExecutors#sameThreadExecutor}. For heavier
|
||||
* listeners, {@code sameThreadExecutor()} carries some caveats. For
|
||||
* example, the listener may run on an unpredictable or undesirable thread:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If this {@code Future} is done at the time {@code addListener} is
|
||||
* called, {@code addListener} will execute the listener inline.
|
||||
* <li>If this {@code Future} is not yet done, {@code addListener} will
|
||||
* schedule the listener to be run by the thread that completes this {@code
|
||||
* Future}, which may be an internal system thread such as an RPC network
|
||||
* thread.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Also note that, regardless of which thread executes the
|
||||
* {@code sameThreadExecutor()} listener, all other registered but unexecuted
|
||||
* listeners are prevented from running during its execution, even if those
|
||||
* listeners are to run in other executors.
|
||||
*
|
||||
* <p>This is the most general listener interface. For common operations
|
||||
* performed using listeners, see {@link
|
||||
* game.future.Futures}. For a simplified but general
|
||||
* listener interface, see {@link
|
||||
* game.future.Futures#addCallback addCallback()}.
|
||||
*
|
||||
* @param listener the listener to run when the computation is complete
|
||||
* @param executor the executor to run the listener in
|
||||
* @throws NullPointerException if the executor or listener was null
|
||||
* @throws RejectedExecutionException if we tried to execute the listener
|
||||
* immediately but the executor rejected it.
|
||||
*/
|
||||
void addListener(Runnable listener, Executor executor);
|
||||
}
|
91
java/src/game/future/ListenableFutureTask.java
Normal file
91
java/src/game/future/ListenableFutureTask.java
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* A {@link FutureTask} that also implements the {@link ListenableFuture}
|
||||
* interface. Unlike {@code FutureTask}, {@code ListenableFutureTask} does not
|
||||
* provide an overrideable {@link FutureTask#done() done()} method. For similar
|
||||
* functionality, call {@link #addListener}.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @author Sven Mawson
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ListenableFutureTask<V> extends FutureTask<V>
|
||||
implements ListenableFuture<V> {
|
||||
// TODO(cpovirk): explore ways of making ListenableFutureTask final. There are
|
||||
// some valid reasons such as BoundedQueueExecutorService to allow extends but it
|
||||
// would be nice to make it final to avoid unintended usage.
|
||||
|
||||
// The execution list to hold our listeners.
|
||||
private final ExecutionList executionList = new ExecutionList();
|
||||
|
||||
/**
|
||||
* Creates a {@code ListenableFutureTask} that will upon running, execute the
|
||||
* given {@code Callable}.
|
||||
*
|
||||
* @param callable the callable task
|
||||
* @since 10.0
|
||||
*/
|
||||
public static <V> ListenableFutureTask<V> create(Callable<V> callable) {
|
||||
return new ListenableFutureTask<V>(callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code ListenableFutureTask} that will upon running, execute the
|
||||
* given {@code Runnable}, and arrange that {@code get} will return the
|
||||
* given result on successful completion.
|
||||
*
|
||||
* @param runnable the runnable task
|
||||
* @param result the result to return on successful completion. If you don't
|
||||
* need a particular result, consider using constructions of the form:
|
||||
* {@code ListenableFuture<?> f = ListenableFutureTask.create(runnable,
|
||||
* null)}
|
||||
* @since 10.0
|
||||
*/
|
||||
public static <V> ListenableFutureTask<V> create(
|
||||
Runnable runnable, V result) {
|
||||
return new ListenableFutureTask<V>(runnable, result);
|
||||
}
|
||||
|
||||
ListenableFutureTask(Callable<V> callable) {
|
||||
super(callable);
|
||||
}
|
||||
|
||||
ListenableFutureTask(Runnable runnable, V result) {
|
||||
super(runnable, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Runnable listener, Executor exec) {
|
||||
executionList.add(listener, exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation detail used to invoke the listeners.
|
||||
*/
|
||||
@Override
|
||||
protected void done() {
|
||||
executionList.execute();
|
||||
}
|
||||
}
|
889
java/src/game/future/MoreExecutors.java
Normal file
889
java/src/game/future/MoreExecutors.java
Normal file
|
@ -0,0 +1,889 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.AbstractExecutorService;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Factory and utility methods for {@link java.util.concurrent.Executor}, {@link
|
||||
* ExecutorService}, and {@link ThreadFactory}.
|
||||
*
|
||||
* @author Eric Fellheimer
|
||||
* @author Kyle Littlefield
|
||||
* @author Justin Mahoney
|
||||
* @since 3.0
|
||||
*/
|
||||
final class MoreExecutors {
|
||||
private MoreExecutors() {}
|
||||
|
||||
/**
|
||||
* Converts the given ThreadPoolExecutor into an ExecutorService that exits
|
||||
* when the application is complete. It does so by using daemon threads and
|
||||
* adding a shutdown hook to wait for their completion.
|
||||
*
|
||||
* <p>This is mainly for fixed thread pools.
|
||||
* See {@link Executors#newFixedThreadPool(int)}.
|
||||
*
|
||||
* @param executor the executor to modify to make sure it exits when the
|
||||
* application is finished
|
||||
* @param terminationTimeout how long to wait for the executor to
|
||||
* finish before terminating the JVM
|
||||
* @param timeUnit unit of time for the time parameter
|
||||
* @return an unmodifiable version of the input which will not hang the JVM
|
||||
*/
|
||||
// @Beta
|
||||
// public static ExecutorService getExitingExecutorService(
|
||||
// ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) {
|
||||
// return new Application()
|
||||
// .getExitingExecutorService(executor, terminationTimeout, timeUnit);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Converts the given ScheduledThreadPoolExecutor into a
|
||||
* ScheduledExecutorService that exits when the application is complete. It
|
||||
* does so by using daemon threads and adding a shutdown hook to wait for
|
||||
* their completion.
|
||||
*
|
||||
* <p>This is mainly for fixed thread pools.
|
||||
* See {@link Executors#newScheduledThreadPool(int)}.
|
||||
*
|
||||
* @param executor the executor to modify to make sure it exits when the
|
||||
* application is finished
|
||||
* @param terminationTimeout how long to wait for the executor to
|
||||
* finish before terminating the JVM
|
||||
* @param timeUnit unit of time for the time parameter
|
||||
* @return an unmodifiable version of the input which will not hang the JVM
|
||||
*/
|
||||
// @Beta
|
||||
// public static ScheduledExecutorService getExitingScheduledExecutorService(
|
||||
// ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) {
|
||||
// return new Application()
|
||||
// .getExitingScheduledExecutorService(executor, terminationTimeout, timeUnit);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Add a shutdown hook to wait for thread completion in the given
|
||||
* {@link ExecutorService service}. This is useful if the given service uses
|
||||
* daemon threads, and we want to keep the JVM from exiting immediately on
|
||||
* shutdown, instead giving these daemon threads a chance to terminate
|
||||
* normally.
|
||||
* @param service ExecutorService which uses daemon threads
|
||||
* @param terminationTimeout how long to wait for the executor to finish
|
||||
* before terminating the JVM
|
||||
* @param timeUnit unit of time for the time parameter
|
||||
*/
|
||||
// @Beta
|
||||
// public static void addDelayedShutdownHook(
|
||||
// ExecutorService service, long terminationTimeout, TimeUnit timeUnit) {
|
||||
// new Application()
|
||||
// .addDelayedShutdownHook(service, terminationTimeout, timeUnit);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Converts the given ThreadPoolExecutor into an ExecutorService that exits
|
||||
* when the application is complete. It does so by using daemon threads and
|
||||
* adding a shutdown hook to wait for their completion.
|
||||
*
|
||||
* <p>This method waits 120 seconds before continuing with JVM termination,
|
||||
* even if the executor has not finished its work.
|
||||
*
|
||||
* <p>This is mainly for fixed thread pools.
|
||||
* See {@link Executors#newFixedThreadPool(int)}.
|
||||
*
|
||||
* @param executor the executor to modify to make sure it exits when the
|
||||
* application is finished
|
||||
* @return an unmodifiable version of the input which will not hang the JVM
|
||||
*/
|
||||
// @Beta
|
||||
// public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) {
|
||||
// return new Application().getExitingExecutorService(executor);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Converts the given ThreadPoolExecutor into a ScheduledExecutorService that
|
||||
* exits when the application is complete. It does so by using daemon threads
|
||||
* and adding a shutdown hook to wait for their completion.
|
||||
*
|
||||
* <p>This method waits 120 seconds before continuing with JVM termination,
|
||||
* even if the executor has not finished its work.
|
||||
*
|
||||
* <p>This is mainly for fixed thread pools.
|
||||
* See {@link Executors#newScheduledThreadPool(int)}.
|
||||
*
|
||||
* @param executor the executor to modify to make sure it exits when the
|
||||
* application is finished
|
||||
* @return an unmodifiable version of the input which will not hang the JVM
|
||||
*/
|
||||
// @Beta
|
||||
// public static ScheduledExecutorService getExitingScheduledExecutorService(
|
||||
// ScheduledThreadPoolExecutor executor) {
|
||||
// return new Application().getExitingScheduledExecutorService(executor);
|
||||
// }
|
||||
|
||||
/** Represents the current application to register shutdown hooks. */
|
||||
// @VisibleForTesting static class Application {
|
||||
//
|
||||
// final ExecutorService getExitingExecutorService(
|
||||
// ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) {
|
||||
// useDaemonThreadFactory(executor);
|
||||
// ExecutorService service = Executors.unconfigurableExecutorService(executor);
|
||||
// addDelayedShutdownHook(service, terminationTimeout, timeUnit);
|
||||
// return service;
|
||||
// }
|
||||
//
|
||||
// final ScheduledExecutorService getExitingScheduledExecutorService(
|
||||
// ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) {
|
||||
// useDaemonThreadFactory(executor);
|
||||
// ScheduledExecutorService service = Executors.unconfigurableScheduledExecutorService(executor);
|
||||
// addDelayedShutdownHook(service, terminationTimeout, timeUnit);
|
||||
// return service;
|
||||
// }
|
||||
//
|
||||
// final void addDelayedShutdownHook(
|
||||
// final ExecutorService service, final long terminationTimeout, final TimeUnit timeUnit) {
|
||||
// checkNotNull(service);
|
||||
// checkNotNull(timeUnit);
|
||||
// addShutdownHook(MoreExecutors.newThread("DelayedShutdownHook-for-" + service, new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// try {
|
||||
// // We'd like to log progress and failures that may arise in the
|
||||
// // following code, but unfortunately the behavior of logging
|
||||
// // is undefined in shutdown hooks.
|
||||
// // This is because the logging code installs a shutdown hook of its
|
||||
// // own. See Cleaner class inside {@link LogManager}.
|
||||
// service.shutdown();
|
||||
// service.awaitTermination(terminationTimeout, timeUnit);
|
||||
// } catch (InterruptedException ignored) {
|
||||
// // We're shutting down anyway, so just ignore.
|
||||
// }
|
||||
// }
|
||||
// }));
|
||||
// }
|
||||
//
|
||||
// final ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) {
|
||||
// return getExitingExecutorService(executor, 120, TimeUnit.SECONDS);
|
||||
// }
|
||||
//
|
||||
// final ScheduledExecutorService getExitingScheduledExecutorService(
|
||||
// ScheduledThreadPoolExecutor executor) {
|
||||
// return getExitingScheduledExecutorService(executor, 120, TimeUnit.SECONDS);
|
||||
// }
|
||||
//
|
||||
// @VisibleForTesting void addShutdownHook(Thread hook) {
|
||||
// Runtime.getRuntime().addShutdownHook(hook);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static void useDaemonThreadFactory(ThreadPoolExecutor executor) {
|
||||
// executor.setThreadFactory(new ThreadFactoryBuilder()
|
||||
// .setDaemon(true)
|
||||
// .setThreadFactory(executor.getThreadFactory())
|
||||
// .build());
|
||||
// }
|
||||
|
||||
/**
|
||||
* Creates an executor service that runs each task in the thread
|
||||
* that invokes {@code execute/submit}, as in {@link CallerRunsPolicy} This
|
||||
* applies both to individually submitted tasks and to collections of tasks
|
||||
* submitted via {@code invokeAll} or {@code invokeAny}. In the latter case,
|
||||
* tasks will run serially on the calling thread. Tasks are run to
|
||||
* completion before a {@code Future} is returned to the caller (unless the
|
||||
* executor has been shutdown).
|
||||
*
|
||||
* <p>Although all tasks are immediately executed in the thread that
|
||||
* submitted the task, this {@code ExecutorService} imposes a small
|
||||
* locking overhead on each task submission in order to implement shutdown
|
||||
* and termination behavior.
|
||||
*
|
||||
* <p>The implementation deviates from the {@code ExecutorService}
|
||||
* specification with regards to the {@code shutdownNow} method. First,
|
||||
* "best-effort" with regards to canceling running tasks is implemented
|
||||
* as "no-effort". No interrupts or other attempts are made to stop
|
||||
* threads executing tasks. Second, the returned list will always be empty,
|
||||
* as any submitted task is considered to have started execution.
|
||||
* This applies also to tasks given to {@code invokeAll} or {@code invokeAny}
|
||||
* which are pending serial execution, even the subset of the tasks that
|
||||
* have not yet started execution. It is unclear from the
|
||||
* {@code ExecutorService} specification if these should be included, and
|
||||
* it's much easier to implement the interpretation that they not be.
|
||||
* Finally, a call to {@code shutdown} or {@code shutdownNow} may result
|
||||
* in concurrent calls to {@code invokeAll/invokeAny} throwing
|
||||
* RejectedExecutionException, although a subset of the tasks may already
|
||||
* have been executed.
|
||||
*
|
||||
* @since 10.0 (<a href="http://code.google.com/p/guava-libraries/wiki/Compatibility"
|
||||
* >mostly source-compatible</a> since 3.0)
|
||||
*/
|
||||
public static SameThreadExecutorService sameThreadExecutor() {
|
||||
return new SameThreadExecutorService();
|
||||
}
|
||||
|
||||
// See sameThreadExecutor javadoc for behavioral notes.
|
||||
private static class SameThreadExecutorService
|
||||
extends AbstractExecutorService implements ExecutorService {
|
||||
/**
|
||||
* Lock used whenever accessing the state variables
|
||||
* (runningTasks, shutdown, terminationCondition) of the executor
|
||||
*/
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
/** Signaled after the executor is shutdown and running tasks are done */
|
||||
private final Condition termination = lock.newCondition();
|
||||
|
||||
/*
|
||||
* Conceptually, these two variables describe the executor being in
|
||||
* one of three states:
|
||||
* - Active: shutdown == false
|
||||
* - Shutdown: runningTasks > 0 and shutdown == true
|
||||
* - Terminated: runningTasks == 0 and shutdown == true
|
||||
*/
|
||||
private int runningTasks = 0;
|
||||
private boolean shutdown = false;
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
startTask();
|
||||
try {
|
||||
command.run();
|
||||
} finally {
|
||||
endTask();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
lock.lock();
|
||||
try {
|
||||
return shutdown;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
lock.lock();
|
||||
try {
|
||||
shutdown = true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// See sameThreadExecutor javadoc for unusual behavior of this method.
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
shutdown();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
lock.lock();
|
||||
try {
|
||||
return shutdown && runningTasks == 0;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit)
|
||||
throws InterruptedException {
|
||||
long nanos = unit.toNanos(timeout);
|
||||
lock.lock();
|
||||
try {
|
||||
for (;;) {
|
||||
if (isTerminated()) {
|
||||
return true;
|
||||
} else if (nanos <= 0) {
|
||||
return false;
|
||||
} else {
|
||||
nanos = termination.awaitNanos(nanos);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the executor has been shut down and increments the running
|
||||
* task count.
|
||||
*
|
||||
* @throws RejectedExecutionException if the executor has been previously
|
||||
* shutdown
|
||||
*/
|
||||
private void startTask() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (isShutdown()) {
|
||||
throw new RejectedExecutionException("Executor already shutdown");
|
||||
}
|
||||
runningTasks++;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the running task count.
|
||||
*/
|
||||
private void endTask() {
|
||||
lock.lock();
|
||||
try {
|
||||
runningTasks--;
|
||||
if (isTerminated()) {
|
||||
termination.signalAll();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected final <T> ListenableFutureTask<T> newTaskFor(Runnable runnable, T value) {
|
||||
return ListenableFutureTask.create(runnable, value);
|
||||
}
|
||||
|
||||
@Override protected final <T> ListenableFutureTask<T> newTaskFor(Callable<T> callable) {
|
||||
return ListenableFutureTask.create(callable);
|
||||
}
|
||||
|
||||
@Override public ListenableFuture<?> submit(Runnable task) {
|
||||
return (ListenableFuture<?>) super.submit(task);
|
||||
}
|
||||
|
||||
@Override public <T> ListenableFuture<T> submit(Runnable task, T result) {
|
||||
return (ListenableFuture<T>) super.submit(task, result);
|
||||
}
|
||||
|
||||
@Override public <T> ListenableFuture<T> submit(Callable<T> task) {
|
||||
return (ListenableFuture<T>) super.submit(task);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link ExecutorService} whose {@code submit} and {@code
|
||||
* invokeAll} methods submit {@link ListenableFutureTask} instances to the
|
||||
* given delegate executor. Those methods, as well as {@code execute} and
|
||||
* {@code invokeAny}, are implemented in terms of calls to {@code
|
||||
* delegate.execute}. All other methods are forwarded unchanged to the
|
||||
* delegate. This implies that the returned {@code ListeningExecutorService}
|
||||
* never calls the delegate's {@code submit}, {@code invokeAll}, and {@code
|
||||
* invokeAny} methods, so any special handling of tasks must be implemented in
|
||||
* the delegate's {@code execute} method or by wrapping the returned {@code
|
||||
* ListeningExecutorService}.
|
||||
*
|
||||
* <p>If the delegate executor was already an instance of {@code
|
||||
* ListeningExecutorService}, it is returned untouched, and the rest of this
|
||||
* documentation does not apply.
|
||||
*
|
||||
* @since 10.0
|
||||
*/
|
||||
// public static ListeningExecutorService listeningDecorator(
|
||||
// ExecutorService delegate) {
|
||||
// return (delegate instanceof ListeningExecutorService)
|
||||
// ? (ListeningExecutorService) delegate
|
||||
// : (delegate instanceof ScheduledExecutorService)
|
||||
// ? new ScheduledListeningDecorator((ScheduledExecutorService) delegate)
|
||||
// : new ListeningDecorator(delegate);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Creates a {@link ScheduledExecutorService} whose {@code submit} and {@code
|
||||
* invokeAll} methods submit {@link ListenableFutureTask} instances to the
|
||||
* given delegate executor. Those methods, as well as {@code execute} and
|
||||
* {@code invokeAny}, are implemented in terms of calls to {@code
|
||||
* delegate.execute}. All other methods are forwarded unchanged to the
|
||||
* delegate. This implies that the returned {@code
|
||||
* ListeningScheduledExecutorService} never calls the delegate's {@code
|
||||
* submit}, {@code invokeAll}, and {@code invokeAny} methods, so any special
|
||||
* handling of tasks must be implemented in the delegate's {@code execute}
|
||||
* method or by wrapping the returned {@code
|
||||
* ListeningScheduledExecutorService}.
|
||||
*
|
||||
* <p>If the delegate executor was already an instance of {@code
|
||||
* ListeningScheduledExecutorService}, it is returned untouched, and the rest
|
||||
* of this documentation does not apply.
|
||||
*
|
||||
* @since 10.0
|
||||
*/
|
||||
// public static ListeningScheduledExecutorService listeningDecorator(
|
||||
// ScheduledExecutorService delegate) {
|
||||
// return (delegate instanceof ListeningScheduledExecutorService)
|
||||
// ? (ListeningScheduledExecutorService) delegate
|
||||
// : new ScheduledListeningDecorator(delegate);
|
||||
// }
|
||||
|
||||
// private static class ListeningDecorator
|
||||
// extends AbstractListeningExecutorService {
|
||||
// private final ExecutorService delegate;
|
||||
//
|
||||
// ListeningDecorator(ExecutorService delegate) {
|
||||
// this.delegate = checkNotNull(delegate);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean awaitTermination(long timeout, TimeUnit unit)
|
||||
// throws InterruptedException {
|
||||
// return delegate.awaitTermination(timeout, unit);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isShutdown() {
|
||||
// return delegate.isShutdown();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isTerminated() {
|
||||
// return delegate.isTerminated();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void shutdown() {
|
||||
// delegate.shutdown();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<Runnable> shutdownNow() {
|
||||
// return delegate.shutdownNow();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void execute(Runnable command) {
|
||||
// delegate.execute(command);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static class ScheduledListeningDecorator
|
||||
// extends ListeningDecorator implements ListeningScheduledExecutorService {
|
||||
//
|
||||
// final ScheduledExecutorService delegate;
|
||||
//
|
||||
// ScheduledListeningDecorator(ScheduledExecutorService delegate) {
|
||||
// super(delegate);
|
||||
// this.delegate = checkNotNull(delegate);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ListenableScheduledFuture<?> schedule(
|
||||
// Runnable command, long delay, TimeUnit unit) {
|
||||
// ListenableFutureTask<Void> task =
|
||||
// ListenableFutureTask.create(command, null);
|
||||
// ScheduledFuture<?> scheduled = delegate.schedule(task, delay, unit);
|
||||
// return new ListenableScheduledTask<Void>(task, scheduled);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <V> ListenableScheduledFuture<V> schedule(
|
||||
// Callable<V> callable, long delay, TimeUnit unit) {
|
||||
// ListenableFutureTask<V> task = ListenableFutureTask.create(callable);
|
||||
// ScheduledFuture<?> scheduled = delegate.schedule(task, delay, unit);
|
||||
// return new ListenableScheduledTask<V>(task, scheduled);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ListenableScheduledFuture<?> scheduleAtFixedRate(
|
||||
// Runnable command, long initialDelay, long period, TimeUnit unit) {
|
||||
// NeverSuccessfulListenableFutureTask task =
|
||||
// new NeverSuccessfulListenableFutureTask(command);
|
||||
// ScheduledFuture<?> scheduled =
|
||||
// delegate.scheduleAtFixedRate(task, initialDelay, period, unit);
|
||||
// return new ListenableScheduledTask<Void>(task, scheduled);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ListenableScheduledFuture<?> scheduleWithFixedDelay(
|
||||
// Runnable command, long initialDelay, long delay, TimeUnit unit) {
|
||||
// NeverSuccessfulListenableFutureTask task =
|
||||
// new NeverSuccessfulListenableFutureTask(command);
|
||||
// ScheduledFuture<?> scheduled =
|
||||
// delegate.scheduleWithFixedDelay(task, initialDelay, delay, unit);
|
||||
// return new ListenableScheduledTask<Void>(task, scheduled);
|
||||
// }
|
||||
//
|
||||
// private static final class ListenableScheduledTask<V>
|
||||
// extends SimpleForwardingListenableFuture<V>
|
||||
// implements ListenableScheduledFuture<V> {
|
||||
//
|
||||
// private final ScheduledFuture<?> scheduledDelegate;
|
||||
//
|
||||
// public ListenableScheduledTask(
|
||||
// ListenableFuture<V> listenableDelegate,
|
||||
// ScheduledFuture<?> scheduledDelegate) {
|
||||
// super(listenableDelegate);
|
||||
// this.scheduledDelegate = scheduledDelegate;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
// boolean cancelled = super.cancel(mayInterruptIfRunning);
|
||||
// if (cancelled) {
|
||||
// // Unless it is cancelled, the delegate may continue being scheduled
|
||||
// scheduledDelegate.cancel(mayInterruptIfRunning);
|
||||
//
|
||||
// // TODO(user): Cancel "this" if "scheduledDelegate" is cancelled.
|
||||
// }
|
||||
// return cancelled;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public long getDelay(TimeUnit unit) {
|
||||
// return scheduledDelegate.getDelay(unit);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int compareTo(Delayed other) {
|
||||
// return scheduledDelegate.compareTo(other);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static final class NeverSuccessfulListenableFutureTask
|
||||
// extends AbstractFuture<Void>
|
||||
// implements Runnable {
|
||||
// private final Runnable delegate;
|
||||
//
|
||||
// public NeverSuccessfulListenableFutureTask(Runnable delegate) {
|
||||
// this.delegate = checkNotNull(delegate);
|
||||
// }
|
||||
//
|
||||
// @Override public void run() {
|
||||
// try {
|
||||
// delegate.run();
|
||||
// } catch (Throwable t) {
|
||||
// setException(t);
|
||||
// throw Throwables.propagate(t);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/*
|
||||
* This following method is a modified version of one found in
|
||||
* http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck/AbstractExecutorServiceTest.java?revision=1.30
|
||||
* which contained the following notice:
|
||||
*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* Other contributors include Andrew Wright, Jeffrey Hayes,
|
||||
* Pat Fisher, Mike Judd.
|
||||
*/
|
||||
|
||||
/**
|
||||
* An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService}
|
||||
* implementations.
|
||||
*/ // static <T> T invokeAnyImpl(ListeningExecutorService executorService,
|
||||
// Collection<? extends Callable<T>> tasks, boolean timed, long nanos)
|
||||
// throws InterruptedException, ExecutionException, TimeoutException {
|
||||
// checkNotNull(executorService);
|
||||
// int ntasks = tasks.size();
|
||||
// checkArgument(ntasks > 0);
|
||||
// List<Future<T>> futures = Lists.newArrayListWithCapacity(ntasks);
|
||||
// BlockingQueue<Future<T>> futureQueue = Queues.newLinkedBlockingQueue();
|
||||
//
|
||||
// // For efficiency, especially in executors with limited
|
||||
// // parallelism, check to see if previously submitted tasks are
|
||||
// // done before submitting more of them. This interleaving
|
||||
// // plus the exception mechanics account for messiness of main
|
||||
// // loop.
|
||||
//
|
||||
// try {
|
||||
// // Record exceptions so that if we fail to obtain any
|
||||
// // result, we can throw the last exception we got.
|
||||
// ExecutionException ee = null;
|
||||
// long lastTime = timed ? System.nanoTime() : 0;
|
||||
// Iterator<? extends Callable<T>> it = tasks.iterator();
|
||||
//
|
||||
// futures.add(submitAndAddQueueListener(executorService, it.next(), futureQueue));
|
||||
// --ntasks;
|
||||
// int active = 1;
|
||||
//
|
||||
// for (;;) {
|
||||
// Future<T> f = futureQueue.poll();
|
||||
// if (f == null) {
|
||||
// if (ntasks > 0) {
|
||||
// --ntasks;
|
||||
// futures.add(submitAndAddQueueListener(executorService, it.next(), futureQueue));
|
||||
// ++active;
|
||||
// } else if (active == 0) {
|
||||
// break;
|
||||
// } else if (timed) {
|
||||
// f = futureQueue.poll(nanos, TimeUnit.NANOSECONDS);
|
||||
// if (f == null) {
|
||||
// throw new TimeoutException();
|
||||
// }
|
||||
// long now = System.nanoTime();
|
||||
// nanos -= now - lastTime;
|
||||
// lastTime = now;
|
||||
// } else {
|
||||
// f = futureQueue.take();
|
||||
// }
|
||||
// }
|
||||
// if (f != null) {
|
||||
// --active;
|
||||
// try {
|
||||
// return f.get();
|
||||
// } catch (ExecutionException eex) {
|
||||
// ee = eex;
|
||||
// } catch (RuntimeException rex) {
|
||||
// ee = new ExecutionException(rex);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (ee == null) {
|
||||
// ee = new ExecutionException(null);
|
||||
// }
|
||||
// throw ee;
|
||||
// } finally {
|
||||
// for (Future<T> f : futures) {
|
||||
// f.cancel(true);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Submits the task and adds a listener that adds the future to {@code queue} when it completes.
|
||||
*/
|
||||
// private static <T> ListenableFuture<T> submitAndAddQueueListener(
|
||||
// ListeningExecutorService executorService, Callable<T> task,
|
||||
// final BlockingQueue<Future<T>> queue) {
|
||||
// final ListenableFuture<T> future = executorService.submit(task);
|
||||
// future.addListener(new Runnable() {
|
||||
// @Override public void run() {
|
||||
// queue.add(future);
|
||||
// }
|
||||
// }, MoreExecutors.sameThreadExecutor());
|
||||
// return future;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns a default thread factory used to create new threads.
|
||||
*
|
||||
* <p>On AppEngine, returns {@code ThreadManager.currentRequestThreadFactory()}.
|
||||
* Otherwise, returns {@link Executors#defaultThreadFactory()}.
|
||||
*
|
||||
* @since 14.0
|
||||
*/
|
||||
// @Beta
|
||||
// public static ThreadFactory platformThreadFactory() {
|
||||
// if (!isAppEngine()) {
|
||||
// return Executors.defaultThreadFactory();
|
||||
// }
|
||||
// try {
|
||||
// return (ThreadFactory) Class.forName("com.google.appengine.api.ThreadManager")
|
||||
// .getMethod("currentRequestThreadFactory")
|
||||
// .invoke(null);
|
||||
// } catch (IllegalAccessException e) {
|
||||
// throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e);
|
||||
// } catch (ClassNotFoundException e) {
|
||||
// throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e);
|
||||
// } catch (NoSuchMethodException e) {
|
||||
// throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e);
|
||||
// } catch (InvocationTargetException e) {
|
||||
// throw Throwables.propagate(e.getCause());
|
||||
// }
|
||||
// }
|
||||
|
||||
// private static boolean isAppEngine() {
|
||||
// if (System.getProperty("com.google.appengine.runtime.environment") == null) {
|
||||
// return false;
|
||||
// }
|
||||
// try {
|
||||
// // If the current environment is null, we're not inside AppEngine.
|
||||
// return Class.forName("com.google.apphosting.api.ApiProxy")
|
||||
// .getMethod("getCurrentEnvironment")
|
||||
// .invoke(null) != null;
|
||||
// } catch (ClassNotFoundException e) {
|
||||
// // If ApiProxy doesn't exist, we're not on AppEngine at all.
|
||||
// return false;
|
||||
// } catch (InvocationTargetException e) {
|
||||
// // If ApiProxy throws an exception, we're not in a proper AppEngine environment.
|
||||
// return false;
|
||||
// } catch (IllegalAccessException e) {
|
||||
// // If the method isn't accessible, we're not on a supported version of AppEngine;
|
||||
// return false;
|
||||
// } catch (NoSuchMethodException e) {
|
||||
// // If the method doesn't exist, we're not on a supported version of AppEngine;
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Creates a thread using {@link #platformThreadFactory}, and sets its name to {@code name}
|
||||
* unless changing the name is forbidden by the security manager.
|
||||
*/
|
||||
// static Thread newThread(String name, Runnable runnable) {
|
||||
// checkNotNull(name);
|
||||
// checkNotNull(runnable);
|
||||
// Thread result = platformThreadFactory().newThread(runnable);
|
||||
// try {
|
||||
// result.setName(name);
|
||||
// } catch (SecurityException e) {
|
||||
// // OK if we can't set the name in this environment.
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// TODO(user): provide overloads for ListeningExecutorService? ListeningScheduledExecutorService?
|
||||
// TODO(user): provide overloads that take constant strings? Function<Runnable, String>s to
|
||||
// calculate names?
|
||||
|
||||
/**
|
||||
* Creates an {@link Executor} that renames the {@link Thread threads} that its tasks run in.
|
||||
*
|
||||
* <p>The names are retrieved from the {@code nameSupplier} on the thread that is being renamed
|
||||
* right before each task is run. The renaming is best effort, if a {@link SecurityManager}
|
||||
* prevents the renaming then it will be skipped but the tasks will still execute.
|
||||
*
|
||||
*
|
||||
* @param executor The executor to decorate
|
||||
* @param nameSupplier The source of names for each task
|
||||
*/
|
||||
// static Executor renamingDecorator(final Executor executor, final Supplier<String> nameSupplier) {
|
||||
// checkNotNull(executor);
|
||||
// checkNotNull(nameSupplier);
|
||||
// if (isAppEngine()) {
|
||||
// // AppEngine doesn't support thread renaming, so don't even try
|
||||
// return executor;
|
||||
// }
|
||||
// return new Executor() {
|
||||
// @Override public void execute(Runnable command) {
|
||||
// executor.execute(Callables.threadRenaming(command, nameSupplier));
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
/**
|
||||
* Creates an {@link ExecutorService} that renames the {@link Thread threads} that its tasks run
|
||||
* in.
|
||||
*
|
||||
* <p>The names are retrieved from the {@code nameSupplier} on the thread that is being renamed
|
||||
* right before each task is run. The renaming is best effort, if a {@link SecurityManager}
|
||||
* prevents the renaming then it will be skipped but the tasks will still execute.
|
||||
*
|
||||
*
|
||||
* @param service The executor to decorate
|
||||
* @param nameSupplier The source of names for each task
|
||||
*/
|
||||
// static ExecutorService renamingDecorator(final ExecutorService service,
|
||||
// final Supplier<String> nameSupplier) {
|
||||
// checkNotNull(service);
|
||||
// checkNotNull(nameSupplier);
|
||||
// if (isAppEngine()) {
|
||||
// // AppEngine doesn't support thread renaming, so don't even try.
|
||||
// return service;
|
||||
// }
|
||||
// return new WrappingExecutorService(service) {
|
||||
// @Override protected <T> Callable<T> wrapTask(Callable<T> callable) {
|
||||
// return Callables.threadRenaming(callable, nameSupplier);
|
||||
// }
|
||||
// @Override protected Runnable wrapTask(Runnable command) {
|
||||
// return Callables.threadRenaming(command, nameSupplier);
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
/**
|
||||
* Creates a {@link ScheduledExecutorService} that renames the {@link Thread threads} that its
|
||||
* tasks run in.
|
||||
*
|
||||
* <p>The names are retrieved from the {@code nameSupplier} on the thread that is being renamed
|
||||
* right before each task is run. The renaming is best effort, if a {@link SecurityManager}
|
||||
* prevents the renaming then it will be skipped but the tasks will still execute.
|
||||
*
|
||||
*
|
||||
* @param service The executor to decorate
|
||||
* @param nameSupplier The source of names for each task
|
||||
*/
|
||||
// static ScheduledExecutorService renamingDecorator(final ScheduledExecutorService service,
|
||||
// final Supplier<String> nameSupplier) {
|
||||
// checkNotNull(service);
|
||||
// checkNotNull(nameSupplier);
|
||||
// if (isAppEngine()) {
|
||||
// // AppEngine doesn't support thread renaming, so don't even try.
|
||||
// return service;
|
||||
// }
|
||||
// return new WrappingScheduledExecutorService(service) {
|
||||
// @Override protected <T> Callable<T> wrapTask(Callable<T> callable) {
|
||||
// return Callables.threadRenaming(callable, nameSupplier);
|
||||
// }
|
||||
// @Override protected Runnable wrapTask(Runnable command) {
|
||||
// return Callables.threadRenaming(command, nameSupplier);
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
/**
|
||||
* Shuts down the given executor gradually, first disabling new submissions and later cancelling
|
||||
* existing tasks.
|
||||
*
|
||||
* <p>The method takes the following steps:
|
||||
* <ol>
|
||||
* <li>calls {@link ExecutorService#shutdown()}, disabling acceptance of new submitted tasks.
|
||||
* <li>waits for half of the specified timeout.
|
||||
* <li>if the timeout expires, it calls {@link ExecutorService#shutdownNow()}, cancelling
|
||||
* pending tasks and interrupting running tasks.
|
||||
* <li>waits for the other half of the specified timeout.
|
||||
* </ol>
|
||||
*
|
||||
* <p>If, at any step of the process, the given executor is terminated or the calling thread is
|
||||
* interrupted, the method may return without executing any remaining steps.
|
||||
*
|
||||
* @param service the {@code ExecutorService} to shut down
|
||||
* @param timeout the maximum time to wait for the {@code ExecutorService} to terminate
|
||||
* @param unit the time unit of the timeout argument
|
||||
* @return {@code true) if the pool was terminated successfully, {@code false} if the
|
||||
* {@code ExecutorService} could not terminate <b>or</b> the thread running this method
|
||||
* is interrupted while waiting for the {@code ExecutorService} to terminate
|
||||
* @since 17.0
|
||||
*/
|
||||
// @Beta
|
||||
// public static boolean shutdownAndAwaitTermination(
|
||||
// ExecutorService service, long timeout, TimeUnit unit) {
|
||||
// checkNotNull(unit);
|
||||
// // Disable new tasks from being submitted
|
||||
// service.shutdown();
|
||||
// try {
|
||||
// long halfTimeoutNanos = TimeUnit.NANOSECONDS.convert(timeout, unit) / 2;
|
||||
// // Wait for half the duration of the timeout for existing tasks to terminate
|
||||
// if (!service.awaitTermination(halfTimeoutNanos, TimeUnit.NANOSECONDS)) {
|
||||
// // Cancel currently executing tasks
|
||||
// service.shutdownNow();
|
||||
// // Wait the other half of the timeout for tasks to respond to being cancelled
|
||||
// service.awaitTermination(halfTimeoutNanos, TimeUnit.NANOSECONDS);
|
||||
// }
|
||||
// } catch (InterruptedException ie) {
|
||||
// // Preserve interrupt status
|
||||
// Thread.currentThread().interrupt();
|
||||
// // (Re-)Cancel if current thread also interrupted
|
||||
// service.shutdownNow();
|
||||
// }
|
||||
// return service.isTerminated();
|
||||
// }
|
||||
}
|
176
java/src/game/future/ThreadFactoryBuilder.java
Normal file
176
java/src/game/future/ThreadFactoryBuilder.java
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* A ThreadFactory builder, providing any combination of these features:
|
||||
* <ul>
|
||||
* <li> whether threads should be marked as {@linkplain Thread#setDaemon daemon}
|
||||
* threads
|
||||
* <li> a {@linkplain ThreadFactoryBuilder#setNameFormat naming format}
|
||||
* <li> a {@linkplain Thread#setPriority thread priority}
|
||||
* <li> an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception
|
||||
* handler}
|
||||
* <li> a {@linkplain ThreadFactory#newThread backing thread factory}
|
||||
* </ul>
|
||||
* <p>If no backing thread factory is provided, a default backing thread factory is
|
||||
* used as if by calling {@code setThreadFactory(}{@link
|
||||
* Executors#defaultThreadFactory()}{@code )}.
|
||||
*
|
||||
* @author Kurt Alfred Kluever
|
||||
* @since 4.0
|
||||
*/
|
||||
public final class ThreadFactoryBuilder {
|
||||
private String nameFormat = null;
|
||||
private Boolean daemon = null;
|
||||
private Integer priority = null;
|
||||
private UncaughtExceptionHandler uncaughtExceptionHandler = null;
|
||||
private ThreadFactory backingThreadFactory = null;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ThreadFactory} builder.
|
||||
*/
|
||||
public ThreadFactoryBuilder() {}
|
||||
|
||||
/**
|
||||
* Sets the naming format to use when naming threads ({@link Thread#setName})
|
||||
* which are created with this ThreadFactory.
|
||||
*
|
||||
* @param nameFormat a {@link String#format(String, Object...)}-compatible
|
||||
* format String, to which a unique integer (0, 1, etc.) will be supplied
|
||||
* as the single parameter. This integer will be unique to the built
|
||||
* instance of the ThreadFactory and will be assigned sequentially. For
|
||||
* example, {@code "rpc-pool-%d"} will generate thread names like
|
||||
* {@code "rpc-pool-0"}, {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, etc.
|
||||
* @return this for the builder pattern
|
||||
*/
|
||||
|
||||
public ThreadFactoryBuilder setNameFormat(String nameFormat) {
|
||||
String.format(nameFormat, 0); // fail fast if the format is bad or null
|
||||
this.nameFormat = nameFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets daemon or not for new threads created with this ThreadFactory.
|
||||
*
|
||||
* @param daemon whether or not new Threads created with this ThreadFactory
|
||||
* will be daemon threads
|
||||
* @return this for the builder pattern
|
||||
*/
|
||||
public ThreadFactoryBuilder setDaemon(boolean daemon) {
|
||||
this.daemon = daemon;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the priority for new threads created with this ThreadFactory.
|
||||
*
|
||||
* @param priority the priority for new Threads created with this
|
||||
* ThreadFactory
|
||||
* @return this for the builder pattern
|
||||
*/
|
||||
// public ThreadFactoryBuilder setPriority(int priority) {
|
||||
// Thread#setPriority() already checks for validity. These error messages
|
||||
// are nicer though and will fail-fast.
|
||||
// checkArgument(priority >= Thread.MIN_PRIORITY,
|
||||
// "Thread priority (%s) must be >= %s", priority, Thread.MIN_PRIORITY);
|
||||
// checkArgument(priority <= Thread.MAX_PRIORITY,
|
||||
// "Thread priority (%s) must be <= %s", priority, Thread.MAX_PRIORITY);
|
||||
// this.priority = priority;
|
||||
// return this;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Sets the {@link UncaughtExceptionHandler} for new threads created with this
|
||||
* ThreadFactory.
|
||||
*
|
||||
* @param uncaughtExceptionHandler the uncaught exception handler for new
|
||||
* Threads created with this ThreadFactory
|
||||
* @return this for the builder pattern
|
||||
*/
|
||||
// public ThreadFactoryBuilder setUncaughtExceptionHandler(
|
||||
// UncaughtExceptionHandler uncaughtExceptionHandler) {
|
||||
// this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler);
|
||||
// return this;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Sets the backing {@link ThreadFactory} for new threads created with this
|
||||
* ThreadFactory. Threads will be created by invoking #newThread(Runnable) on
|
||||
* this backing {@link ThreadFactory}.
|
||||
*
|
||||
* @param backingThreadFactory the backing {@link ThreadFactory} which will
|
||||
* be delegated to during thread creation.
|
||||
* @return this for the builder pattern
|
||||
*
|
||||
* @see MoreExecutors
|
||||
*/
|
||||
// public ThreadFactoryBuilder setThreadFactory(
|
||||
// ThreadFactory backingThreadFactory) {
|
||||
// this.backingThreadFactory = checkNotNull(backingThreadFactory);
|
||||
// return this;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns a new thread factory using the options supplied during the building
|
||||
* process. After building, it is still possible to change the options used to
|
||||
* build the ThreadFactory and/or build again. State is not shared amongst
|
||||
* built instances.
|
||||
*
|
||||
* @return the fully constructed {@link ThreadFactory}
|
||||
*/
|
||||
public ThreadFactory build() {
|
||||
return build(this);
|
||||
}
|
||||
|
||||
private static ThreadFactory build(ThreadFactoryBuilder builder) {
|
||||
final String nameFormat = builder.nameFormat;
|
||||
final Boolean daemon = builder.daemon;
|
||||
final Integer priority = builder.priority;
|
||||
final UncaughtExceptionHandler uncaughtExceptionHandler =
|
||||
builder.uncaughtExceptionHandler;
|
||||
final ThreadFactory backingThreadFactory =
|
||||
(builder.backingThreadFactory != null)
|
||||
? builder.backingThreadFactory
|
||||
: Executors.defaultThreadFactory();
|
||||
final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null;
|
||||
return new ThreadFactory() {
|
||||
@Override public Thread newThread(Runnable runnable) {
|
||||
Thread thread = backingThreadFactory.newThread(runnable);
|
||||
if (nameFormat != null) {
|
||||
thread.setName(String.format(nameFormat, count.getAndIncrement()));
|
||||
}
|
||||
if (daemon != null) {
|
||||
thread.setDaemon(daemon);
|
||||
}
|
||||
if (priority != null) {
|
||||
thread.setPriority(priority);
|
||||
}
|
||||
if (uncaughtExceptionHandler != null) {
|
||||
thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
65
java/src/game/future/UncheckedExecutionException.java
Normal file
65
java/src/game/future/UncheckedExecutionException.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Guava Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package game.future;
|
||||
|
||||
/**
|
||||
* Unchecked variant of {@link java.util.concurrent.ExecutionException}. As with
|
||||
* {@code ExecutionException}, the exception's {@linkplain #getCause() cause}
|
||||
* comes from a failed task, possibly run in another thread.
|
||||
*
|
||||
* <p>{@code UncheckedExecutionException} is intended as an alternative to
|
||||
* {@code ExecutionException} when the exception thrown by a task is an
|
||||
* unchecked exception. However, it may also wrap a checked exception in some
|
||||
* cases.
|
||||
*
|
||||
* <p>When wrapping an {@code Error} from another thread, prefer {@link
|
||||
* ExecutionError}. When wrapping a checked exception, prefer {@code
|
||||
* ExecutionException}.
|
||||
*
|
||||
* @author Charles Fry
|
||||
* @since 10.0
|
||||
*/
|
||||
|
||||
class UncheckedExecutionException extends RuntimeException {
|
||||
/**
|
||||
* Creates a new instance with {@code null} as its detail message.
|
||||
*/
|
||||
protected UncheckedExecutionException() {}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given detail message.
|
||||
*/
|
||||
protected UncheckedExecutionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given detail message and cause.
|
||||
*/
|
||||
public UncheckedExecutionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given cause.
|
||||
*/
|
||||
public UncheckedExecutionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue