diff options
author | Yohann Roussel <yroussel@google.com> | 2014-03-19 16:25:37 +0100 |
---|---|---|
committer | Yohann Roussel <yroussel@google.com> | 2014-03-20 15:13:33 +0100 |
commit | 4eceb95409e844fdc33c9c706e1dc307bfd40303 (patch) | |
tree | ee9f4f3fc79f757c79081c336bce4f1782c6ccd8 /guava/src/com/google/common/util/concurrent | |
parent | 3d2402901b1a6462e2cf47a6fd09711f327961c3 (diff) | |
download | toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.zip toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.gz toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.bz2 |
Initial Jack import.
Change-Id: I953cf0a520195a7187d791b2885848ad0d5a9b43
Diffstat (limited to 'guava/src/com/google/common/util/concurrent')
46 files changed, 10698 insertions, 0 deletions
diff --git a/guava/src/com/google/common/util/concurrent/AbstractCheckedFuture.java b/guava/src/com/google/common/util/concurrent/AbstractCheckedFuture.java new file mode 100644 index 0000000..dfaf343 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AbstractCheckedFuture.java @@ -0,0 +1,117 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A delegating wrapper around a {@link ListenableFuture} that adds support for + * the {@link #checkedGet()} and {@link #checkedGet(long, TimeUnit)} methods. + * + * @author Sven Mawson + * @since 1.0 + */ +@Beta +public abstract class AbstractCheckedFuture<V, X extends Exception> + extends ForwardingListenableFuture.SimpleForwardingListenableFuture<V> + implements CheckedFuture<V, X> { + /** + * Constructs an {@code AbstractCheckedFuture} that wraps a delegate. + */ + protected AbstractCheckedFuture(ListenableFuture<V> delegate) { + super(delegate); + } + + /** + * Translates from an {@link InterruptedException}, + * {@link CancellationException} or {@link ExecutionException} thrown by + * {@code get} to an exception of type {@code X} to be thrown by + * {@code checkedGet}. Subclasses must implement this method. + * + * <p>If {@code e} is an {@code InterruptedException}, the calling + * {@code checkedGet} method has already restored the interrupt after catching + * the exception. If an implementation of {@link #mapException(Exception)} + * wishes to swallow the interrupt, it can do so by calling + * {@link Thread#interrupted()}. + * + * <p>Subclasses may choose to throw, rather than return, a subclass of + * {@code RuntimeException} to allow creating a CheckedFuture that throws + * both checked and unchecked exceptions. + */ + protected abstract X mapException(Exception e); + + /** + * {@inheritDoc} + * + * <p>This implementation calls {@link #get()} and maps that method's standard + * exceptions to instances of type {@code X} using {@link #mapException}. + * + * <p>In addition, if {@code get} throws an {@link InterruptedException}, this + * implementation will set the current thread's interrupt status before + * calling {@code mapException}. + * + * @throws X if {@link #get()} throws an {@link InterruptedException}, + * {@link CancellationException}, or {@link ExecutionException} + */ + @Override + public V checkedGet() throws X { + try { + return get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw mapException(e); + } catch (CancellationException e) { + throw mapException(e); + } catch (ExecutionException e) { + throw mapException(e); + } + } + + /** + * {@inheritDoc} + * + * <p>This implementation calls {@link #get(long, TimeUnit)} and maps that + * method's standard exceptions (excluding {@link TimeoutException}, which is + * propagated) to instances of type {@code X} using {@link #mapException}. + * + * <p>In addition, if {@code get} throws an {@link InterruptedException}, this + * implementation will set the current thread's interrupt status before + * calling {@code mapException}. + * + * @throws X if {@link #get()} throws an {@link InterruptedException}, + * {@link CancellationException}, or {@link ExecutionException} + * @throws TimeoutException {@inheritDoc} + */ + @Override + public V checkedGet(long timeout, TimeUnit unit) throws TimeoutException, X { + try { + return get(timeout, unit); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw mapException(e); + } catch (CancellationException e) { + throw mapException(e); + } catch (ExecutionException e) { + throw mapException(e); + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java b/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java new file mode 100644 index 0000000..6926c85 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Throwables; + +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Base class for services that can implement {@link #startUp}, {@link #run} and + * {@link #shutDown} methods. This class uses a single thread to execute the + * service; consider {@link AbstractService} if you would like to manage any + * threading manually. + * + * @author Jesse Wilson + * @since 1.0 + */ +@Beta +public abstract class AbstractExecutionThreadService implements Service { + private static final Logger logger = Logger.getLogger( + AbstractExecutionThreadService.class.getName()); + + /* use AbstractService for state management */ + private final Service delegate = new AbstractService() { + @Override protected final void doStart() { + executor().execute(new Runnable() { + @Override + public void run() { + try { + startUp(); + notifyStarted(); + + if (isRunning()) { + try { + AbstractExecutionThreadService.this.run(); + } catch (Throwable t) { + try { + shutDown(); + } catch (Exception ignored) { + logger.log(Level.WARNING, + "Error while attempting to shut down the service" + + " after failure.", ignored); + } + throw t; + } + } + + shutDown(); + notifyStopped(); + } catch (Throwable t) { + notifyFailed(t); + throw Throwables.propagate(t); + } + } + }); + } + + @Override protected void doStop() { + triggerShutdown(); + } + }; + + /** + * Constructor for use by subclasses. + */ + protected AbstractExecutionThreadService() {} + + /** + * Start the service. This method is invoked on the execution thread. + * + * <p>By default this method does nothing. + */ + protected void startUp() throws Exception {} + + /** + * Run the service. This method is invoked on the execution thread. + * Implementations must respond to stop requests. You could poll for lifecycle + * changes in a work loop: + * <pre> + * public void run() { + * while ({@link #isRunning()}) { + * // perform a unit of work + * } + * } + * </pre> + * ...or you could respond to stop requests by implementing {@link + * #triggerShutdown()}, which should cause {@link #run()} to return. + */ + protected abstract void run() throws Exception; + + /** + * Stop the service. This method is invoked on the execution thread. + * + * <p>By default this method does nothing. + */ + // TODO: consider supporting a TearDownTestCase-like API + protected void shutDown() throws Exception {} + + /** + * Invoked to request the service to stop. + * + * <p>By default this method does nothing. + */ + protected void triggerShutdown() {} + + /** + * Returns the {@link Executor} that will be used to run this service. + * Subclasses may override this method to use a custom {@link Executor}, which + * may configure its worker thread with a specific name, thread group or + * priority. The returned executor's {@link Executor#execute(Runnable) + * execute()} method is called when this service is started, and should return + * promptly. + * + * <p>The default implementation returns a new {@link Executor} that sets the + * name of its threads to the string returned by {@link #getServiceName} + */ + protected Executor executor() { + return new Executor() { + @Override + public void execute(Runnable command) { + new Thread(command, getServiceName()).start(); + } + }; + } + + @Override public String toString() { + return getServiceName() + " [" + state() + "]"; + } + + // We override instead of using ForwardingService so that these can be final. + + @Override public final ListenableFuture<State> start() { + return delegate.start(); + } + + @Override public final State startAndWait() { + return delegate.startAndWait(); + } + + @Override public final boolean isRunning() { + return delegate.isRunning(); + } + + @Override public final State state() { + return delegate.state(); + } + + @Override public final ListenableFuture<State> stop() { + return delegate.stop(); + } + + @Override public final State stopAndWait() { + return delegate.stopAndWait(); + } + + @Override public final void addListener(Listener listener, Executor executor) { + delegate.addListener(listener, executor); + } + + /** + * Returns the name of this service. {@link AbstractExecutionThreadService} + * may include the name in debugging output. + * + * <p>Subclasses may override this method. + * + * @since 10.0 + */ + protected String getServiceName() { + return getClass().getSimpleName(); + } +} diff --git a/guava/src/com/google/common/util/concurrent/AbstractFuture.java b/guava/src/com/google/common/util/concurrent/AbstractFuture.java new file mode 100644 index 0000000..28b2cc0 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AbstractFuture.java @@ -0,0 +1,371 @@ +/* + * 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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkNotNull; + +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; + +import javax.annotation.Nullable; + +/** + * 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 + */ +public 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()) { + 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() { + } + + /** + * {@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(@Nullable 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. + * @throws Error if the throwable was an {@link Error}. + */ + protected boolean setException(Throwable throwable) { + boolean result = sync.setException(checkNotNull(throwable)); + if (result) { + executionList.execute(); + } + + // If it's an Error, we want to make sure it reaches the top of the + // call stack, so we rethrow it. + if (throwable instanceof Error) { + throw (Error) throwable; + } + 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 either + * {@link #COMPLETED} or {@link #CANCELLED}. + * + * <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 or CANCELLED. + * + * <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; + + 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: + throw new CancellationException("Task was cancelled."); + + default: + throw new IllegalStateException( + "Error, synchronizer in invalid state: " + state); + } + } + + /** + * Checks if the state is {@link #COMPLETED} or {@link #CANCELLED}. + */ + boolean isDone() { + return (getState() & (COMPLETED | CANCELLED)) != 0; + } + + /** + * Checks if the state is {@link #CANCELLED}. + */ + boolean isCancelled() { + return getState() == CANCELLED; + } + + /** + * Transition to the COMPLETED state and set the value. + */ + boolean set(@Nullable 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 state. + */ + boolean cancel() { + return complete(null, null, 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} or {@link #CANCELLED}). + * + * @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(@Nullable V v, @Nullable 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; + this.exception = 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; + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/AbstractIdleService.java b/guava/src/com/google/common/util/concurrent/AbstractIdleService.java new file mode 100644 index 0000000..6d49ddd --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AbstractIdleService.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Throwables; + +import java.util.concurrent.Executor; + +/** + * Base class for services that do not need a thread while "running" + * but may need one during startup and shutdown. Subclasses can + * implement {@link #startUp} and {@link #shutDown} methods, each + * which run in a executor which by default uses a separate thread + * for each method. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@Beta +public abstract class AbstractIdleService implements Service { + + /* use AbstractService for state management */ + private final Service delegate = new AbstractService() { + @Override protected final void doStart() { + executor(State.STARTING).execute(new Runnable() { + @Override public void run() { + try { + startUp(); + notifyStarted(); + } catch (Throwable t) { + notifyFailed(t); + throw Throwables.propagate(t); + } + } + }); + } + + @Override protected final void doStop() { + executor(State.STOPPING).execute(new Runnable() { + @Override public void run() { + try { + shutDown(); + notifyStopped(); + } catch (Throwable t) { + notifyFailed(t); + throw Throwables.propagate(t); + } + } + }); + } + }; + + /** Start the service. */ + protected abstract void startUp() throws Exception; + + /** Stop the service. */ + protected abstract void shutDown() throws Exception; + + /** + * Returns the {@link Executor} that will be used to run this service. + * Subclasses may override this method to use a custom {@link Executor}, which + * may configure its worker thread with a specific name, thread group or + * priority. The returned executor's {@link Executor#execute(Runnable) + * execute()} method is called when this service is started and stopped, + * and should return promptly. + * + * @param state {@link Service.State#STARTING} or + * {@link Service.State#STOPPING}, used by the default implementation for + * naming the thread + */ + protected Executor executor(final State state) { + return new Executor() { + @Override + public void execute(Runnable command) { + new Thread(command, getServiceName() + " " + state).start(); + } + }; + } + + @Override public String toString() { + return getServiceName() + " [" + state() + "]"; + } + + // We override instead of using ForwardingService so that these can be final. + + @Override public final ListenableFuture<State> start() { + return delegate.start(); + } + + @Override public final State startAndWait() { + return delegate.startAndWait(); + } + + @Override public final boolean isRunning() { + return delegate.isRunning(); + } + + @Override public final State state() { + return delegate.state(); + } + + @Override public final ListenableFuture<State> stop() { + return delegate.stop(); + } + + @Override public final State stopAndWait() { + return delegate.stopAndWait(); + } + + @Override public final void addListener(Listener listener, Executor executor) { + delegate.addListener(listener, executor); + } + + private String getServiceName() { + return getClass().getSimpleName(); + } +} diff --git a/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java b/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java new file mode 100644 index 0000000..4e867a6 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java @@ -0,0 +1,163 @@ +/* + * This file is a modified version of + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/AbstractExecutorService.java?revision=1.35 + * 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/ + * + * Rationale for copying: + * Guava targets JDK5, whose AbstractExecutorService class lacks the newTaskFor protected + * customization methods needed by MoreExecutors.listeningDecorator. This class is a copy of + * AbstractExecutorService from the JSR166 CVS repository. It contains the desired methods. + */ + +package com.google.common.util.concurrent; + +import static com.google.common.util.concurrent.MoreExecutors.invokeAnyImpl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Implements {@link ListeningExecutorService} execution methods atop the abstract {@link #execute} + * method. More concretely, the {@code submit}, {@code invokeAny} and {@code invokeAll} methods + * create {@link ListenableFutureTask} instances and pass them to {@link #execute}. + * + * <p>In addition to {@link #execute}, subclasses must implement all methods related to shutdown and + * termination. + * + * @author Doug Lea + */ +abstract class AbstractListeningExecutorService implements ListeningExecutorService { + @Override public ListenableFuture<?> submit(Runnable task) { + ListenableFutureTask<Void> ftask = ListenableFutureTask.create(task, null); + execute(ftask); + return ftask; + } + + @Override public <T> ListenableFuture<T> submit(Runnable task, T result) { + ListenableFutureTask<T> ftask = ListenableFutureTask.create(task, result); + execute(ftask); + return ftask; + } + + @Override public <T> ListenableFuture<T> submit(Callable<T> task) { + ListenableFutureTask<T> ftask = ListenableFutureTask.create(task); + execute(ftask); + return ftask; + } + + @Override public <T> T invokeAny(Collection<? extends Callable<T>> tasks) + throws InterruptedException, ExecutionException { + try { + return invokeAnyImpl(this, tasks, false, 0); + } catch (TimeoutException cannotHappen) { + throw new AssertionError(); + } + } + + @Override public <T> T invokeAny( + Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return invokeAnyImpl(this, tasks, true, unit.toNanos(timeout)); + } + + @Override public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) + throws InterruptedException { + if (tasks == null) { + throw new NullPointerException(); + } + List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); + boolean done = false; + try { + for (Callable<T> t : tasks) { + ListenableFutureTask<T> f = ListenableFutureTask.create(t); + futures.add(f); + execute(f); + } + for (Future<T> f : futures) { + if (!f.isDone()) { + try { + f.get(); + } catch (CancellationException ignore) { + } catch (ExecutionException ignore) { + } + } + } + done = true; + return futures; + } finally { + if (!done) { + for (Future<T> f : futures) { + f.cancel(true); + } + } + } + } + + @Override public <T> List<Future<T>> invokeAll( + Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + if (tasks == null || unit == null) { + throw new NullPointerException(); + } + long nanos = unit.toNanos(timeout); + List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); + boolean done = false; + try { + for (Callable<T> t : tasks) { + futures.add(ListenableFutureTask.create(t)); + } + + long lastTime = System.nanoTime(); + + // Interleave time checks and calls to execute in case + // executor doesn't have any/much parallelism. + Iterator<Future<T>> it = futures.iterator(); + while (it.hasNext()) { + execute((Runnable) (it.next())); + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + if (nanos <= 0) { + return futures; + } + } + + for (Future<T> f : futures) { + if (!f.isDone()) { + if (nanos <= 0) { + return futures; + } + try { + f.get(nanos, TimeUnit.NANOSECONDS); + } catch (CancellationException ignore) { + } catch (ExecutionException ignore) { + } catch (TimeoutException toe) { + return futures; + } + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + } + } + done = true; + return futures; + } finally { + if (!done) { + for (Future<T> f : futures) { + f.cancel(true); + } + } + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java b/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java new file mode 100644 index 0000000..cfc7475 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java @@ -0,0 +1,446 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.concurrent.GuardedBy; + +/** + * Base class for services that can implement {@link #startUp} and {@link #shutDown} but while in + * the "running" state need to perform a periodic task. Subclasses can implement {@link #startUp}, + * {@link #shutDown} and also a {@link #runOneIteration} method that will be executed periodically. + * + * <p>This class uses the {@link ScheduledExecutorService} returned from {@link #executor} to run + * the {@link #startUp} and {@link #shutDown} methods and also uses that service to schedule the + * {@link #runOneIteration} that will be executed periodically as specified by its + * {@link Scheduler}. When this service is asked to stop via {@link #stop} or {@link #stopAndWait}, + * it will cancel the periodic task (but not interrupt it) and wait for it to stop before running + * the {@link #shutDown} method. + * + * <p>Subclasses are guaranteed that the life cycle methods ({@link #runOneIteration}, {@link + * #startUp} and {@link #shutDown}) will never run concurrently. Notably, if any execution of {@link + * #runOneIteration} takes longer than its schedule defines, then subsequent executions may start + * late. Also, all life cycle methods are executed with a lock held, so subclasses can safely + * modify shared state without additional synchronization necessary for visibility to later + * executions of the life cycle methods. + * + * <h3>Usage Example</h3> + * + * Here is a sketch of a service which crawls a website and uses the scheduling capabilities to + * rate limit itself. <pre> {@code + * class CrawlingService extends AbstractScheduledService { + * private Set<Uri> visited; + * private Queue<Uri> toCrawl; + * protected void startUp() throws Exception { + * toCrawl = readStartingUris(); + * } + * + * protected void runOneIteration() throws Exception { + * Uri uri = toCrawl.remove(); + * Collection<Uri> newUris = crawl(uri); + * visited.add(uri); + * for (Uri newUri : newUris) { + * if (!visited.contains(newUri)) { toCrawl.add(newUri); } + * } + * } + * + * protected void shutDown() throws Exception { + * saveUris(toCrawl); + * } + * + * protected Scheduler scheduler() { + * return Scheduler.newFixedRateSchedule(0, 1, TimeUnit.SECONDS); + * } + * }}</pre> + * + * This class uses the life cycle methods to read in a list of starting URIs and save the set of + * outstanding URIs when shutting down. Also, it takes advantage of the scheduling functionality to + * rate limit the number of queries we perform. + * + * @author Luke Sandberg + * @since 11.0 + */ +@Beta +public abstract class AbstractScheduledService implements Service { + private static final Logger logger = Logger.getLogger(AbstractScheduledService.class.getName()); + + /** + * A scheduler defines the policy for how the {@link AbstractScheduledService} should run its + * task. + * + * <p>Consider using the {@link #newFixedDelaySchedule} and {@link #newFixedRateSchedule} factory + * methods, these provide {@link Scheduler} instances for the common use case of running the + * service with a fixed schedule. If more flexibility is needed then consider subclassing the + * {@link CustomScheduler} abstract class in preference to creating your own {@link Scheduler} + * implementation. + * + * @author Luke Sandberg + * @since 11.0 + */ + public abstract static class Scheduler { + /** + * Returns a {@link Scheduler} that schedules the task using the + * {@link ScheduledExecutorService#scheduleWithFixedDelay} method. + * + * @param initialDelay the time to delay first execution + * @param delay the delay between the termination of one execution and the commencement of the + * next + * @param unit the time unit of the initialDelay and delay parameters + */ + public static Scheduler newFixedDelaySchedule(final long initialDelay, final long delay, + final TimeUnit unit) { + return new Scheduler() { + @Override + public Future<?> schedule(AbstractService service, ScheduledExecutorService executor, + Runnable task) { + return executor.scheduleWithFixedDelay(task, initialDelay, delay, unit); + } + }; + } + + /** + * Returns a {@link Scheduler} that schedules the task using the + * {@link ScheduledExecutorService#scheduleAtFixedRate} method. + * + * @param initialDelay the time to delay first execution + * @param period the period between successive executions of the task + * @param unit the time unit of the initialDelay and period parameters + */ + public static Scheduler newFixedRateSchedule(final long initialDelay, final long period, + final TimeUnit unit) { + return new Scheduler() { + @Override + public Future<?> schedule(AbstractService service, ScheduledExecutorService executor, + Runnable task) { + return executor.scheduleAtFixedRate(task, initialDelay, period, unit); + } + }; + } + + /** Schedules the task to run on the provided executor on behalf of the service. */ + abstract Future<?> schedule(AbstractService service, ScheduledExecutorService executor, + Runnable runnable); + + private Scheduler() {} + } + + /* use AbstractService for state management */ + private final AbstractService delegate = new AbstractService() { + + // A handle to the running task so that we can stop it when a shutdown has been requested. + // These two fields are volatile because their values will be accessed from multiple threads. + private volatile Future<?> runningTask; + private volatile ScheduledExecutorService executorService; + + // This lock protects the task so we can ensure that none of the template methods (startUp, + // shutDown or runOneIteration) run concurrently with one another. + private final ReentrantLock lock = new ReentrantLock(); + + private final Runnable task = new Runnable() { + @Override public void run() { + lock.lock(); + try { + AbstractScheduledService.this.runOneIteration(); + } catch (Throwable t) { + try { + shutDown(); + } catch (Exception ignored) { + logger.log(Level.WARNING, + "Error while attempting to shut down the service after failure.", ignored); + } + notifyFailed(t); + throw Throwables.propagate(t); + } finally { + lock.unlock(); + } + } + }; + + @Override protected final void doStart() { + executorService = executor(); + executorService.execute(new Runnable() { + @Override public void run() { + lock.lock(); + try { + startUp(); + runningTask = scheduler().schedule(delegate, executorService, task); + notifyStarted(); + } catch (Throwable t) { + notifyFailed(t); + throw Throwables.propagate(t); + } finally { + lock.unlock(); + } + } + }); + } + + @Override protected final void doStop() { + runningTask.cancel(false); + executorService.execute(new Runnable() { + @Override public void run() { + try { + lock.lock(); + try { + if (state() != State.STOPPING) { + // This means that the state has changed since we were scheduled. This implies that + // an execution of runOneIteration has thrown an exception and we have transitioned + // to a failed state, also this means that shutDown has already been called, so we + // do not want to call it again. + return; + } + shutDown(); + } finally { + lock.unlock(); + } + notifyStopped(); + } catch (Throwable t) { + notifyFailed(t); + throw Throwables.propagate(t); + } + } + }); + } + }; + + /** + * Run one iteration of the scheduled task. If any invocation of this method throws an exception, + * the service will transition to the {@link Service.State#FAILED} state and this method will no + * longer be called. + */ + protected abstract void runOneIteration() throws Exception; + + /** + * Start the service. + * + * <p>By default this method does nothing. + */ + protected void startUp() throws Exception {} + + /** + * Stop the service. This is guaranteed not to run concurrently with {@link #runOneIteration}. + * + * <p>By default this method does nothing. + */ + protected void shutDown() throws Exception {} + + /** + * Returns the {@link Scheduler} object used to configure this service. This method will only be + * called once. + */ + protected abstract Scheduler scheduler(); + + /** + * Returns the {@link ScheduledExecutorService} that will be used to execute the {@link #startUp}, + * {@link #runOneIteration} and {@link #shutDown} methods. The executor will not be + * {@link ScheduledExecutorService#shutdown} when this service stops. Subclasses may override this + * method to use a custom {@link ScheduledExecutorService} instance. + * + * <p>By default this returns a new {@link ScheduledExecutorService} with a single thread thread + * pool. This method will only be called once. + */ + protected ScheduledExecutorService executor() { + return Executors.newSingleThreadScheduledExecutor(); + } + + @Override public String toString() { + return getClass().getSimpleName() + " [" + state() + "]"; + } + + // We override instead of using ForwardingService so that these can be final. + + @Override public final ListenableFuture<State> start() { + return delegate.start(); + } + + @Override public final State startAndWait() { + return delegate.startAndWait(); + } + + @Override public final boolean isRunning() { + return delegate.isRunning(); + } + + @Override public final State state() { + return delegate.state(); + } + + @Override public final ListenableFuture<State> stop() { + return delegate.stop(); + } + + @Override public final State stopAndWait() { + return delegate.stopAndWait(); + } + + @Override public final void addListener(Listener listener, Executor executor) { + delegate.addListener(listener, executor); + } + + /** + * A {@link Scheduler} that provides a convenient way for the {@link AbstractScheduledService} to + * use a dynamically changing schedule. After every execution of the task, assuming it hasn't + * been cancelled, the {@link #getNextSchedule} method will be called. + * + * @author Luke Sandberg + * @since 11.0 + */ + @Beta + public abstract static class CustomScheduler extends Scheduler { + + /** + * A callable class that can reschedule itself using a {@link CustomScheduler}. + */ + private class ReschedulableCallable extends ForwardingFuture<Void> implements Callable<Void> { + + /** The underlying task. */ + private final Runnable wrappedRunnable; + + /** The executor on which this Callable will be scheduled. */ + private final ScheduledExecutorService executor; + + /** + * The service that is managing this callable. This is used so that failure can be + * reported properly. + */ + private final AbstractService service; + + /** + * This lock is used to ensure safe and correct cancellation, it ensures that a new task is + * not scheduled while a cancel is ongoing. Also it protects the currentFuture variable to + * ensure that it is assigned atomically with being scheduled. + */ + private final ReentrantLock lock = new ReentrantLock(); + + /** The future that represents the next execution of this task.*/ + @GuardedBy("lock") + private Future<Void> currentFuture; + + ReschedulableCallable(AbstractService service, ScheduledExecutorService executor, + Runnable runnable) { + this.wrappedRunnable = runnable; + this.executor = executor; + this.service = service; + } + + @Override + public Void call() throws Exception { + wrappedRunnable.run(); + reschedule(); + return null; + } + + /** + * Atomically reschedules this task and assigns the new future to {@link #currentFuture}. + */ + public void reschedule() { + // We reschedule ourselves with a lock held for two reasons. 1. we want to make sure that + // cancel calls cancel on the correct future. 2. we want to make sure that the assignment + // to currentFuture doesn't race with itself so that currentFuture is assigned in the + // correct order. + lock.lock(); + try { + if (currentFuture == null || !currentFuture.isCancelled()) { + final Schedule schedule = CustomScheduler.this.getNextSchedule(); + currentFuture = executor.schedule(this, schedule.delay, schedule.unit); + } + } catch (Throwable e) { + // If an exception is thrown by the subclass then we need to make sure that the service + // notices and transitions to the FAILED state. We do it by calling notifyFailed directly + // because the service does not monitor the state of the future so if the exception is not + // caught and forwarded to the service the task would stop executing but the service would + // have no idea. + service.notifyFailed(e); + } finally { + lock.unlock(); + } + } + + // N.B. Only protect cancel and isCancelled because those are the only methods that are + // invoked by the AbstractScheduledService. + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + // Ensure that a task cannot be rescheduled while a cancel is ongoing. + lock.lock(); + try { + return currentFuture.cancel(mayInterruptIfRunning); + } finally { + lock.unlock(); + } + } + + @Override + protected Future<Void> delegate() { + throw new UnsupportedOperationException("Only cancel is supported by this future"); + } + } + + @Override + final Future<?> schedule(AbstractService service, ScheduledExecutorService executor, + Runnable runnable) { + ReschedulableCallable task = new ReschedulableCallable(service, executor, runnable); + task.reschedule(); + return task; + } + + /** + * A value object that represents an absolute delay until a task should be invoked. + * + * @author Luke Sandberg + * @since 11.0 + */ + @Beta + protected static final class Schedule { + + private final long delay; + private final TimeUnit unit; + + /** + * @param delay the time from now to delay execution + * @param unit the time unit of the delay parameter + */ + public Schedule(long delay, TimeUnit unit) { + this.delay = delay; + this.unit = Preconditions.checkNotNull(unit); + } + } + + /** + * Calculates the time at which to next invoke the task. + * + * <p>This is guaranteed to be called immediately after the task has completed an iteration and + * on the same thread as the previous execution of {@link + * AbstractScheduledService#runOneIteration}. + * + * @return a schedule that defines the delay before the next execution. + */ + protected abstract Schedule getNextSchedule() throws Exception; + } +} diff --git a/guava/src/com/google/common/util/concurrent/AbstractService.java b/guava/src/com/google/common/util/concurrent/AbstractService.java new file mode 100644 index 0000000..a2e14fe --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AbstractService.java @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.Beta; +import com.google.common.collect.Lists; +import com.google.common.collect.Queues; +import com.google.common.util.concurrent.Service.State; // javadoc needs this + +import java.util.List; +import java.util.Queue; +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.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.Immutable; + +/** + * Base class for implementing services that can handle {@link #doStart} and {@link #doStop} + * requests, responding to them with {@link #notifyStarted()} and {@link #notifyStopped()} + * callbacks. Its subclasses must manage threads manually; consider + * {@link AbstractExecutionThreadService} if you need only a single execution thread. + * + * @author Jesse Wilson + * @author Luke Sandberg + * @since 1.0 + */ +@Beta +public abstract class AbstractService implements Service { + private static final Logger logger = Logger.getLogger(AbstractService.class.getName()); + private final ReentrantLock lock = new ReentrantLock(); + + private final Transition startup = new Transition(); + private final Transition shutdown = new Transition(); + + /** + * The listeners to notify during a state transition. + */ + @GuardedBy("lock") + private final List<ListenerExecutorPair> listeners = Lists.newArrayList(); + + /** + * The queue of listeners that are waiting to be executed. + * + * <p>Enqueue operations should be protected by {@link #lock} while dequeue operations should be + * protected by the implicit lock on this object. Dequeue operations should be executed atomically + * with the execution of the {@link Runnable} and additionally the {@link #lock} should not be + * held when the listeners are being executed. Use {@link #executeListeners} for this operation. + * This is necessary to ensure that elements on the queue are executed in the correct order. + * Enqueue operations should be protected so that listeners are added in the correct order. We use + * a concurrent queue implementation so that enqueues can be executed concurrently with dequeues. + */ + @GuardedBy("queuedListeners") + private final Queue<Runnable> queuedListeners = Queues.newConcurrentLinkedQueue(); + + /** + * The current state of the service. This should be written with the lock held but can be read + * without it because it is an immutable object in a volatile field. This is desirable so that + * methods like {@link #state}, {@link #failureCause} and notably {@link #toString} can be run + * without grabbing the lock. + * + * <p>To update this field correctly the lock must be held to guarantee that the state is + * consistent. + */ + @GuardedBy("lock") + private volatile StateSnapshot snapshot = new StateSnapshot(State.NEW); + + protected AbstractService() { + // Add a listener to update the futures. This needs to be added first so that it is executed + // before the other listeners. This way the other listeners can access the completed futures. + addListener( + new Listener() { + @Override public void starting() {} + + @Override public void running() { + startup.set(State.RUNNING); + } + + @Override public void stopping(State from) { + if (from == State.STARTING) { + startup.set(State.STOPPING); + } + } + + @Override public void terminated(State from) { + if (from == State.NEW) { + startup.set(State.TERMINATED); + } + shutdown.set(State.TERMINATED); + } + + @Override public void failed(State from, Throwable failure) { + switch (from) { + case STARTING: + startup.setException(failure); + shutdown.setException(new Exception("Service failed to start.", failure)); + break; + case RUNNING: + shutdown.setException(new Exception("Service failed while running", failure)); + break; + case STOPPING: + shutdown.setException(failure); + break; + case TERMINATED: /* fall-through */ + case FAILED: /* fall-through */ + case NEW: /* fall-through */ + default: + throw new AssertionError("Unexpected from state: " + from); + } + } + }, + MoreExecutors.sameThreadExecutor()); + } + + /** + * This method is called by {@link #start} to initiate service startup. The invocation of this + * method should cause a call to {@link #notifyStarted()}, either during this method's run, or + * after it has returned. If startup fails, the invocation should cause a call to + * {@link #notifyFailed(Throwable)} instead. + * + * <p>This method should return promptly; prefer to do work on a different thread where it is + * convenient. It is invoked exactly once on service startup, even when {@link #start} is called + * multiple times. + */ + protected abstract void doStart(); + + /** + * This method should be used to initiate service shutdown. The invocation of this method should + * cause a call to {@link #notifyStopped()}, either during this method's run, or after it has + * returned. If shutdown fails, the invocation should cause a call to + * {@link #notifyFailed(Throwable)} instead. + * + * <p> This method should return promptly; prefer to do work on a different thread where it is + * convenient. It is invoked exactly once on service shutdown, even when {@link #stop} is called + * multiple times. + */ + protected abstract void doStop(); + + @Override + public final ListenableFuture<State> start() { + lock.lock(); + try { + if (snapshot.state == State.NEW) { + snapshot = new StateSnapshot(State.STARTING); + starting(); + doStart(); + } + } catch (Throwable startupFailure) { + notifyFailed(startupFailure); + } finally { + lock.unlock(); + executeListeners(); + } + + return startup; + } + + @Override + public final ListenableFuture<State> stop() { + lock.lock(); + try { + switch (snapshot.state) { + case NEW: + snapshot = new StateSnapshot(State.TERMINATED); + terminated(State.NEW); + break; + case STARTING: + snapshot = new StateSnapshot(State.STARTING, true, null); + stopping(State.STARTING); + break; + case RUNNING: + snapshot = new StateSnapshot(State.STOPPING); + stopping(State.RUNNING); + doStop(); + break; + case STOPPING: + case TERMINATED: + case FAILED: + // do nothing + break; + default: + throw new AssertionError("Unexpected state: " + snapshot.state); + } + } catch (Throwable shutdownFailure) { + notifyFailed(shutdownFailure); + } finally { + lock.unlock(); + executeListeners(); + } + + return shutdown; + } + + @Override + public State startAndWait() { + return Futures.getUnchecked(start()); + } + + @Override + public State stopAndWait() { + return Futures.getUnchecked(stop()); + } + + /** + * Implementing classes should invoke this method once their service has started. It will cause + * the service to transition from {@link State#STARTING} to {@link State#RUNNING}. + * + * @throws IllegalStateException if the service is not {@link State#STARTING}. + */ + protected final void notifyStarted() { + lock.lock(); + try { + if (snapshot.state != State.STARTING) { + IllegalStateException failure = new IllegalStateException( + "Cannot notifyStarted() when the service is " + snapshot.state); + notifyFailed(failure); + throw failure; + } + + if (snapshot.shutdownWhenStartupFinishes) { + snapshot = new StateSnapshot(State.STOPPING); + // We don't call listeners here because we already did that when we set the + // shutdownWhenStartupFinishes flag. + doStop(); + } else { + snapshot = new StateSnapshot(State.RUNNING); + running(); + } + } finally { + lock.unlock(); + executeListeners(); + } + } + + /** + * Implementing classes should invoke this method once their service has stopped. It will cause + * the service to transition from {@link State#STOPPING} to {@link State#TERMINATED}. + * + * @throws IllegalStateException if the service is neither {@link State#STOPPING} nor + * {@link State#RUNNING}. + */ + protected final void notifyStopped() { + lock.lock(); + try { + if (snapshot.state != State.STOPPING && snapshot.state != State.RUNNING) { + IllegalStateException failure = new IllegalStateException( + "Cannot notifyStopped() when the service is " + snapshot.state); + notifyFailed(failure); + throw failure; + } + State previous = snapshot.state; + snapshot = new StateSnapshot(State.TERMINATED); + terminated(previous); + } finally { + lock.unlock(); + executeListeners(); + } + } + + /** + * Invoke this method to transition the service to the {@link State#FAILED}. The service will + * <b>not be stopped</b> if it is running. Invoke this method when a service has failed critically + * or otherwise cannot be started nor stopped. + */ + protected final void notifyFailed(Throwable cause) { + checkNotNull(cause); + + lock.lock(); + try { + switch (snapshot.state) { + case NEW: + case TERMINATED: + throw new IllegalStateException("Failed while in state:" + snapshot.state, cause); + case RUNNING: + case STARTING: + case STOPPING: + State previous = snapshot.state; + snapshot = new StateSnapshot(State.FAILED, false, cause); + failed(previous, cause); + break; + case FAILED: + // Do nothing + break; + default: + throw new AssertionError("Unexpected state: " + snapshot.state); + } + } finally { + lock.unlock(); + executeListeners(); + } + } + + @Override + public final boolean isRunning() { + return state() == State.RUNNING; + } + + @Override + public final State state() { + return snapshot.externalState(); + } + + @Override + public final void addListener(Listener listener, Executor executor) { + checkNotNull(listener, "listener"); + checkNotNull(executor, "executor"); + lock.lock(); + try { + if (snapshot.state != State.TERMINATED && snapshot.state != State.FAILED) { + listeners.add(new ListenerExecutorPair(listener, executor)); + } + } finally { + lock.unlock(); + } + } + + @Override public String toString() { + return getClass().getSimpleName() + " [" + state() + "]"; + } + + /** + * A change from one service state to another, plus the result of the change. + */ + private class Transition extends AbstractFuture<State> { + @Override + public State get(long timeout, TimeUnit unit) + throws InterruptedException, TimeoutException, ExecutionException { + try { + return super.get(timeout, unit); + } catch (TimeoutException e) { + throw new TimeoutException(AbstractService.this.toString()); + } + } + } + + /** + * Attempts to execute all the listeners in {@link #queuedListeners} while not holding the + * {@link #lock}. + */ + private void executeListeners() { + if (!lock.isHeldByCurrentThread()) { + synchronized (queuedListeners) { + Runnable listener; + while ((listener = queuedListeners.poll()) != null) { + listener.run(); + } + } + } + } + + @GuardedBy("lock") + private void starting() { + for (final ListenerExecutorPair pair : listeners) { + queuedListeners.add(new Runnable() { + @Override public void run() { + pair.execute(new Runnable() { + @Override public void run() { + pair.listener.starting(); + } + }); + } + }); + } + } + + @GuardedBy("lock") + private void running() { + for (final ListenerExecutorPair pair : listeners) { + queuedListeners.add(new Runnable() { + @Override public void run() { + pair.execute(new Runnable() { + @Override public void run() { + pair.listener.running(); + } + }); + } + }); + } + } + + @GuardedBy("lock") + private void stopping(final State from) { + for (final ListenerExecutorPair pair : listeners) { + queuedListeners.add(new Runnable() { + @Override public void run() { + pair.execute(new Runnable() { + @Override public void run() { + pair.listener.stopping(from); + } + }); + } + }); + } + } + + @GuardedBy("lock") + private void terminated(final State from) { + for (final ListenerExecutorPair pair : listeners) { + queuedListeners.add(new Runnable() { + @Override public void run() { + pair.execute(new Runnable() { + @Override public void run() { + pair.listener.terminated(from); + } + }); + } + }); + } + // There are no more state transitions so we can clear this out. + listeners.clear(); + } + + @GuardedBy("lock") + private void failed(final State from, final Throwable cause) { + for (final ListenerExecutorPair pair : listeners) { + queuedListeners.add(new Runnable() { + @Override public void run() { + pair.execute(new Runnable() { + @Override public void run() { + pair.listener.failed(from, cause); + } + }); + } + }); + } + // There are no more state transitions so we can clear this out. + listeners.clear(); + } + + /** A simple holder for a listener and its executor. */ + private static class ListenerExecutorPair { + final Listener listener; + final Executor executor; + + ListenerExecutorPair(Listener listener, Executor executor) { + this.listener = listener; + this.executor = executor; + } + + /** + * Executes the given {@link Runnable} on {@link #executor} logging and swallowing all + * exceptions + */ + void execute(Runnable runnable) { + try { + executor.execute(runnable); + } catch (Exception e) { + logger.log(Level.SEVERE, "Exception while executing listener " + listener + + " with executor " + executor, e); + } + } + } + + /** + * An immutable snapshot of the current state of the service. This class represents a consistent + * snapshot of the state and therefore it can be used to answer simple queries without needing to + * grab a lock. + */ + @Immutable + private static final class StateSnapshot { + /** + * The internal state, which equals external state unless + * shutdownWhenStartupFinishes is true. + */ + final State state; + + /** + * If true, the user requested a shutdown while the service was still starting + * up. + */ + final boolean shutdownWhenStartupFinishes; + + /** + * The exception that caused this service to fail. This will be {@code null} + * unless the service has failed. + */ + @Nullable + final Throwable failure; + + StateSnapshot(State internalState) { + this(internalState, false, null); + } + + StateSnapshot(State internalState, boolean shutdownWhenStartupFinishes, Throwable failure) { + checkArgument(!shutdownWhenStartupFinishes || internalState == State.STARTING, + "shudownWhenStartupFinishes can only be set if state is STARTING. Got %s instead.", + internalState); + checkArgument(!(failure != null ^ internalState == State.FAILED), + "A failure cause should be set if and only if the state is failed. Got %s and %s " + + "instead.", internalState, failure); + this.state = internalState; + this.shutdownWhenStartupFinishes = shutdownWhenStartupFinishes; + this.failure = failure; + } + + /** @see Service#state() */ + State externalState() { + if (shutdownWhenStartupFinishes && state == State.STARTING) { + return State.STOPPING; + } else { + return state; + } + } + + /** @see Service#failureCause() */ + Throwable failureCause() { + checkState(state == State.FAILED, + "failureCause() is only valid if the service has failed, service is %s", state); + return failure; + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/AsyncFunction.java b/guava/src/com/google/common/util/concurrent/AsyncFunction.java new file mode 100644 index 0000000..441c029 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AsyncFunction.java @@ -0,0 +1,41 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.Future; + +/** + * Transforms a value, possibly asynchronously. For an example usage and more + * information, see {@link Futures#transform(ListenableFuture, AsyncFunction)}. + * + * @author Chris Povirk + * @since 11.0 + */ +@Beta +public interface AsyncFunction<I, O> { + /** + * Returns an output {@code Future} to use in place of the given {@code + * input}. The output {@code Future} need not be {@linkplain Future#isDone + * done}, making {@code AsyncFunction} suitable for asynchronous derivations. + * + * <p>Throwing an exception from this method is equivalent to returning a + * failing {@code Future}. + */ + ListenableFuture<O> apply(I input) throws Exception; +} diff --git a/guava/src/com/google/common/util/concurrent/AtomicDouble.java b/guava/src/com/google/common/util/concurrent/AtomicDouble.java new file mode 100644 index 0000000..615d7a9 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AtomicDouble.java @@ -0,0 +1,257 @@ +/* + * Written by Doug Lea and Martin Buchholz 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/ + */ + +/* + * Source: + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/extra/AtomicDouble.java?revision=1.13 + * (Modified to adapt to guava coding conventions and + * to use AtomicLongFieldUpdater instead of sun.misc.Unsafe) + */ + +package com.google.common.util.concurrent; + +import static java.lang.Double.doubleToRawLongBits; +import static java.lang.Double.longBitsToDouble; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.atomic.AtomicLongFieldUpdater; + +/** + * A {@code double} value that may be updated atomically. See the + * {@link java.util.concurrent.atomic} package specification for + * description of the properties of atomic variables. An {@code + * AtomicDouble} is used in applications such as atomic accumulation, + * and cannot be used as a replacement for a {@link Double}. However, + * this class does extend {@code Number} to allow uniform access by + * tools and utilities that deal with numerically-based classes. + * + * <p><a name="bitEquals">This class compares primitive {@code double} + * values in methods such as {@link #compareAndSet} by comparing their + * bitwise representation using {@link Double#doubleToRawLongBits}, + * which differs from both the primitive double {@code ==} operator + * and from {@link Double#equals}, as if implemented by: + * <pre> {@code + * static boolean bitEquals(double x, double y) { + * long xBits = Double.doubleToRawLongBits(x); + * long yBits = Double.doubleToRawLongBits(y); + * return xBits == yBits; + * }}</pre> + * + * <p>It is possible to write a more scalable updater, at the cost of + * giving up strict atomicity. See for example + * <a href="http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/DoubleAdder.html" + * DoubleAdder> + * and + * <a href="http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/DoubleMaxUpdater.html" + * DoubleMaxUpdater>. + * + * @author Doug Lea + * @author Martin Buchholz + * @since 11.0 + */ +@Beta +public class AtomicDouble extends Number implements java.io.Serializable { + private static final long serialVersionUID = 0L; + + private transient volatile long value; + + private static final AtomicLongFieldUpdater<AtomicDouble> updater = + AtomicLongFieldUpdater.newUpdater(AtomicDouble.class, "value"); + + /** + * Creates a new {@code AtomicDouble} with the given initial value. + * + * @param initialValue the initial value + */ + public AtomicDouble(double initialValue) { + value = doubleToRawLongBits(initialValue); + } + + /** + * Creates a new {@code AtomicDouble} with initial value {@code 0.0}. + */ + public AtomicDouble() { + // assert doubleToRawLongBits(0.0) == 0L; + } + + /** + * Gets the current value. + * + * @return the current value + */ + public final double get() { + return longBitsToDouble(value); + } + + /** + * Sets to the given value. + * + * @param newValue the new value + */ + public final void set(double newValue) { + long next = doubleToRawLongBits(newValue); + value = next; + } + + /** + * Eventually sets to the given value. + * + * @param newValue the new value + */ + public final void lazySet(double newValue) { + set(newValue); + // TODO(user): replace with code below when jdk5 support is dropped. + // long next = doubleToRawLongBits(newValue); + // updater.lazySet(this, next); + } + + /** + * Atomically sets to the given value and returns the old value. + * + * @param newValue the new value + * @return the previous value + */ + public final double getAndSet(double newValue) { + long next = doubleToRawLongBits(newValue); + return longBitsToDouble(updater.getAndSet(this, next)); + } + + /** + * Atomically sets the value to the given updated value + * if the current value is <a href="#bitEquals">bitwise equal</a> + * to the expected value. + * + * @param expect the expected value + * @param update the new value + * @return {@code true} if successful. False return indicates that + * the actual value was not bitwise equal to the expected value. + */ + public final boolean compareAndSet(double expect, double update) { + return updater.compareAndSet(this, + doubleToRawLongBits(expect), + doubleToRawLongBits(update)); + } + + /** + * Atomically sets the value to the given updated value + * if the current value is <a href="#bitEquals">bitwise equal</a> + * to the expected value. + * + * <p>May <a + * href="http://download.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html#Spurious"> + * fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * + * @param expect the expected value + * @param update the new value + * @return {@code true} if successful + */ + public final boolean weakCompareAndSet(double expect, double update) { + return updater.weakCompareAndSet(this, + doubleToRawLongBits(expect), + doubleToRawLongBits(update)); + } + + /** + * Atomically adds the given value to the current value. + * + * @param delta the value to add + * @return the previous value + */ + public final double getAndAdd(double delta) { + while (true) { + long current = value; + double currentVal = longBitsToDouble(current); + double nextVal = currentVal + delta; + long next = doubleToRawLongBits(nextVal); + if (updater.compareAndSet(this, current, next)) { + return currentVal; + } + } + } + + /** + * Atomically adds the given value to the current value. + * + * @param delta the value to add + * @return the updated value + */ + public final double addAndGet(double delta) { + while (true) { + long current = value; + double currentVal = longBitsToDouble(current); + double nextVal = currentVal + delta; + long next = doubleToRawLongBits(nextVal); + if (updater.compareAndSet(this, current, next)) { + return nextVal; + } + } + } + + /** + * Returns the String representation of the current value. + * @return the String representation of the current value + */ + public String toString() { + return Double.toString(get()); + } + + /** + * Returns the value of this {@code AtomicDouble} as an {@code int} + * after a narrowing primitive conversion. + */ + public int intValue() { + return (int) get(); + } + + /** + * Returns the value of this {@code AtomicDouble} as a {@code long} + * after a narrowing primitive conversion. + */ + public long longValue() { + return (long) get(); + } + + /** + * Returns the value of this {@code AtomicDouble} as a {@code float} + * after a narrowing primitive conversion. + */ + public float floatValue() { + return (float) get(); + } + + /** + * Returns the value of this {@code AtomicDouble} as a {@code double}. + */ + public double doubleValue() { + return get(); + } + + /** + * Saves the state to a stream (that is, serializes it). + * + * @serialData The current value is emitted (a {@code double}). + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + + s.writeDouble(get()); + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + set(s.readDouble()); + } +} diff --git a/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java b/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java new file mode 100644 index 0000000..8da0195 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java @@ -0,0 +1,271 @@ +/* + * 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/ + */ + +/* + * Source: + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/extra/AtomicDoubleArray.java?revision=1.5 + * (Modified to adapt to guava coding conventions and + * to use AtomicLongArray instead of sun.misc.Unsafe) + */ + +package com.google.common.util.concurrent; + +import static java.lang.Double.doubleToRawLongBits; +import static java.lang.Double.longBitsToDouble; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.atomic.AtomicLongArray; + +/** + * A {@code double} array in which elements may be updated atomically. + * See the {@link java.util.concurrent.atomic} package specification + * for description of the properties of atomic variables. + * + * <p><a name="bitEquals">This class compares primitive {@code double} + * values in methods such as {@link #compareAndSet} by comparing their + * bitwise representation using {@link Double#doubleToRawLongBits}, + * which differs from both the primitive double {@code ==} operator + * and from {@link Double#equals}, as if implemented by: + * <pre> {@code + * static boolean bitEquals(double x, double y) { + * long xBits = Double.doubleToRawLongBits(x); + * long yBits = Double.doubleToRawLongBits(y); + * return xBits == yBits; + * }}</pre> + * + * @author Doug Lea + * @author Martin Buchholz + * @since 11.0 + */ +@Beta +public class AtomicDoubleArray implements java.io.Serializable { + private static final long serialVersionUID = 0L; + + // Making this non-final is the lesser evil according to Effective + // Java 2nd Edition Item 76: Write readObject methods defensively. + private transient AtomicLongArray longs; + + /** + * Creates a new {@code AtomicDoubleArray} of the given length, + * with all elements initially zero. + * + * @param length the length of the array + */ + public AtomicDoubleArray(int length) { + this.longs = new AtomicLongArray(length); + } + + /** + * Creates a new {@code AtomicDoubleArray} with the same length + * as, and all elements copied from, the given array. + * + * @param array the array to copy elements from + * @throws NullPointerException if array is null + */ + public AtomicDoubleArray(double[] array) { + final int len = array.length; + long[] longArray = new long[len]; + for (int i = 0; i < len; i++) { + longArray[i] = doubleToRawLongBits(array[i]); + } + this.longs = new AtomicLongArray(longArray); + } + + /** + * Returns the length of the array. + * + * @return the length of the array + */ + public final int length() { + return longs.length(); + } + + /** + * Gets the current value at position {@code i}. + * + * @param i the index + * @return the current value + */ + public final double get(int i) { + return longBitsToDouble(longs.get(i)); + } + + /** + * Sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + */ + public final void set(int i, double newValue) { + long next = doubleToRawLongBits(newValue); + longs.set(i, next); + } + + /** + * Eventually sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + */ + public final void lazySet(int i, double newValue) { + set(i, newValue); + // TODO(user): replace with code below when jdk5 support is dropped. + // long next = doubleToRawLongBits(newValue); + // longs.lazySet(i, next); + } + + /** + * Atomically sets the element at position {@code i} to the given value + * and returns the old value. + * + * @param i the index + * @param newValue the new value + * @return the previous value + */ + public final double getAndSet(int i, double newValue) { + long next = doubleToRawLongBits(newValue); + return longBitsToDouble(longs.getAndSet(i, next)); + } + + /** + * Atomically sets the element at position {@code i} to the given + * updated value + * if the current value is <a href="#bitEquals">bitwise equal</a> + * to the expected value. + * + * @param i the index + * @param expect the expected value + * @param update the new value + * @return true if successful. False return indicates that + * the actual value was not equal to the expected value. + */ + public final boolean compareAndSet(int i, double expect, double update) { + return longs.compareAndSet(i, + doubleToRawLongBits(expect), + doubleToRawLongBits(update)); + } + + /** + * Atomically sets the element at position {@code i} to the given + * updated value + * if the current value is <a href="#bitEquals">bitwise equal</a> + * to the expected value. + * + * <p>May <a + * href="http://download.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html#Spurious"> + * fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * + * @param i the index + * @param expect the expected value + * @param update the new value + * @return true if successful + */ + public final boolean weakCompareAndSet(int i, double expect, double update) { + return longs.weakCompareAndSet(i, + doubleToRawLongBits(expect), + doubleToRawLongBits(update)); + } + + /** + * Atomically adds the given value to the element at index {@code i}. + * + * @param i the index + * @param delta the value to add + * @return the previous value + */ + public final double getAndAdd(int i, double delta) { + while (true) { + long current = longs.get(i); + double currentVal = longBitsToDouble(current); + double nextVal = currentVal + delta; + long next = doubleToRawLongBits(nextVal); + if (longs.compareAndSet(i, current, next)) { + return currentVal; + } + } + } + + /** + * Atomically adds the given value to the element at index {@code i}. + * + * @param i the index + * @param delta the value to add + * @return the updated value + */ + public double addAndGet(int i, double delta) { + while (true) { + long current = longs.get(i); + double currentVal = longBitsToDouble(current); + double nextVal = currentVal + delta; + long next = doubleToRawLongBits(nextVal); + if (longs.compareAndSet(i, current, next)) { + return nextVal; + } + } + } + + /** + * Returns the String representation of the current values of array. + * @return the String representation of the current values of array + */ + public String toString() { + int iMax = length() - 1; + if (iMax == -1) { + return "[]"; + } + + // Double.toString(Math.PI).length() == 17 + StringBuilder b = new StringBuilder((17 + 2) * (iMax + 1)); + b.append('['); + for (int i = 0;; i++) { + b.append(longBitsToDouble(longs.get(i))); + if (i == iMax) { + return b.append(']').toString(); + } + b.append(',').append(' '); + } + } + + /** + * Saves the state to a stream (that is, serializes it). + * + * @serialData The length of the array is emitted (int), followed by all + * of its elements (each a {@code double}) in the proper order. + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + + // Write out array length + int length = length(); + s.writeInt(length); + + // Write out all elements in the proper order. + for (int i = 0; i < length; i++) { + s.writeDouble(get(i)); + } + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + // Read in array length and allocate array + int length = s.readInt(); + this.longs = new AtomicLongArray(length); + + // Read in all elements in the proper order. + for (int i = 0; i < length; i++) { + set(i, s.readDouble()); + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/AtomicLongMap.java b/guava/src/com/google/common/util/concurrent/AtomicLongMap.java new file mode 100644 index 0000000..f5aafd0 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/AtomicLongMap.java @@ -0,0 +1,434 @@ +/* + * 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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.collect.Maps; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A map containing {@code long} values that can be atomically updated. While writes to a + * traditional {@code Map} rely on {@code put(K, V)}, the typical mechanism for writing to this map + * is {@code addAndGet(K, long)}, which adds a {@code long} to the value currently associated with + * {@code K}. If a key has not yet been associated with a value, its implicit value is zero. + * + * <p>Most methods in this class treat absent values and zero values identically, as individually + * documented. Exceptions to this are {@link #containsKey}, {@link #size}, {@link #isEmpty}, + * {@link #asMap}, and {@link #toString}. + * + * <p>Instances of this class may be used by multiple threads concurrently. All operations are + * atomic unless otherwise noted. + * + * <p><b>Note:</b> If your values are always positive and less than 2^31, you may wish to use a + * {@link com.google.common.collect.Multiset} such as + * {@link com.google.common.collect.ConcurrentHashMultiset} instead. + * + * <b>Warning:</b> Unlike {@code Multiset}, entries whose values are zero are not automatically + * removed from the map. Instead they must be removed manually with {@link #removeAllZeros}. + * + * @author Charles Fry + * @since 11.0 + */ +@Beta +@GwtCompatible +public final class AtomicLongMap<K> { + private final ConcurrentHashMap<K, AtomicLong> map; + + private AtomicLongMap(ConcurrentHashMap<K, AtomicLong> map) { + this.map = checkNotNull(map); + } + + /** + * Creates an {@code AtomicLongMap}. + */ + public static <K> AtomicLongMap<K> create() { + return new AtomicLongMap<K>(new ConcurrentHashMap<K, AtomicLong>()); + } + + /** + * Creates an {@code AtomicLongMap} with the same mappings as the specified {@code Map}. + */ + public static <K> AtomicLongMap<K> create(Map<? extends K, ? extends Long> m) { + AtomicLongMap<K> result = create(); + result.putAll(m); + return result; + } + + /** + * Returns the value associated with {@code key}, or zero if there is no value associated with + * {@code key}. + */ + public long get(K key) { + AtomicLong atomic = map.get(key); + return atomic == null ? 0L : atomic.get(); + } + + /** + * Increments by one the value currently associated with {@code key}, and returns the new value. + */ + public long incrementAndGet(K key) { + return addAndGet(key, 1); + } + + /** + * Decrements by one the value currently associated with {@code key}, and returns the new value. + */ + public long decrementAndGet(K key) { + return addAndGet(key, -1); + } + + /** + * Adds {@code delta} to the value currently associated with {@code key}, and returns the new + * value. + */ + public long addAndGet(K key, long delta) { + outer: for (;;) { + AtomicLong atomic = map.get(key); + if (atomic == null) { + atomic = map.putIfAbsent(key, new AtomicLong(delta)); + if (atomic == null) { + return delta; + } + // atomic is now non-null; fall through + } + + for (;;) { + long oldValue = atomic.get(); + if (oldValue == 0L) { + // don't compareAndSet a zero + if (map.replace(key, atomic, new AtomicLong(delta))) { + return delta; + } + // atomic replaced + continue outer; + } + + long newValue = oldValue + delta; + if (atomic.compareAndSet(oldValue, newValue)) { + return newValue; + } + // value changed + } + } + } + + /** + * Increments by one the value currently associated with {@code key}, and returns the old value. + */ + public long getAndIncrement(K key) { + return getAndAdd(key, 1); + } + + /** + * Decrements by one the value currently associated with {@code key}, and returns the old value. + */ + public long getAndDecrement(K key) { + return getAndAdd(key, -1); + } + + /** + * Adds {@code delta} to the value currently associated with {@code key}, and returns the old + * value. + */ + public long getAndAdd(K key, long delta) { + outer: for (;;) { + AtomicLong atomic = map.get(key); + if (atomic == null) { + atomic = map.putIfAbsent(key, new AtomicLong(delta)); + if (atomic == null) { + return 0L; + } + // atomic is now non-null; fall through + } + + for (;;) { + long oldValue = atomic.get(); + if (oldValue == 0L) { + // don't compareAndSet a zero + if (map.replace(key, atomic, new AtomicLong(delta))) { + return 0L; + } + // atomic replaced + continue outer; + } + + long newValue = oldValue + delta; + if (atomic.compareAndSet(oldValue, newValue)) { + return oldValue; + } + // value changed + } + } + } + + /** + * Associates {@code newValue} with {@code key} in this map, and returns the value previously + * associated with {@code key}, or zero if there was no such value. + */ + public long put(K key, long newValue) { + outer: for (;;) { + AtomicLong atomic = map.get(key); + if (atomic == null) { + atomic = map.putIfAbsent(key, new AtomicLong(newValue)); + if (atomic == null) { + return 0L; + } + // atomic is now non-null; fall through + } + + for (;;) { + long oldValue = atomic.get(); + if (oldValue == 0L) { + // don't compareAndSet a zero + if (map.replace(key, atomic, new AtomicLong(newValue))) { + return 0L; + } + // atomic replaced + continue outer; + } + + if (atomic.compareAndSet(oldValue, newValue)) { + return oldValue; + } + // value changed + } + } + } + + /** + * Copies all of the mappings from the specified map to this map. The effect of this call is + * equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key + * {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined + * if the specified map is modified while the operation is in progress. + */ + public void putAll(Map<? extends K, ? extends Long> m) { + for (Map.Entry<? extends K, ? extends Long> entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + /** + * Removes and returns the value associated with {@code key}. If {@code key} is not + * in the map, this method has no effect and returns zero. + */ + public long remove(K key) { + AtomicLong atomic = map.get(key); + if (atomic == null) { + return 0L; + } + + for (;;) { + long oldValue = atomic.get(); + if (oldValue == 0L || atomic.compareAndSet(oldValue, 0L)) { + // only remove after setting to zero, to avoid concurrent updates + map.remove(key, atomic); + // succeed even if the remove fails, since the value was already adjusted + return oldValue; + } + } + } + + /** + * Removes all mappings from this map whose values are zero. + * + * <p>This method is not atomic: the map may be visible in intermediate states, where some + * of the zero values have been removed and others have not. + */ + public void removeAllZeros() { + for (K key : map.keySet()) { + AtomicLong atomic = map.get(key); + if (atomic != null && atomic.get() == 0L) { + map.remove(key, atomic); + } + } + } + + /** + * Returns the sum of all values in this map. + * + * <p>This method is not atomic: the sum may or may not include other concurrent operations. + */ + public long sum() { + long sum = 0L; + for (AtomicLong value : map.values()) { + sum = sum + value.get(); + } + return sum; + } + + private transient Map<K, Long> asMap; + + /** + * Returns a live, read-only view of the map backing this {@code AtomicLongMap}. + */ + public Map<K, Long> asMap() { + Map<K, Long> result = asMap; + return (result == null) ? asMap = createAsMap() : result; + } + + private Map<K, Long> createAsMap() { + return Collections.unmodifiableMap( + Maps.transformValues(map, new Function<AtomicLong, Long>() { + @Override + public Long apply(AtomicLong atomic) { + return atomic.get(); + } + })); + } + + /** + * Returns true if this map contains a mapping for the specified key. + */ + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + /** + * Returns the number of key-value mappings in this map. If the map contains more than + * {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. + */ + public int size() { + return map.size(); + } + + /** + * Returns {@code true} if this map contains no key-value mappings. + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Removes all of the mappings from this map. The map will be empty after this call returns. + * + * <p>This method is not atomic: the map may not be empty after returning if there were concurrent + * writes. + */ + public void clear() { + map.clear(); + } + + @Override + public String toString() { + return map.toString(); + } + + /* + * ConcurrentMap operations which we may eventually add. + * + * The problem with these is that remove(K, long) has to be done in two phases by definition --- + * first decrementing to zero, and then removing. putIfAbsent or replace could observe the + * intermediate zero-state. Ways we could deal with this are: + * + * - Don't define any of the ConcurrentMap operations. This is the current state of affairs. + * + * - Define putIfAbsent and replace as treating zero and absent identically (as currently + * implemented below). This is a bit surprising with putIfAbsent, which really becomes + * putIfZero. + * + * - Allow putIfAbsent and replace to distinguish between zero and absent, but don't implement + * remove(K, long). Without any two-phase operations it becomes feasible for all remaining + * operations to distinguish between zero and absent. If we do this, then perhaps we should add + * replace(key, long). + * + * - Introduce a special-value private static final AtomicLong that would have the meaning of + * removal-in-progress, and rework all operations to properly distinguish between zero and + * absent. + */ + + /** + * If {@code key} is not already associated with a value or if {@code key} is associated with + * zero, associate it with {@code newValue}. Returns the previous value associated with + * {@code key}, or zero if there was no mapping for {@code key}. + */ + long putIfAbsent(K key, long newValue) { + for (;;) { + AtomicLong atomic = map.get(key); + if (atomic == null) { + atomic = map.putIfAbsent(key, new AtomicLong(newValue)); + if (atomic == null) { + return 0L; + } + // atomic is now non-null; fall through + } + + long oldValue = atomic.get(); + if (oldValue == 0L) { + // don't compareAndSet a zero + if (map.replace(key, atomic, new AtomicLong(newValue))) { + return 0L; + } + // atomic replaced + continue; + } + + return oldValue; + } + } + + /** + * If {@code (key, expectedOldValue)} is currently in the map, this method replaces + * {@code expectedOldValue} with {@code newValue} and returns true; otherwise, this method + * returns false. + * + * <p>If {@code expectedOldValue} is zero, this method will succeed if {@code (key, zero)} + * is currently in the map, or if {@code key} is not in the map at all. + */ + boolean replace(K key, long expectedOldValue, long newValue) { + if (expectedOldValue == 0L) { + return putIfAbsent(key, newValue) == 0L; + } else { + AtomicLong atomic = map.get(key); + return (atomic == null) ? false : atomic.compareAndSet(expectedOldValue, newValue); + } + } + + /** + * If {@code (key, value)} is currently in the map, this method removes it and returns + * true; otherwise, this method returns false. + */ + boolean remove(K key, long value) { + AtomicLong atomic = map.get(key); + if (atomic == null) { + return false; + } + + long oldValue = atomic.get(); + if (oldValue != value) { + return false; + } + + if (oldValue == 0L || atomic.compareAndSet(oldValue, 0L)) { + // only remove after setting to zero, to avoid concurrent updates + map.remove(key, atomic); + // succeed even if the remove fails, since the value was already adjusted + return true; + } + + // value changed + return false; + } + +} diff --git a/guava/src/com/google/common/util/concurrent/Atomics.java b/guava/src/com/google/common/util/concurrent/Atomics.java new file mode 100644 index 0000000..fece83d --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/Atomics.java @@ -0,0 +1,76 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; + +import javax.annotation.Nullable; + +/** + * Static utility methods pertaining to classes in the + * {@code java.util.concurrent.atomic} package. + * + * @author Kurt Alfred Kluever + * @since 10.0 + */ +@Beta +public final class Atomics { + private Atomics() {} + + /** + * Creates an {@code AtomicReference} instance with no initial value. + * + * @return a new {@code AtomicReference} with no initial value + */ + public static <V> AtomicReference<V> newReference() { + return new AtomicReference<V>(); + } + + /** + * Creates an {@code AtomicReference} instance with the given initial value. + * + * @param initialValue the initial value + * @return a new {@code AtomicReference} with the given initial value + */ + public static <V> AtomicReference<V> newReference(@Nullable V initialValue) { + return new AtomicReference<V>(initialValue); + } + + /** + * Creates an {@code AtomicReferenceArray} instance of given length. + * + * @param length the length of the array + * @return a new {@code AtomicReferenceArray} with the given length + */ + public static <E> AtomicReferenceArray<E> newReferenceArray(int length) { + return new AtomicReferenceArray<E>(length); + } + + /** + * Creates an {@code AtomicReferenceArray} instance with the same length as, + * and all elements copied from, the given array. + * + * @param array the array to copy elements from + * @return a new {@code AtomicReferenceArray} copied from the given array + */ + public static <E> AtomicReferenceArray<E> newReferenceArray(E[] array) { + return new AtomicReferenceArray<E>(array); + } +} diff --git a/guava/src/com/google/common/util/concurrent/Callables.java b/guava/src/com/google/common/util/concurrent/Callables.java new file mode 100644 index 0000000..f970f42 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/Callables.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import java.util.concurrent.Callable; + +import javax.annotation.Nullable; + +/** + * Static utility methods pertaining to the {@link Callable} interface. + * + * @author Isaac Shum + * @since 1.0 + */ +public final class Callables { + private Callables() {} + + /** + * Creates a {@code Callable} which immediately returns a preset value each + * time it is called. + */ + public static <T> Callable<T> returning(final @Nullable T value) { + return new Callable<T>() { + @Override public T call() { + return value; + } + }; + } +} diff --git a/guava/src/com/google/common/util/concurrent/CheckedFuture.java b/guava/src/com/google/common/util/concurrent/CheckedFuture.java new file mode 100644 index 0000000..31504e2 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/CheckedFuture.java @@ -0,0 +1,76 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A {@code CheckedFuture} is a {@link ListenableFuture} that includes versions + * of the {@code get} methods that can throw a checked exception. This makes it + * easier to create a future that executes logic which can throw an exception. + * + * <p>A common implementation is {@link Futures#immediateCheckedFuture}. + * + * <p>Implementations of this interface must adapt the exceptions thrown by + * {@code Future#get()}: {@link CancellationException}, + * {@link ExecutionException} and {@link InterruptedException} into the type + * specified by the {@code E} type parameter. + * + * <p>This interface also extends the ListenableFuture interface to allow + * listeners to be added. This allows the future to be used as a normal + * {@link Future} or as an asynchronous callback mechanism as needed. This + * allows multiple callbacks to be registered for a particular task, and the + * future will guarantee execution of all listeners when the task completes. + * + * <p>For a simpler alternative to CheckedFuture, consider accessing Future + * values with {@link Futures#get(Future, Class) Futures.get()}. + * + * @author Sven Mawson + * @since 1.0 + */ +@Beta +public interface CheckedFuture<V, X extends Exception> + extends ListenableFuture<V> { + + /** + * Exception checking version of {@link Future#get()} that will translate + * {@link InterruptedException}, {@link CancellationException} and + * {@link ExecutionException} into application-specific exceptions. + * + * @return the result of executing the future. + * @throws X on interruption, cancellation or execution exceptions. + */ + V checkedGet() throws X; + + /** + * Exception checking version of {@link Future#get(long, TimeUnit)} that will + * translate {@link InterruptedException}, {@link CancellationException} and + * {@link ExecutionException} into application-specific exceptions. On + * timeout this method throws a normal {@link TimeoutException}. + * + * @return the result of executing the future. + * @throws TimeoutException if retrieving the result timed out. + * @throws X on interruption, cancellation or execution exceptions. + */ + V checkedGet(long timeout, TimeUnit unit) throws TimeoutException, X; +} diff --git a/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java b/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java new file mode 100644 index 0000000..53d19d0 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java @@ -0,0 +1,1034 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.MapMaker; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; + +/** + * The {@code CycleDetectingLockFactory} creates {@link ReentrantLock}s and + * {@link ReentrantReadWriteLock}s that detect potential deadlock by checking + * for cycles in lock acquisition order. + * <p> + * Potential deadlocks detected when calling the {@code lock()}, + * {@code lockInterruptibly()}, or {@code tryLock()} methods will result in the + * execution of the {@link Policy} specified when creating the factory. The + * currently available policies are: + * <ul> + * <li>DISABLED + * <li>WARN + * <li>THROW + * </ul> + * The locks created by a factory instance will detect lock acquisition cycles + * with locks created by other {@code CycleDetectingLockFactory} instances + * (except those with {@code Policy.DISABLED}). A lock's behavior when a cycle + * is detected, however, is defined by the {@code Policy} of the factory that + * created it. This allows detection of cycles across components while + * delegating control over lock behavior to individual components. + * <p> + * Applications are encouraged to use a {@code CycleDetectingLockFactory} to + * create any locks for which external/unmanaged code is executed while the lock + * is held. (See caveats under <strong>Performance</strong>). + * <p> + * <strong>Cycle Detection</strong> + * <p> + * Deadlocks can arise when locks are acquired in an order that forms a cycle. + * In a simple example involving two locks and two threads, deadlock occurs + * when one thread acquires Lock A, and then Lock B, while another thread + * acquires Lock B, and then Lock A: + * <pre> + * Thread1: acquire(LockA) --X acquire(LockB) + * Thread2: acquire(LockB) --X acquire(LockA) + * </pre> + * Neither thread will progress because each is waiting for the other. In more + * complex applications, cycles can arise from interactions among more than 2 + * locks: + * <pre> + * Thread1: acquire(LockA) --X acquire(LockB) + * Thread2: acquire(LockB) --X acquire(LockC) + * ... + * ThreadN: acquire(LockN) --X acquire(LockA) + * </pre> + * The implementation detects cycles by constructing a directed graph in which + * each lock represents a node and each edge represents an acquisition ordering + * between two locks. + * <ul> + * <li>Each lock adds (and removes) itself to/from a ThreadLocal Set of acquired + * locks when the Thread acquires its first hold (and releases its last + * remaining hold). + * <li>Before the lock is acquired, the lock is checked against the current set + * of acquired locks---to each of the acquired locks, an edge from the + * soon-to-be-acquired lock is either verified or created. + * <li>If a new edge needs to be created, the outgoing edges of the acquired + * locks are traversed to check for a cycle that reaches the lock to be + * acquired. If no cycle is detected, a new "safe" edge is created. + * <li>If a cycle is detected, an "unsafe" (cyclic) edge is created to represent + * a potential deadlock situation, and the appropriate Policy is executed. + * </ul> + * Note that detection of potential deadlock does not necessarily indicate that + * deadlock will happen, as it is possible that higher level application logic + * prevents the cyclic lock acquisition from occurring. One example of a false + * positive is: + * <pre> + * LockA -> LockB -> LockC + * LockA -> LockC -> LockB + * </pre> + * + * <strong>ReadWriteLocks</strong> + * <p> + * While {@code ReadWriteLock}s have different properties and can form cycles + * without potential deadlock, this class treats {@code ReadWriteLock}s as + * equivalent to traditional exclusive locks. Although this increases the false + * positives that the locks detect (i.e. cycles that will not actually result in + * deadlock), it simplifies the algorithm and implementation considerably. The + * assumption is that a user of this factory wishes to eliminate any cyclic + * acquisition ordering. + * <p> + * <strong>Explicit Lock Acquisition Ordering</strong> + * <p> + * The {@link CycleDetectingLockFactory.WithExplicitOrdering} class can be used + * to enforce an application-specific ordering in addition to performing general + * cycle detection. + * <p> + * <strong>Garbage Collection</strong> + * <p> + * In order to allow proper garbage collection of unused locks, the edges of + * the lock graph are weak references. + * <p> + * <strong>Performance</strong> + * <p> + * The extra bookkeeping done by cycle detecting locks comes at some cost to + * performance. Benchmarks (as of December 2011) show that: + * + * <ul> + * <li>for an unnested {@code lock()} and {@code unlock()}, a cycle detecting + * lock takes 38ns as opposed to the 24ns taken by a plain lock. + * <li>for nested locking, the cost increases with the depth of the nesting: + * <ul> + * <li> 2 levels: average of 64ns per lock()/unlock() + * <li> 3 levels: average of 77ns per lock()/unlock() + * <li> 4 levels: average of 99ns per lock()/unlock() + * <li> 5 levels: average of 103ns per lock()/unlock() + * <li>10 levels: average of 184ns per lock()/unlock() + * <li>20 levels: average of 393ns per lock()/unlock() + * </ul> + * </ul> + * + * As such, the CycleDetectingLockFactory may not be suitable for + * performance-critical applications which involve tightly-looped or + * deeply-nested locking algorithms. + * + * @author Darick Tong + * @since 13.0 + */ +@Beta +@ThreadSafe +public class CycleDetectingLockFactory { + + /** + * Encapsulates the action to be taken when a potential deadlock is + * encountered. Clients can use one of the predefined {@link Policies} or + * specify a custom implementation. Implementations must be thread-safe. + * + * @since 13.0 + */ + @Beta + @ThreadSafe + public interface Policy { + + /** + * Called when a potential deadlock is encountered. Implementations can + * throw the given {@code exception} and/or execute other desired logic. + * <p> + * Note that the method will be called even upon an invocation of + * {@code tryLock()}. Although {@code tryLock()} technically recovers from + * deadlock by eventually timing out, this behavior is chosen based on the + * assumption that it is the application's wish to prohibit any cyclical + * lock acquisitions. + */ + void handlePotentialDeadlock(PotentialDeadlockException exception); + } + + /** + * Pre-defined {@link Policy} implementations. + * + * @since 13.0 + */ + @Beta + public enum Policies implements Policy { + /** + * When potential deadlock is detected, this policy results in the throwing + * of the {@code PotentialDeadlockException} indicating the potential + * deadlock, which includes stack traces illustrating the cycle in lock + * acquisition order. + */ + THROW { + @Override + public void handlePotentialDeadlock(PotentialDeadlockException e) { + throw e; + } + }, + + /** + * When potential deadlock is detected, this policy results in the logging + * of a {@link Level#SEVERE} message indicating the potential deadlock, + * which includes stack traces illustrating the cycle in lock acquisition + * order. + */ + WARN { + @Override + public void handlePotentialDeadlock(PotentialDeadlockException e) { + logger.log(Level.SEVERE, "Detected potential deadlock", e); + } + }, + + /** + * Disables cycle detection. This option causes the factory to return + * unmodified lock implementations provided by the JDK, and is provided to + * allow applications to easily parameterize when cycle detection is + * enabled. + * <p> + * Note that locks created by a factory with this policy will <em>not</em> + * participate the cycle detection performed by locks created by other + * factories. + */ + DISABLED { + @Override + public void handlePotentialDeadlock(PotentialDeadlockException e) { + } + }; + } + + /** + * Creates a new factory with the specified policy. + */ + public static CycleDetectingLockFactory newInstance(Policy policy) { + return new CycleDetectingLockFactory(policy); + } + + /** + * Equivalent to {@code newReentrantLock(lockName, false)}. + */ + public ReentrantLock newReentrantLock(String lockName) { + return newReentrantLock(lockName, false); + } + + /** + * Creates a {@link ReentrantLock} with the given fairness policy. The + * {@code lockName} is used in the warning or exception output to help + * identify the locks involved in the detected deadlock. + */ + public ReentrantLock newReentrantLock(String lockName, boolean fair) { + return policy == Policies.DISABLED ? new ReentrantLock(fair) + : new CycleDetectingReentrantLock( + new LockGraphNode(lockName), fair); + } + + /** + * Equivalent to {@code newReentrantReadWriteLock(lockName, false)}. + */ + public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName) { + return newReentrantReadWriteLock(lockName, false); + } + + /** + * Creates a {@link ReentrantReadWriteLock} with the given fairness policy. + * The {@code lockName} is used in the warning or exception output to help + * identify the locks involved in the detected deadlock. + */ + public ReentrantReadWriteLock newReentrantReadWriteLock( + String lockName, boolean fair) { + return policy == Policies.DISABLED ? new ReentrantReadWriteLock(fair) + : new CycleDetectingReentrantReadWriteLock( + new LockGraphNode(lockName), fair); + } + + // A static mapping from an Enum type to its set of LockGraphNodes. + private static final Map<Class<? extends Enum>, + Map<? extends Enum, LockGraphNode>> lockGraphNodesPerType = + new MapMaker().weakKeys().makeComputingMap( + new OrderedLockGraphNodesCreator()); + + /** + * Creates a {@code CycleDetectingLockFactory.WithExplicitOrdering<E>}. + */ + public static <E extends Enum<E>> WithExplicitOrdering<E> + newInstanceWithExplicitOrdering(Class<E> enumClass, Policy policy) { + // OrderedLockGraphNodesCreator maps each enumClass to a Map with the + // corresponding enum key type. + @SuppressWarnings("unchecked") + Map<E, LockGraphNode> lockGraphNodes = + (Map<E, LockGraphNode>) lockGraphNodesPerType.get(enumClass); + return new WithExplicitOrdering<E>(policy, lockGraphNodes); + } + + /** + * A {@code CycleDetectingLockFactory.WithExplicitOrdering} provides the + * additional enforcement of an application-specified ordering of lock + * acquisitions. The application defines the allowed ordering with an + * {@code Enum} whose values each correspond to a lock type. The order in + * which the values are declared dictates the allowed order of lock + * acquisition. In other words, locks corresponding to smaller values of + * {@link Enum#ordinal()} should only be acquired before locks with larger + * ordinals. Example: + * + * <pre> {@code + * enum MyLockOrder { + * FIRST, SECOND, THIRD; + * } + * + * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory = + * CycleDetectingLockFactory.newInstanceWithExplicitOrdering(Policies.THROW); + * + * Lock lock1 = factory.newReentrantLock(MyLockOrder.FIRST); + * Lock lock2 = factory.newReentrantLock(MyLockOrder.SECOND); + * Lock lock3 = factory.newReentrantLock(MyLockOrder.THIRD); + * + * lock1.lock(); + * lock3.lock(); + * lock2.lock(); // will throw an IllegalStateException + * }</pre> + * + * As with all locks created by instances of {@code CycleDetectingLockFactory} + * explicitly ordered locks participate in general cycle detection with all + * other cycle detecting locks, and a lock's behavior when detecting a cyclic + * lock acquisition is defined by the {@code Policy} of the factory that + * created it. + * <p> + * Note, however, that although multiple locks can be created for a given Enum + * value, whether it be through separate factory instances or through multiple + * calls to the same factory, attempting to acquire multiple locks with the + * same Enum value (within the same thread) will result in an + * IllegalStateException regardless of the factory's policy. For example: + * + * <pre> {@code + * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory1 = + * CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...); + * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory2 = + * CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...); + * + * Lock lockA = factory1.newReentrantLock(MyLockOrder.FIRST); + * Lock lockB = factory1.newReentrantLock(MyLockOrder.FIRST); + * Lock lockC = factory2.newReentrantLock(MyLockOrder.FIRST); + * + * lockA.lock(); + * + * lockB.lock(); // will throw an IllegalStateException + * lockC.lock(); // will throw an IllegalStateException + * + * lockA.lock(); // reentrant acquisition is okay + * }</pre> + * + * It is the responsibility of the application to ensure that multiple lock + * instances with the same rank are never acquired in the same thread. + * + * @param <E> The Enum type representing the explicit lock ordering. + * @since 13.0 + */ + @Beta + public static final class WithExplicitOrdering<E extends Enum<E>> + extends CycleDetectingLockFactory { + + private final Map<E, LockGraphNode> lockGraphNodes; + + @VisibleForTesting + WithExplicitOrdering( + Policy policy, Map<E, LockGraphNode> lockGraphNodes) { + super(policy); + this.lockGraphNodes = lockGraphNodes; + } + + /** + * Equivalent to {@code newReentrantLock(rank, false)}. + */ + public ReentrantLock newReentrantLock(E rank) { + return newReentrantLock(rank, false); + } + + /** + * Creates a {@link ReentrantLock} with the given fairness policy and rank. + * The values returned by {@link Enum#getDeclaringClass()} and + * {@link Enum#name()} are used to describe the lock in warning or + * exception output. + * + * @throws IllegalStateException If the factory has already created a + * {@code Lock} with the specified rank. + */ + public ReentrantLock newReentrantLock(E rank, boolean fair) { + return policy == Policies.DISABLED ? new ReentrantLock(fair) + : new CycleDetectingReentrantLock(lockGraphNodes.get(rank), fair); + } + + /** + * Equivalent to {@code newReentrantReadWriteLock(rank, false)}. + */ + public ReentrantReadWriteLock newReentrantReadWriteLock(E rank) { + return newReentrantReadWriteLock(rank, false); + } + + /** + * Creates a {@link ReentrantReadWriteLock} with the given fairness policy + * and rank. The values returned by {@link Enum#getDeclaringClass()} and + * {@link Enum#name()} are used to describe the lock in warning or exception + * output. + * + * @throws IllegalStateException If the factory has already created a + * {@code Lock} with the specified rank. + */ + public ReentrantReadWriteLock newReentrantReadWriteLock( + E rank, boolean fair) { + return policy == Policies.DISABLED ? new ReentrantReadWriteLock(fair) + : new CycleDetectingReentrantReadWriteLock( + lockGraphNodes.get(rank), fair); + } + } + + /** + * For a given Enum type, creates an immutable map from each of the Enum's + * values to a corresponding LockGraphNode, with the + * {@code allowedPriorLocks} and {@code disallowedPriorLocks} prepopulated + * with nodes according to the natural ordering of the associated Enum values. + */ + @VisibleForTesting + static class OrderedLockGraphNodesCreator + implements Function<Class<? extends Enum>, + Map<? extends Enum, LockGraphNode>> { + + @Override + @SuppressWarnings("unchecked") // There's no way to properly express with + // wildcards the recursive Enum type required by createNodesFor(), and the + // Map/Function types must use wildcards since they accept any Enum class. + public Map<? extends Enum, LockGraphNode> apply( + Class<? extends Enum> clazz) { + return createNodesFor(clazz); + } + + <E extends Enum<E>> Map<E, LockGraphNode> createNodesFor(Class<E> clazz) { + EnumMap<E, LockGraphNode> map = Maps.newEnumMap(clazz); + E[] keys = clazz.getEnumConstants(); + final int numKeys = keys.length; + ArrayList<LockGraphNode> nodes = + Lists.newArrayListWithCapacity(numKeys); + // Create a LockGraphNode for each enum value. + for (E key : keys) { + LockGraphNode node = new LockGraphNode(getLockName(key)); + nodes.add(node); + map.put(key, node); + } + // Pre-populate all allowedPriorLocks with nodes of smaller ordinal. + for (int i = 1; i < numKeys; i++) { + nodes.get(i).checkAcquiredLocks(Policies.THROW, nodes.subList(0, i)); + } + // Pre-populate all disallowedPriorLocks with nodes of larger ordinal. + for (int i = 0; i < numKeys - 1; i++) { + nodes.get(i).checkAcquiredLocks( + Policies.DISABLED, nodes.subList(i + 1, numKeys)); + } + return Collections.unmodifiableMap(map); + } + + /** + * For the given Enum value {@code rank}, returns the value's + * {@code "EnumClass.name"}, which is used in exception and warning + * output. + */ + private String getLockName(Enum<?> rank) { + return rank.getDeclaringClass().getSimpleName() + "." + rank.name(); + } + } + + //////// Implementation ///////// + + private static final Logger logger = Logger.getLogger( + CycleDetectingLockFactory.class.getName()); + + final Policy policy; + + private CycleDetectingLockFactory(Policy policy) { + this.policy = policy; + } + + /** + * Tracks the currently acquired locks for each Thread, kept up to date by + * calls to {@link #aboutToAcquire(CycleDetectingLock)} and + * {@link #lockStateChanged(CycleDetectingLock)}. + */ + // This is logically a Set, but an ArrayList is used to minimize the amount + // of allocation done on lock()/unlock(). + private static final ThreadLocal<ArrayList<LockGraphNode>> + acquiredLocks = new ThreadLocal<ArrayList<LockGraphNode>>() { + @Override + protected ArrayList<LockGraphNode> initialValue() { + return Lists.<LockGraphNode>newArrayListWithCapacity(3); + } + }; + + /** + * A Throwable used to record a stack trace that illustrates an example of + * a specific lock acquisition ordering. The top of the stack trace is + * truncated such that it starts with the acquisition of the lock in + * question, e.g. + * + * <pre> + * com...ExampleStackTrace: LockB -> LockC + * at com...CycleDetectingReentrantLock.lock(CycleDetectingLockFactory.java:443) + * at ... + * at ... + * at com...MyClass.someMethodThatAcquiresLockB(MyClass.java:123) + * </pre> + */ + private static class ExampleStackTrace extends IllegalStateException { + + static final StackTraceElement[] EMPTY_STACK_TRACE = + new StackTraceElement[0]; + + static Set<String> EXCLUDED_CLASS_NAMES = ImmutableSet.of( + CycleDetectingLockFactory.class.getName(), + ExampleStackTrace.class.getName(), + LockGraphNode.class.getName()); + + ExampleStackTrace(LockGraphNode node1, LockGraphNode node2) { + super(node1.getLockName() + " -> " + node2.getLockName()); + StackTraceElement[] origStackTrace = getStackTrace(); + for (int i = 0, n = origStackTrace.length; i < n; i++) { + if (WithExplicitOrdering.class.getName().equals( + origStackTrace[i].getClassName())) { + // For pre-populated disallowedPriorLocks edges, omit the stack trace. + setStackTrace(EMPTY_STACK_TRACE); + break; + } + if (!EXCLUDED_CLASS_NAMES.contains(origStackTrace[i].getClassName())) { + setStackTrace(Arrays.copyOfRange(origStackTrace, i, n)); + break; + } + } + } + } + + /** + * Represents a detected cycle in lock acquisition ordering. The exception + * includes a causal chain of {@code ExampleStackTrace}s to illustrate the + * cycle, e.g. + * + * <pre> + * com....PotentialDeadlockException: Potential Deadlock from LockC -> ReadWriteA + * at ... + * at ... + * Caused by: com...ExampleStackTrace: LockB -> LockC + * at ... + * at ... + * Caused by: com...ExampleStackTrace: ReadWriteA -> LockB + * at ... + * at ... + * </pre> + * + * Instances are logged for the {@code Policies.WARN}, and thrown for + * {@code Policies.THROW}. + * + * @since 13.0 + */ + @Beta + public static final class PotentialDeadlockException + extends ExampleStackTrace { + + private final ExampleStackTrace conflictingStackTrace; + + private PotentialDeadlockException( + LockGraphNode node1, + LockGraphNode node2, + ExampleStackTrace conflictingStackTrace) { + super(node1, node2); + this.conflictingStackTrace = conflictingStackTrace; + initCause(conflictingStackTrace); + } + + public ExampleStackTrace getConflictingStackTrace() { + return conflictingStackTrace; + } + + /** + * Appends the chain of messages from the {@code conflictingStackTrace} to + * the original {@code message}. + */ + @Override + public String getMessage() { + StringBuilder message = new StringBuilder(super.getMessage()); + for (Throwable t = conflictingStackTrace; t != null; t = t.getCause()) { + message.append(", ").append(t.getMessage()); + } + return message.toString(); + } + } + + /** + * Internal Lock implementations implement the {@code CycleDetectingLock} + * interface, allowing the detection logic to treat all locks in the same + * manner. + */ + private interface CycleDetectingLock { + + /** @return the {@link LockGraphNode} associated with this lock. */ + LockGraphNode getLockGraphNode(); + + /** @return {@code true} if the current thread has acquired this lock. */ + boolean isAcquiredByCurrentThread(); + } + + /** + * A {@code LockGraphNode} associated with each lock instance keeps track of + * the directed edges in the lock acquisition graph. + */ + private static class LockGraphNode { + + /** + * The map tracking the locks that are known to be acquired before this + * lock, each associated with an example stack trace. Locks are weakly keyed + * to allow proper garbage collection when they are no longer referenced. + */ + final Map<LockGraphNode, ExampleStackTrace> allowedPriorLocks = + new MapMaker().weakKeys().makeMap(); + + /** + * The map tracking lock nodes that can cause a lock acquisition cycle if + * acquired before this node. + */ + final Map<LockGraphNode, PotentialDeadlockException> + disallowedPriorLocks = new MapMaker().weakKeys().makeMap(); + + final String lockName; + + LockGraphNode(String lockName) { + this.lockName = Preconditions.checkNotNull(lockName); + } + + String getLockName() { + return lockName; + } + + void checkAcquiredLocks( + Policy policy, List<LockGraphNode> acquiredLocks) { + for (int i = 0, size = acquiredLocks.size(); i < size; i++) { + checkAcquiredLock(policy, acquiredLocks.get(i)); + } + } + + /** + * Checks the acquisition-ordering between {@code this}, which is about to + * be acquired, and the specified {@code acquiredLock}. + * <p> + * When this method returns, the {@code acquiredLock} should be in either + * the {@code preAcquireLocks} map, for the case in which it is safe to + * acquire {@code this} after the {@code acquiredLock}, or in the + * {@code disallowedPriorLocks} map, in which case it is not safe. + */ + void checkAcquiredLock(Policy policy, LockGraphNode acquiredLock) { + // checkAcquiredLock() should never be invoked by a lock that has already + // been acquired. For unordered locks, aboutToAcquire() ensures this by + // checking isAcquiredByCurrentThread(). For ordered locks, however, this + // can happen because multiple locks may share the same LockGraphNode. In + // this situation, throw an IllegalStateException as defined by contract + // described in the documentation of WithExplicitOrdering. + Preconditions.checkState( + this != acquiredLock, + "Attempted to acquire multiple locks with the same rank " + + acquiredLock.getLockName()); + + if (allowedPriorLocks.containsKey(acquiredLock)) { + // The acquisition ordering from "acquiredLock" to "this" has already + // been verified as safe. In a properly written application, this is + // the common case. + return; + } + PotentialDeadlockException previousDeadlockException = + disallowedPriorLocks.get(acquiredLock); + if (previousDeadlockException != null) { + // Previously determined to be an unsafe lock acquisition. + // Create a new PotentialDeadlockException with the same causal chain + // (the example cycle) as that of the cached exception. + PotentialDeadlockException exception = new PotentialDeadlockException( + acquiredLock, this, + previousDeadlockException.getConflictingStackTrace()); + policy.handlePotentialDeadlock(exception); + return; + } + // Otherwise, it's the first time seeing this lock relationship. Look for + // a path from the acquiredLock to this. + Set<LockGraphNode> seen = Sets.newIdentityHashSet(); + ExampleStackTrace path = acquiredLock.findPathTo(this, seen); + + if (path == null) { + // this can be safely acquired after the acquiredLock. + // + // Note that there is a race condition here which can result in missing + // a cyclic edge: it's possible for two threads to simultaneous find + // "safe" edges which together form a cycle. Preventing this race + // condition efficiently without _introducing_ deadlock is probably + // tricky. For now, just accept the race condition---missing a warning + // now and then is still better than having no deadlock detection. + allowedPriorLocks.put( + acquiredLock, new ExampleStackTrace(acquiredLock, this)); + } else { + // Unsafe acquisition order detected. Create and cache a + // PotentialDeadlockException. + PotentialDeadlockException exception = + new PotentialDeadlockException(acquiredLock, this, path); + disallowedPriorLocks.put(acquiredLock, exception); + policy.handlePotentialDeadlock(exception); + } + } + + /** + * Performs a depth-first traversal of the graph edges defined by each + * node's {@code allowedPriorLocks} to find a path between {@code this} and + * the specified {@code lock}. + * + * @return If a path was found, a chained {@link ExampleStackTrace} + * illustrating the path to the {@code lock}, or {@code null} if no path + * was found. + */ + @Nullable + private ExampleStackTrace findPathTo( + LockGraphNode node, Set<LockGraphNode> seen) { + if (!seen.add(this)) { + return null; // Already traversed this node. + } + ExampleStackTrace found = allowedPriorLocks.get(node); + if (found != null) { + return found; // Found a path ending at the node! + } + // Recurse the edges. + for (Map.Entry<LockGraphNode, ExampleStackTrace> entry : + allowedPriorLocks.entrySet()) { + LockGraphNode preAcquiredLock = entry.getKey(); + found = preAcquiredLock.findPathTo(node, seen); + if (found != null) { + // One of this node's allowedPriorLocks found a path. Prepend an + // ExampleStackTrace(preAcquiredLock, this) to the returned chain of + // ExampleStackTraces. + ExampleStackTrace path = + new ExampleStackTrace(preAcquiredLock, this); + path.setStackTrace(entry.getValue().getStackTrace()); + path.initCause(found); + return path; + } + } + return null; + } + } + + /** + * CycleDetectingLock implementations must call this method before attempting + * to acquire the lock. + */ + private void aboutToAcquire(CycleDetectingLock lock) { + if (!lock.isAcquiredByCurrentThread()) { + ArrayList<LockGraphNode> acquiredLockList = acquiredLocks.get(); + LockGraphNode node = lock.getLockGraphNode(); + node.checkAcquiredLocks(policy, acquiredLockList); + acquiredLockList.add(node); + } + } + + /** + * CycleDetectingLock implementations must call this method in a + * {@code finally} clause after any attempt to change the lock state, + * including both lock and unlock attempts. Failure to do so can result in + * corrupting the acquireLocks set. + */ + private void lockStateChanged(CycleDetectingLock lock) { + if (!lock.isAcquiredByCurrentThread()) { + ArrayList<LockGraphNode> acquiredLockList = acquiredLocks.get(); + LockGraphNode node = lock.getLockGraphNode(); + // Iterate in reverse because locks are usually locked/unlocked in a + // LIFO order. + for (int i = acquiredLockList.size() - 1; i >=0; i--) { + if (acquiredLockList.get(i) == node) { + acquiredLockList.remove(i); + break; + } + } + } + } + + final class CycleDetectingReentrantLock + extends ReentrantLock implements CycleDetectingLock { + + private final LockGraphNode lockGraphNode; + + private CycleDetectingReentrantLock( + LockGraphNode lockGraphNode, boolean fair) { + super(fair); + this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode); + } + + ///// CycleDetectingLock methods. ///// + + @Override + public LockGraphNode getLockGraphNode() { + return lockGraphNode; + } + + @Override + public boolean isAcquiredByCurrentThread() { + return isHeldByCurrentThread(); + } + + ///// Overridden ReentrantLock methods. ///// + + @Override + public void lock() { + aboutToAcquire(this); + try { + super.lock(); + } finally { + lockStateChanged(this); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + aboutToAcquire(this); + try { + super.lockInterruptibly(); + } finally { + lockStateChanged(this); + } + } + + @Override + public boolean tryLock() { + aboutToAcquire(this); + try { + return super.tryLock(); + } finally { + lockStateChanged(this); + } + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) + throws InterruptedException { + aboutToAcquire(this); + try { + return super.tryLock(timeout, unit); + } finally { + lockStateChanged(this); + } + } + + @Override + public void unlock() { + try { + super.unlock(); + } finally { + lockStateChanged(this); + } + } + } + + final class CycleDetectingReentrantReadWriteLock + extends ReentrantReadWriteLock implements CycleDetectingLock { + + // These ReadLock/WriteLock implementations shadow those in the + // ReentrantReadWriteLock superclass. They are simply wrappers around the + // internal Sync object, so this is safe since the shadowed locks are never + // exposed or used. + private final CycleDetectingReentrantReadLock readLock; + private final CycleDetectingReentrantWriteLock writeLock; + + private final LockGraphNode lockGraphNode; + + private CycleDetectingReentrantReadWriteLock( + LockGraphNode lockGraphNode, boolean fair) { + super(fair); + this.readLock = new CycleDetectingReentrantReadLock(this); + this.writeLock = new CycleDetectingReentrantWriteLock(this); + this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode); + } + + ///// Overridden ReentrantReadWriteLock methods. ///// + + @Override + public ReadLock readLock() { + return readLock; + } + + @Override + public WriteLock writeLock() { + return writeLock; + } + + ///// CycleDetectingLock methods. ///// + + @Override + public LockGraphNode getLockGraphNode() { + return lockGraphNode; + } + + @Override + public boolean isAcquiredByCurrentThread() { + return isWriteLockedByCurrentThread() || getReadHoldCount() > 0; + } + } + + private class CycleDetectingReentrantReadLock + extends ReentrantReadWriteLock.ReadLock { + + final CycleDetectingReentrantReadWriteLock readWriteLock; + + CycleDetectingReentrantReadLock( + CycleDetectingReentrantReadWriteLock readWriteLock) { + super(readWriteLock); + this.readWriteLock = readWriteLock; + } + + @Override + public void lock() { + aboutToAcquire(readWriteLock); + try { + super.lock(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + aboutToAcquire(readWriteLock); + try { + super.lockInterruptibly(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public boolean tryLock() { + aboutToAcquire(readWriteLock); + try { + return super.tryLock(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) + throws InterruptedException { + aboutToAcquire(readWriteLock); + try { + return super.tryLock(timeout, unit); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public void unlock() { + try { + super.unlock(); + } finally { + lockStateChanged(readWriteLock); + } + } + } + + private class CycleDetectingReentrantWriteLock + extends ReentrantReadWriteLock.WriteLock { + + final CycleDetectingReentrantReadWriteLock readWriteLock; + + CycleDetectingReentrantWriteLock( + CycleDetectingReentrantReadWriteLock readWriteLock) { + super(readWriteLock); + this.readWriteLock = readWriteLock; + } + + @Override + public void lock() { + aboutToAcquire(readWriteLock); + try { + super.lock(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + aboutToAcquire(readWriteLock); + try { + super.lockInterruptibly(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public boolean tryLock() { + aboutToAcquire(readWriteLock); + try { + return super.tryLock(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) + throws InterruptedException { + aboutToAcquire(readWriteLock); + try { + return super.tryLock(timeout, unit); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public void unlock() { + try { + super.unlock(); + } finally { + lockStateChanged(readWriteLock); + } + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/ExecutionError.java b/guava/src/com/google/common/util/concurrent/ExecutionError.java new file mode 100644 index 0000000..1a2edac --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ExecutionError.java @@ -0,0 +1,64 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * {@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 + */ +@Beta +@GwtCompatible +public 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; +} diff --git a/guava/src/com/google/common/util/concurrent/ExecutionList.java b/guava/src/com/google/common/util/concurrent/ExecutionList.java new file mode 100644 index 0000000..d1b78f5 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ExecutionList.java @@ -0,0 +1,159 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +import java.util.Queue; +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 + */ +public final class ExecutionList { + + // Logger to log exceptions caught when running runnables. + private static final Logger log = + Logger.getLogger(ExecutionList.class.getName()); + + // The runnable,executor pairs to execute. + private final Queue<RunnableExecutorPair> runnables = Lists.newLinkedList(); + + // Boolean we use mark when execution has started. Only accessed from within + // synchronized blocks. + private boolean executed = false; + + /** 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. + Preconditions.checkNotNull(runnable, "Runnable was null."); + Preconditions.checkNotNull(executor, "Executor was null."); + + boolean executeImmediate = false; + + // 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 (runnables) { + if (!executed) { + runnables.add(new RunnableExecutorPair(runnable, executor)); + } else { + executeImmediate = true; + } + } + + // 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. + if (executeImmediate) { + new RunnableExecutorPair(runnable, executor).execute(); + } + } + + /** + * 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. + synchronized (runnables) { + if (executed) { + return; + } + executed = true; + } + + // At this point the runnables will never be modified by another + // thread, so we are safe using it outside of the synchronized block. + while (!runnables.isEmpty()) { + runnables.poll().execute(); + } + } + + private static class RunnableExecutorPair { + final Runnable runnable; + final Executor executor; + + RunnableExecutorPair(Runnable runnable, Executor executor) { + this.runnable = runnable; + this.executor = executor; + } + + void execute() { + 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); + } + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java b/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java new file mode 100644 index 0000000..890479d --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +/** + * A TimeLimiter implementation which actually does not attempt to limit time + * at all. This may be desirable to use in some unit tests. More importantly, + * attempting to debug a call which is time-limited would be extremely annoying, + * so this gives you a time-limiter you can easily swap in for your real + * time-limiter while you're debugging. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@Beta +public final class FakeTimeLimiter implements TimeLimiter { + @Override + public <T> T newProxy(T target, Class<T> interfaceType, long timeoutDuration, + TimeUnit timeoutUnit) { + return target; // ha ha + } + + @Override + public <T> T callWithTimeout(Callable<T> callable, long timeoutDuration, + TimeUnit timeoutUnit, boolean amInterruptible) throws Exception { + return callable.call(); // fooled you + } +} diff --git a/guava/src/com/google/common/util/concurrent/ForwardingBlockingQueue.java b/guava/src/com/google/common/util/concurrent/ForwardingBlockingQueue.java new file mode 100644 index 0000000..f7257a2 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ForwardingBlockingQueue.java @@ -0,0 +1,74 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.collect.ForwardingQueue; + +import java.util.Collection; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * A {@link BlockingQueue} which forwards all its method calls to another + * {@link BlockingQueue}. Subclasses should override one or more methods to + * modify the behavior of the backing collection as desired per the <a + * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. + * + * @author Raimundo Mirisola + * + * @param <E> the type of elements held in this collection + * @since 4.0 + */ +public abstract class ForwardingBlockingQueue<E> extends ForwardingQueue<E> + implements BlockingQueue<E> { + + /** Constructor for use by subclasses. */ + protected ForwardingBlockingQueue() {} + + @Override protected abstract BlockingQueue<E> delegate(); + + @Override public int drainTo( + Collection<? super E> c, int maxElements) { + return delegate().drainTo(c, maxElements); + } + + @Override public int drainTo(Collection<? super E> c) { + return delegate().drainTo(c); + } + + @Override public boolean offer(E e, long timeout, TimeUnit unit) + throws InterruptedException { + return delegate().offer(e, timeout, unit); + } + + @Override public E poll(long timeout, TimeUnit unit) + throws InterruptedException { + return delegate().poll(timeout, unit); + } + + @Override public void put(E e) throws InterruptedException { + delegate().put(e); + } + + @Override public int remainingCapacity() { + return delegate().remainingCapacity(); + } + + @Override public E take() throws InterruptedException { + return delegate().take(); + } +} diff --git a/guava/src/com/google/common/util/concurrent/ForwardingCheckedFuture.java b/guava/src/com/google/common/util/concurrent/ForwardingCheckedFuture.java new file mode 100644 index 0000000..1dee47a --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ForwardingCheckedFuture.java @@ -0,0 +1,78 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A future which forwards all its method calls to another future. Subclasses + * should override one or more methods to modify the behavior of the backing + * future as desired per the <a href= + * "http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. + * + * <p>Most subclasses can simply extend {@link SimpleForwardingCheckedFuture}. + * + * @param <V> The result type returned by this Future's {@code get} method + * @param <X> The type of the Exception thrown by the Future's + * {@code checkedGet} method + * + * @author Anthony Zana + * @since 9.0 + */ +@Beta +public abstract class ForwardingCheckedFuture<V, X extends Exception> + extends ForwardingListenableFuture<V> implements CheckedFuture<V, X> { + + @Override + public V checkedGet() throws X { + return delegate().checkedGet(); + } + + @Override + public V checkedGet(long timeout, TimeUnit unit) throws TimeoutException, X { + return delegate().checkedGet(timeout, unit); + } + + @Override + protected abstract CheckedFuture<V, X> delegate(); + + // TODO(cpovirk): Use Standard Javadoc form for SimpleForwarding* + /** + * A simplified version of {@link ForwardingCheckedFuture} where subclasses + * can pass in an already constructed {@link CheckedFuture} as the delegate. + * + * @since 9.0 + */ + @Beta + public abstract static class SimpleForwardingCheckedFuture< + V, X extends Exception> extends ForwardingCheckedFuture<V, X> { + private final CheckedFuture<V, X> delegate; + + protected SimpleForwardingCheckedFuture(CheckedFuture<V, X> delegate) { + this.delegate = Preconditions.checkNotNull(delegate); + } + + @Override + protected final CheckedFuture<V, X> delegate() { + return delegate; + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/ForwardingExecutorService.java b/guava/src/com/google/common/util/concurrent/ForwardingExecutorService.java new file mode 100644 index 0000000..9c09af0 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ForwardingExecutorService.java @@ -0,0 +1,117 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.collect.ForwardingObject; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * An executor service which forwards all its method calls to another executor + * service. Subclasses should override one or more methods to modify the + * behavior of the backing executor service as desired per the <a + * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. + * + * @author Kurt Alfred Kluever + * @since 10.0 + */ +public abstract class ForwardingExecutorService extends ForwardingObject + implements ExecutorService { + /** Constructor for use by subclasses. */ + protected ForwardingExecutorService() {} + + @Override + protected abstract ExecutorService delegate(); + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + return delegate().awaitTermination(timeout, unit); + } + + @Override + public <T> List<Future<T>> invokeAll( + Collection<? extends Callable<T>> tasks) throws InterruptedException { + return delegate().invokeAll(tasks); + } + + @Override + public <T> List<Future<T>> invokeAll( + Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + return delegate().invokeAll(tasks, timeout, unit); + } + + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks) + throws InterruptedException, ExecutionException { + return delegate().invokeAny(tasks); + } + + @Override + public <T> T invokeAny( + Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate().invokeAny(tasks, 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); + } + + public <T> Future<T> submit(Callable<T> task) { + return delegate().submit(task); + } + + @Override + public Future<?> submit(Runnable task) { + return delegate().submit(task); + } + + @Override + public <T> Future<T> submit(Runnable task, T result) { + return delegate().submit(task, result); + } +} diff --git a/guava/src/com/google/common/util/concurrent/ForwardingFuture.java b/guava/src/com/google/common/util/concurrent/ForwardingFuture.java new file mode 100644 index 0000000..9fcd119 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ForwardingFuture.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ForwardingObject; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A {@link Future} which forwards all its method calls to another future. + * Subclasses should override one or more methods to modify the behavior of + * the backing future as desired per the <a + * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. + * + * <p>Most subclasses can just use {@link SimpleForwardingFuture}. + * + * @author Sven Mawson + * @since 1.0 + */ +public abstract class ForwardingFuture<V> extends ForwardingObject + implements Future<V> { + + /** Constructor for use by subclasses. */ + protected ForwardingFuture() {} + + @Override protected abstract Future<V> delegate(); + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return delegate().cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return delegate().isCancelled(); + } + + @Override + public boolean isDone() { + return delegate().isDone(); + } + + @Override + public V get() throws InterruptedException, ExecutionException { + return delegate().get(); + } + + @Override + public V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate().get(timeout, unit); + } + + /* + * TODO(cpovirk): Use standard Javadoc form for SimpleForwarding* class and + * constructor + */ + /** + * A simplified version of {@link ForwardingFuture} where subclasses + * can pass in an already constructed {@link Future} as the delegate. + * + * @since 9.0 + */ + public abstract static class SimpleForwardingFuture<V> + extends ForwardingFuture<V> { + private final Future<V> delegate; + + protected SimpleForwardingFuture(Future<V> delegate) { + this.delegate = Preconditions.checkNotNull(delegate); + } + + @Override + protected final Future<V> delegate() { + return delegate; + } + + } +} diff --git a/guava/src/com/google/common/util/concurrent/ForwardingListenableFuture.java b/guava/src/com/google/common/util/concurrent/ForwardingListenableFuture.java new file mode 100644 index 0000000..79f6d53 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ForwardingListenableFuture.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; + +import java.util.concurrent.Executor; + +/** + * A {@link ListenableFuture} which forwards all its method calls to another + * future. Subclasses should override one or more methods to modify the behavior + * of the backing future as desired per the <a + * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. + * + * <p>Most subclasses can just use {@link SimpleForwardingListenableFuture}. + * + * @param <V> The result type returned by this Future's {@code get} method + * + * @author Shardul Deo + * @since 4.0 + */ +public abstract class ForwardingListenableFuture<V> extends ForwardingFuture<V> + implements ListenableFuture<V> { + + /** Constructor for use by subclasses. */ + protected ForwardingListenableFuture() {} + + @Override + protected abstract ListenableFuture<V> delegate(); + + @Override + public void addListener(Runnable listener, Executor exec) { + delegate().addListener(listener, exec); + } + + /* + * TODO(cpovirk): Use standard Javadoc form for SimpleForwarding* class and + * constructor + */ + /** + * A simplified version of {@link ForwardingListenableFuture} where subclasses + * can pass in an already constructed {@link ListenableFuture} + * as the delegate. + * + * @since 9.0 + */ + public abstract static class SimpleForwardingListenableFuture<V> + extends ForwardingListenableFuture<V> { + private final ListenableFuture<V> delegate; + + protected SimpleForwardingListenableFuture(ListenableFuture<V> delegate) { + this.delegate = Preconditions.checkNotNull(delegate); + } + + @Override + protected final ListenableFuture<V> delegate() { + return delegate; + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/ForwardingListeningExecutorService.java b/guava/src/com/google/common/util/concurrent/ForwardingListeningExecutorService.java new file mode 100644 index 0000000..27e44de --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ForwardingListeningExecutorService.java @@ -0,0 +1,52 @@ +/* + * 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 com.google.common.util.concurrent; + +import java.util.concurrent.Callable; + +/** + * A listening executor service which forwards all its method calls to another + * listening executor service. Subclasses should override one or more methods to + * modify the behavior of the backing executor service as desired per the <a + * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. + * + * @author Isaac Shum + * @since 10.0 + */ +public abstract class ForwardingListeningExecutorService + extends ForwardingExecutorService implements ListeningExecutorService { + /** Constructor for use by subclasses. */ + protected ForwardingListeningExecutorService() {} + + @Override + protected abstract ListeningExecutorService delegate(); + + @Override + public <T> ListenableFuture<T> submit(Callable<T> task) { + return delegate().submit(task); + } + + @Override + public ListenableFuture<?> submit(Runnable task) { + return delegate().submit(task); + } + + @Override + public <T> ListenableFuture<T> submit(Runnable task, T result) { + return delegate().submit(task, result); + } +} diff --git a/guava/src/com/google/common/util/concurrent/ForwardingService.java b/guava/src/com/google/common/util/concurrent/ForwardingService.java new file mode 100644 index 0000000..0fff279 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ForwardingService.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ForwardingObject; + +import java.util.concurrent.Executor; + +/** + * A {@link Service} that forwards all method calls to another service. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@Beta +public abstract class ForwardingService extends ForwardingObject + implements Service { + + /** Constructor for use by subclasses. */ + protected ForwardingService() {} + + @Override protected abstract Service delegate(); + + @Override public ListenableFuture<State> start() { + return delegate().start(); + } + + @Override public State state() { + return delegate().state(); + } + + @Override public ListenableFuture<State> stop() { + return delegate().stop(); + } + + @Override public State startAndWait() { + return delegate().startAndWait(); + } + + @Override public State stopAndWait() { + return delegate().stopAndWait(); + } + + @Override public boolean isRunning() { + return delegate().isRunning(); + } + + @Override public void addListener(Listener listener, Executor executor) { + delegate().addListener(listener, executor); + } + + /** + * A sensible default implementation of {@link #startAndWait()}, in terms of + * {@link #start}. If you override {@link #start}, you may wish to override + * {@link #startAndWait()} to forward to this implementation. + * @since 9.0 + */ + protected State standardStartAndWait() { + return Futures.getUnchecked(start()); + } + + /** + * A sensible default implementation of {@link #stopAndWait()}, in terms of + * {@link #stop}. If you override {@link #stop}, you may wish to override + * {@link #stopAndWait()} to forward to this implementation. + * @since 9.0 + */ + protected State standardStopAndWait() { + return Futures.getUnchecked(stop()); + } +} diff --git a/guava/src/com/google/common/util/concurrent/FutureCallback.java b/guava/src/com/google/common/util/concurrent/FutureCallback.java new file mode 100644 index 0000000..7b39d4a --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/FutureCallback.java @@ -0,0 +1,49 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.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 + */ +@Beta +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); +} diff --git a/guava/src/com/google/common/util/concurrent/Futures.java b/guava/src/com/google/common/util/concurrent/Futures.java new file mode 100644 index 0000000..0ff8e8a --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/Futures.java @@ -0,0 +1,1249 @@ +/* + * Copyright (C) 2006 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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; +import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; +import static com.google.common.util.concurrent.Uninterruptibles.putUninterruptibly; +import static com.google.common.util.concurrent.Uninterruptibles.takeUninterruptibly; +import static java.lang.Thread.currentThread; +import static java.util.Arrays.asList; + +import com.google.common.annotations.Beta; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.annotation.Nullable; + +/** + * Static utility methods pertaining to the {@link Future} interface. + * + * <p>Many of these methods use the {@link ListenableFuture} API; consult the + * Guava User Guide article on <a href= + * "http://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained"> + * {@code ListenableFuture}</a>. + * + * @author Kevin Bourrillion + * @author Nishant Thakkar + * @author Sven Mawson + * @since 1.0 + */ +@Beta +public final class Futures { + private Futures() {} + + /** + * Creates a {@link CheckedFuture} out of a normal {@link ListenableFuture} + * and a {@link Function} that maps from {@link Exception} instances into the + * appropriate checked type. + * + * <p>The given mapping function will be applied to an + * {@link InterruptedException}, a {@link CancellationException}, or an + * {@link ExecutionException} with the actual cause of the exception. + * See {@link Future#get()} for details on the exceptions thrown. + * + * @since 9.0 (source-compatible since 1.0) + */ + public static <V, X extends Exception> CheckedFuture<V, X> makeChecked( + ListenableFuture<V> future, Function<Exception, X> mapper) { + return new MappingCheckedFuture<V, X>(checkNotNull(future), mapper); + } + + /** + * Creates a {@code ListenableFuture} which has its value set immediately upon + * construction. The getters just return the value. This {@code Future} can't + * be canceled or timed out and its {@code isDone()} method always returns + * {@code true}. + */ + public static <V> ListenableFuture<V> immediateFuture(@Nullable V value) { + SettableFuture<V> future = SettableFuture.create(); + future.set(value); + return future; + } + + /** + * Returns a {@code CheckedFuture} which has its value set immediately upon + * construction. + * + * <p>The returned {@code Future} can't be cancelled, and its {@code isDone()} + * method always returns {@code true}. Calling {@code get()} or {@code + * checkedGet()} will immediately return the provided value. + */ + public static <V, X extends Exception> CheckedFuture<V, X> + immediateCheckedFuture(@Nullable V value) { + SettableFuture<V> future = SettableFuture.create(); + future.set(value); + return Futures.makeChecked(future, new Function<Exception, X>() { + @Override + public X apply(Exception e) { + throw new AssertionError("impossible"); + } + }); + } + + /** + * Returns a {@code ListenableFuture} which has an exception set immediately + * upon construction. + * + * <p>The returned {@code Future} can't be cancelled, and its {@code isDone()} + * method always returns {@code true}. Calling {@code get()} will immediately + * throw the provided {@code Throwable} wrapped in an {@code + * ExecutionException}. + * + * @throws Error if the throwable is an {@link Error}. + */ + public static <V> ListenableFuture<V> immediateFailedFuture( + Throwable throwable) { + checkNotNull(throwable); + SettableFuture<V> future = SettableFuture.create(); + future.setException(throwable); + return future; + } + + /** + * Returns a {@code CheckedFuture} which has an exception set immediately upon + * construction. + * + * <p>The returned {@code Future} can't be cancelled, and its {@code isDone()} + * method always returns {@code true}. Calling {@code get()} will immediately + * throw the provided {@code Throwable} wrapped in an {@code + * ExecutionException}, and calling {@code checkedGet()} will throw the + * provided exception itself. + * + * @throws Error if the throwable is an {@link Error}. + */ + public static <V, X extends Exception> CheckedFuture<V, X> + immediateFailedCheckedFuture(final X exception) { + checkNotNull(exception); + return makeChecked(Futures.<V>immediateFailedFuture(exception), + new Function<Exception, X>() { + @Override + public X apply(Exception e) { + return exception; + } + }); + } + + /** + * Returns a new {@code ListenableFuture} whose result is asynchronously + * derived from the result of the given {@code Future}. More precisely, the + * returned {@code Future} takes its result from a {@code Future} produced by + * applying the given {@code AsyncFunction} to the result of the original + * {@code Future}. Example: + * + * <pre> {@code + * ListenableFuture<RowKey> rowKeyFuture = indexService.lookUp(query); + * AsyncFunction<RowKey, QueryResult> queryFunction = + * new AsyncFunction<RowKey, QueryResult>() { + * public ListenableFuture<QueryResult> apply(RowKey rowKey) { + * return dataService.read(rowKey); + * } + * }; + * ListenableFuture<QueryResult> queryFuture = + * transform(rowKeyFuture, queryFunction); + * }</pre> + * + * Note: If the derived {@code Future} is slow or heavyweight to create + * (whether the {@code Future} itself is slow or heavyweight to complete is + * irrelevant), consider {@linkplain #transform(ListenableFuture, + * AsyncFunction, Executor) supplying an executor}. If you do not supply an + * executor, {@code transform} will use {@link + * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some + * caveats for heavier operations. For example, the call to {@code + * function.apply} may run on an unpredictable or undesirable thread: + * + * <ul> + * <li>If the input {@code Future} is done at the time {@code transform} is + * called, {@code transform} will call {@code function.apply} inline. + * <li>If the input {@code Future} is not yet done, {@code transform} will + * schedule {@code function.apply} to be run by the thread that completes the + * input {@code Future}, which may be an internal system thread such as an + * RPC network thread. + * </ul> + * + * Also note that, regardless of which thread executes {@code + * function.apply}, all other registered but unexecuted listeners are + * prevented from running during its execution, even if those listeners are + * to run in other executors. + * + * <p>The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future and that of the future returned by the + * function. That is, if the returned {@code Future} is cancelled, it will + * attempt to cancel the other two, and if either of the other two is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + * @param input The future to transform + * @param function A function to transform the result of the input future + * to the result of the output future + * @return A future that holds result of the function (if the input succeeded) + * or the original input's failure (if not) + * @since 11.0 + */ + public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> input, + AsyncFunction<? super I, ? extends O> function) { + return transform(input, function, MoreExecutors.sameThreadExecutor()); + } + + /** + * Returns a new {@code ListenableFuture} whose result is asynchronously + * derived from the result of the given {@code Future}. More precisely, the + * returned {@code Future} takes its result from a {@code Future} produced by + * applying the given {@code AsyncFunction} to the result of the original + * {@code Future}. Example: + * + * <pre> {@code + * ListenableFuture<RowKey> rowKeyFuture = indexService.lookUp(query); + * AsyncFunction<RowKey, QueryResult> queryFunction = + * new AsyncFunction<RowKey, QueryResult>() { + * public ListenableFuture<QueryResult> apply(RowKey rowKey) { + * return dataService.read(rowKey); + * } + * }; + * ListenableFuture<QueryResult> queryFuture = + * transform(rowKeyFuture, queryFunction, executor); + * }</pre> + * + * <p>The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future and that of the future returned by the + * chain function. That is, if the returned {@code Future} is cancelled, it + * will attempt to cancel the other two, and if either of the other two is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + * <p>When the execution of {@code function.apply} is fast and lightweight + * (though the {@code Future} it returns need not meet these criteria), + * consider {@linkplain #transform(ListenableFuture, AsyncFunction) omitting + * the executor} or explicitly specifying {@code sameThreadExecutor}. + * However, be aware of the caveats documented in the link above. + * + * @param input The future to transform + * @param function A function to transform the result of the input future + * to the result of the output future + * @param executor Executor to run the function in. + * @return A future that holds result of the function (if the input succeeded) + * or the original input's failure (if not) + * @since 11.0 + */ + public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> input, + AsyncFunction<? super I, ? extends O> function, + Executor executor) { + ChainingListenableFuture<I, O> output = + new ChainingListenableFuture<I, O>(function, input); + input.addListener(output, executor); + return output; + } + + /** + * Returns a new {@code ListenableFuture} whose result is the product of + * applying the given {@code Function} to the result of the given {@code + * Future}. Example: + * + * <pre> {@code + * ListenableFuture<QueryResult> queryFuture = ...; + * Function<QueryResult, List<Row>> rowsFunction = + * new Function<QueryResult, List<Row>>() { + * public List<Row> apply(QueryResult queryResult) { + * return queryResult.getRows(); + * } + * }; + * ListenableFuture<List<Row>> rowsFuture = + * transform(queryFuture, rowsFunction); + * }</pre> + * + * Note: If the transformation is slow or heavyweight, consider {@linkplain + * #transform(ListenableFuture, Function, Executor) supplying an executor}. + * If you do not supply an executor, {@code transform} will use {@link + * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some + * caveats for heavier operations. For example, the call to {@code + * function.apply} may run on an unpredictable or undesirable thread: + * + * <ul> + * <li>If the input {@code Future} is done at the time {@code transform} is + * called, {@code transform} will call {@code function.apply} inline. + * <li>If the input {@code Future} is not yet done, {@code transform} will + * schedule {@code function.apply} to be run by the thread that completes the + * input {@code Future}, which may be an internal system thread such as an + * RPC network thread. + * </ul> + * + * Also note that, regardless of which thread executes {@code + * function.apply}, all other registered but unexecuted listeners are + * prevented from running during its execution, even if those listeners are + * to run in other executors. + * + * <p>The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future. That is, if the returned {@code Future} + * is cancelled, it will attempt to cancel the input, and if the input is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + * <p>An example use of this method is to convert a serializable object + * returned from an RPC into a POJO. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future + * to the results of the returned future. This will be run in the thread + * that notifies input it is complete. + * @return A future that holds result of the transformation. + * @since 9.0 (in 1.0 as {@code compose}) + */ + public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> input, + final Function<? super I, ? extends O> function) { + return transform(input, function, MoreExecutors.sameThreadExecutor()); + } + + /** + * Returns a new {@code ListenableFuture} whose result is the product of + * applying the given {@code Function} to the result of the given {@code + * Future}. Example: + * + * <pre> {@code + * ListenableFuture<QueryResult> queryFuture = ...; + * Function<QueryResult, List<Row>> rowsFunction = + * new Function<QueryResult, List<Row>>() { + * public List<Row> apply(QueryResult queryResult) { + * return queryResult.getRows(); + * } + * }; + * ListenableFuture<List<Row>> rowsFuture = + * transform(queryFuture, rowsFunction, executor); + * }</pre> + * + * <p>The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future. That is, if the returned {@code Future} + * is cancelled, it will attempt to cancel the input, and if the input is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + * <p>An example use of this method is to convert a serializable object + * returned from an RPC into a POJO. + * + * <p>When the transformation is fast and lightweight, consider {@linkplain + * #transform(ListenableFuture, Function) omitting the executor} or + * explicitly specifying {@code sameThreadExecutor}. However, be aware of the + * caveats documented in the link above. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future + * to the results of the returned future. + * @param executor Executor to run the function in. + * @return A future that holds result of the transformation. + * @since 9.0 (in 2.0 as {@code compose}) + */ + public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> input, + final Function<? super I, ? extends O> function, Executor executor) { + checkNotNull(function); + AsyncFunction<I, O> wrapperFunction + = new AsyncFunction<I, O>() { + @Override public ListenableFuture<O> apply(I input) { + O output = function.apply(input); + return immediateFuture(output); + } + }; + return transform(input, wrapperFunction, executor); + } + + /** + * Like {@link #transform(ListenableFuture, Function)} except that the + * transformation {@code function} is invoked on each call to + * {@link Future#get() get()} on the returned future. + * + * <p>The returned {@code Future} reflects the input's cancellation + * state directly, and any attempt to cancel the returned Future is likewise + * passed through to the input Future. + * + * <p>Note that calls to {@linkplain Future#get(long, TimeUnit) timed get} + * only apply the timeout to the execution of the underlying {@code Future}, + * <em>not</em> to the execution of the transformation function. + * + * <p>The primary audience of this method is callers of {@code transform} + * who don't have a {@code ListenableFuture} available and + * do not mind repeated, lazy function evaluation. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future + * to the results of the returned future. + * @return A future that returns the result of the transformation. + * @since 10.0 + */ + @Beta + public static <I, O> Future<O> lazyTransform(final Future<I> input, + final Function<? super I, ? extends O> function) { + checkNotNull(input); + checkNotNull(function); + return new Future<O>() { + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return input.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return input.isCancelled(); + } + + @Override + public boolean isDone() { + return input.isDone(); + } + + @Override + public O get() throws InterruptedException, ExecutionException { + return applyTransformation(input.get()); + } + + @Override + public O get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return applyTransformation(input.get(timeout, unit)); + } + + private O applyTransformation(I input) throws ExecutionException { + try { + return function.apply(input); + } catch (Throwable t) { + throw new ExecutionException(t); + } + } + }; + } + + /** + * An implementation of {@code ListenableFuture} that also implements + * {@code Runnable} so that it can be used to nest ListenableFutures. + * Once the passed-in {@code ListenableFuture} is complete, it calls the + * passed-in {@code Function} to generate the result. + * + * <p>If the function throws any checked exceptions, they should be wrapped + * in a {@code UndeclaredThrowableException} so that this class can get + * access to the cause. + */ + private static class ChainingListenableFuture<I, O> + extends AbstractFuture<O> implements Runnable { + + private AsyncFunction<? super I, ? extends O> function; + private ListenableFuture<? extends I> inputFuture; + private volatile ListenableFuture<? extends O> outputFuture; + private final BlockingQueue<Boolean> mayInterruptIfRunningChannel = + new LinkedBlockingQueue<Boolean>(1); + private final CountDownLatch outputCreated = new CountDownLatch(1); + + private ChainingListenableFuture( + AsyncFunction<? super I, ? extends O> function, + ListenableFuture<? extends I> inputFuture) { + this.function = checkNotNull(function); + this.inputFuture = checkNotNull(inputFuture); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + /* + * Our additional cancellation work needs to occur even if + * !mayInterruptIfRunning, so we can't move it into interruptTask(). + */ + if (super.cancel(mayInterruptIfRunning)) { + // This should never block since only one thread is allowed to cancel + // this Future. + putUninterruptibly(mayInterruptIfRunningChannel, mayInterruptIfRunning); + cancel(inputFuture, mayInterruptIfRunning); + cancel(outputFuture, mayInterruptIfRunning); + return true; + } + return false; + } + + private void cancel(@Nullable Future<?> future, + boolean mayInterruptIfRunning) { + if (future != null) { + future.cancel(mayInterruptIfRunning); + } + } + + @Override + public void run() { + try { + I sourceResult; + try { + sourceResult = getUninterruptibly(inputFuture); + } catch (CancellationException e) { + // Cancel this future and return. + // At this point, inputFuture is cancelled and outputFuture doesn't + // exist, so the value of mayInterruptIfRunning is irrelevant. + cancel(false); + return; + } catch (ExecutionException e) { + // Set the cause of the exception as this future's exception + setException(e.getCause()); + return; + } + + final ListenableFuture<? extends O> outputFuture = this.outputFuture = + function.apply(sourceResult); + if (isCancelled()) { + // Handles the case where cancel was called while the function was + // being applied. + // There is a gap in cancel(boolean) between calling sync.cancel() + // and storing the value of mayInterruptIfRunning, so this thread + // needs to block, waiting for that value. + outputFuture.cancel( + takeUninterruptibly(mayInterruptIfRunningChannel)); + this.outputFuture = null; + return; + } + outputFuture.addListener(new Runnable() { + @Override + public void run() { + try { + // Here it would have been nice to have had an + // UninterruptibleListenableFuture, but we don't want to start a + // combinatorial explosion of interfaces, so we have to make do. + set(getUninterruptibly(outputFuture)); + } catch (CancellationException e) { + // Cancel this future and return. + // At this point, inputFuture and outputFuture are done, so the + // value of mayInterruptIfRunning is irrelevant. + cancel(false); + return; + } catch (ExecutionException e) { + // Set the cause of the exception as this future's exception + setException(e.getCause()); + } finally { + // Don't pin inputs beyond completion + ChainingListenableFuture.this.outputFuture = null; + } + } + }, MoreExecutors.sameThreadExecutor()); + } catch (UndeclaredThrowableException e) { + // Set the cause of the exception as this future's exception + setException(e.getCause()); + } catch (Exception e) { + // This exception is irrelevant in this thread, but useful for the + // client + setException(e); + } catch (Error e) { + // Propagate errors up ASAP - our superclass will rethrow the error + setException(e); + } finally { + // Don't pin inputs beyond completion + function = null; + inputFuture = null; + // Allow our get routines to examine outputFuture now. + outputCreated.countDown(); + } + } + } + + /** + * Returns a new {@code ListenableFuture} whose result is the product of + * calling {@code get()} on the {@code Future} nested within the given {@code + * Future}, effectively chaining the futures one after the other. Example: + * + * <pre> {@code + * SettableFuture<ListenableFuture<String>> nested = SettableFuture.create(); + * ListenableFuture<String> dereferenced = dereference(nested); + * }</pre> + * + * <p>This call has the same cancellation and execution semantics as {@link + * #transform(ListenableFuture, AsyncFunction)}, in that the returned {@code + * Future} attempts to keep its cancellation state in sync with both the + * input {@code Future} and the nested {@code Future}. The transformation + * is very lightweight and therefore takes place in the thread that called + * {@code dereference}. + * + * @param nested The nested future to transform. + * @return A future that holds result of the inner future. + * @since 13.0 + */ + @Beta + @SuppressWarnings({"rawtypes", "unchecked"}) + public static <V> ListenableFuture<V> dereference( + ListenableFuture<? extends ListenableFuture<? extends V>> nested) { + return Futures.transform((ListenableFuture) nested, (AsyncFunction) DEREFERENCER); + } + + /** + * Helper {@code Function} for {@link #dereference}. + */ + private static final AsyncFunction<ListenableFuture<Object>, Object> DEREFERENCER = + new AsyncFunction<ListenableFuture<Object>, Object>() { + @Override public ListenableFuture<Object> apply(ListenableFuture<Object> input) { + return input; + } + }; + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the + * values of all its input futures, if all succeed. If any input fails, the + * returned future fails. + * + * <p>The list of results is in the same order as the input list. + * + * <p>Canceling this future does not cancel any of the component futures; + * however, if any of the provided futures fails or is canceled, this one is, + * too. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component + * futures + * @since 10.0 + */ + @Beta + public static <V> ListenableFuture<List<V>> allAsList( + ListenableFuture<? extends V>... futures) { + return new ListFuture<V>(ImmutableList.copyOf(futures), true, + MoreExecutors.sameThreadExecutor()); + } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the + * values of all its input futures, if all succeed. If any input fails, the + * returned future fails. + * + * <p>The list of results is in the same order as the input list. + * + * <p>Canceling this future does not cancel any of the component futures; + * however, if any of the provided futures fails or is canceled, this one is, + * too. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component + * futures + * @since 10.0 + */ + @Beta + public static <V> ListenableFuture<List<V>> allAsList( + Iterable<? extends ListenableFuture<? extends V>> futures) { + return new ListFuture<V>(ImmutableList.copyOf(futures), true, + MoreExecutors.sameThreadExecutor()); + } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the + * values of all its successful input futures. The list of results is in the + * same order as the input list, and if any of the provided futures fails or + * is canceled, its corresponding position will contain {@code null} (which is + * indistinguishable from the future having a successful value of + * {@code null}). + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component + * futures + * @since 10.0 + */ + @Beta + public static <V> ListenableFuture<List<V>> successfulAsList( + ListenableFuture<? extends V>... futures) { + return new ListFuture<V>(ImmutableList.copyOf(futures), false, + MoreExecutors.sameThreadExecutor()); + } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the + * values of all its successful input futures. The list of results is in the + * same order as the input list, and if any of the provided futures fails or + * is canceled, its corresponding position will contain {@code null} (which is + * indistinguishable from the future having a successful value of + * {@code null}). + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component + * futures + * @since 10.0 + */ + @Beta + public static <V> ListenableFuture<List<V>> successfulAsList( + Iterable<? extends ListenableFuture<? extends V>> futures) { + return new ListFuture<V>(ImmutableList.copyOf(futures), false, + MoreExecutors.sameThreadExecutor()); + } + + /** + * Registers separate success and failure callbacks to be run when the {@code + * Future}'s computation is {@linkplain java.util.concurrent.Future#isDone() + * complete} or, if the computation is already complete, immediately. + * + * <p>There is no guaranteed ordering of execution of callbacks, but any + * callback added through this method is guaranteed to be called once the + * computation is complete. + * + * Example: <pre> {@code + * ListenableFuture<QueryResult> future = ...; + * addCallback(future, + * new FutureCallback<QueryResult> { + * public void onSuccess(QueryResult result) { + * storeInCache(result); + * } + * public void onFailure(Throwable t) { + * reportError(t); + * } + * });}</pre> + * + * Note: If the callback is slow or heavyweight, consider {@linkplain + * #addCallback(ListenableFuture, FutureCallback, Executor) supplying an + * executor}. If you do not supply an executor, {@code addCallback} will use + * {@link MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries + * some caveats for heavier operations. For example, the callback may run on + * an unpredictable or undesirable thread: + * + * <ul> + * <li>If the input {@code Future} is done at the time {@code addCallback} is + * called, {@code addCallback} will execute the callback inline. + * <li>If the input {@code Future} is not yet done, {@code addCallback} will + * schedule the callback to be run by the thread that completes the input + * {@code Future}, which may be an internal system thread such as an RPC + * network thread. + * </ul> + * + * Also note that, regardless of which thread executes the callback, all + * other registered but unexecuted listeners are prevented from running + * during its execution, even if those listeners are to run in other + * executors. + * + * <p>For a more general interface to attach a completion listener to a + * {@code Future}, see {@link ListenableFuture#addListener addListener}. + * + * @param future The future attach the callback to. + * @param callback The callback to invoke when {@code future} is completed. + * @since 10.0 + */ + public static <V> void addCallback(ListenableFuture<V> future, + FutureCallback<? super V> callback) { + addCallback(future, callback, MoreExecutors.sameThreadExecutor()); + } + + /** + * Registers separate success and failure callbacks to be run when the {@code + * Future}'s computation is {@linkplain java.util.concurrent.Future#isDone() + * complete} or, if the computation is already complete, immediately. + * + * <p>The callback is run in {@code executor}. + * There is no guaranteed ordering of execution of callbacks, but any + * callback added through this method is guaranteed to be called once the + * computation is complete. + * + * Example: <pre> {@code + * ListenableFuture<QueryResult> future = ...; + * Executor e = ... + * addCallback(future, e, + * new FutureCallback<QueryResult> { + * public void onSuccess(QueryResult result) { + * storeInCache(result); + * } + * public void onFailure(Throwable t) { + * reportError(t); + * } + * });}</pre> + * + * When the callback is fast and lightweight, consider {@linkplain + * #addCallback(ListenableFuture, FutureCallback) omitting the executor} or + * explicitly specifying {@code sameThreadExecutor}. However, be aware of the + * caveats documented in the link above. + * + * <p>For a more general interface to attach a completion listener to a + * {@code Future}, see {@link ListenableFuture#addListener addListener}. + * + * @param future The future attach the callback to. + * @param callback The callback to invoke when {@code future} is completed. + * @param executor The executor to run {@code callback} when the future + * completes. + * @since 10.0 + */ + public static <V> void addCallback(final ListenableFuture<V> future, + final FutureCallback<? super V> callback, Executor executor) { + Preconditions.checkNotNull(callback); + Runnable callbackListener = new Runnable() { + @Override + public void run() { + try { + // TODO(user): (Before Guava release), validate that this + // is the thing for IE. + V value = getUninterruptibly(future); + callback.onSuccess(value); + } catch (ExecutionException e) { + callback.onFailure(e.getCause()); + } catch (RuntimeException e) { + callback.onFailure(e); + } catch (Error e) { + callback.onFailure(e); + } + } + }; + future.addListener(callbackListener, executor); + } + + /** + * Returns the result of {@link Future#get()}, converting most exceptions to a + * new instance of the given checked exception type. This reduces boilerplate + * for a common use of {@code Future} in which it is unnecessary to + * programmatically distinguish between exception types or to extract other + * information from the exception instance. + * + * <p>Exceptions from {@code Future.get} are treated as follows: + * <ul> + * <li>Any {@link ExecutionException} has its <i>cause</i> wrapped in an + * {@code X} if the cause is a checked exception, an {@link + * UncheckedExecutionException} if the cause is a {@code + * RuntimeException}, or an {@link ExecutionError} if the cause is an + * {@code Error}. + * <li>Any {@link InterruptedException} is wrapped in an {@code X} (after + * restoring the interrupt). + * <li>Any {@link CancellationException} is propagated untouched, as is any + * other {@link RuntimeException} (though {@code get} implementations are + * discouraged from throwing such exceptions). + * </ul> + * + * The overall principle is to continue to treat every checked exception as a + * checked exception, every unchecked exception as an unchecked exception, and + * every error as an error. In addition, the cause of any {@code + * ExecutionException} is wrapped in order to ensure that the new stack trace + * matches that of the current thread. + * + * <p>Instances of {@code exceptionClass} are created by choosing an arbitrary + * public constructor that accepts zero or more arguments, all of type {@code + * String} or {@code Throwable} (preferring constructors with at least one + * {@code String}) and calling the constructor via reflection. If the + * exception did not already have a cause, one is set by calling {@link + * Throwable#initCause(Throwable)} on it. If no such constructor exists, an + * {@code IllegalArgumentException} is thrown. + * + * @throws X if {@code get} throws any checked exception except for an {@code + * ExecutionException} whose cause is not itself a checked exception + * @throws UncheckedExecutionException if {@code get} throws an {@code + * ExecutionException} with a {@code RuntimeException} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} + * with an {@code Error} as its cause + * @throws CancellationException if {@code get} throws a {@code + * CancellationException} + * @throws IllegalArgumentException if {@code exceptionClass} extends {@code + * RuntimeException} or does not have a suitable constructor + * @since 10.0 + */ + @Beta + public static <V, X extends Exception> V get( + Future<V> future, Class<X> exceptionClass) throws X { + checkNotNull(future); + checkArgument(!RuntimeException.class.isAssignableFrom(exceptionClass), + "Futures.get exception type (%s) must not be a RuntimeException", + exceptionClass); + try { + return future.get(); + } catch (InterruptedException e) { + currentThread().interrupt(); + throw newWithCause(exceptionClass, e); + } catch (ExecutionException e) { + wrapAndThrowExceptionOrError(e.getCause(), exceptionClass); + throw new AssertionError(); + } + } + + /** + * Returns the result of {@link Future#get(long, TimeUnit)}, converting most + * exceptions to a new instance of the given checked exception type. This + * reduces boilerplate for a common use of {@code Future} in which it is + * unnecessary to programmatically distinguish between exception types or to + * extract other information from the exception instance. + * + * <p>Exceptions from {@code Future.get} are treated as follows: + * <ul> + * <li>Any {@link ExecutionException} has its <i>cause</i> wrapped in an + * {@code X} if the cause is a checked exception, an {@link + * UncheckedExecutionException} if the cause is a {@code + * RuntimeException}, or an {@link ExecutionError} if the cause is an + * {@code Error}. + * <li>Any {@link InterruptedException} is wrapped in an {@code X} (after + * restoring the interrupt). + * <li>Any {@link TimeoutException} is wrapped in an {@code X}. + * <li>Any {@link CancellationException} is propagated untouched, as is any + * other {@link RuntimeException} (though {@code get} implementations are + * discouraged from throwing such exceptions). + * </ul> + * + * The overall principle is to continue to treat every checked exception as a + * checked exception, every unchecked exception as an unchecked exception, and + * every error as an error. In addition, the cause of any {@code + * ExecutionException} is wrapped in order to ensure that the new stack trace + * matches that of the current thread. + * + * <p>Instances of {@code exceptionClass} are created by choosing an arbitrary + * public constructor that accepts zero or more arguments, all of type {@code + * String} or {@code Throwable} (preferring constructors with at least one + * {@code String}) and calling the constructor via reflection. If the + * exception did not already have a cause, one is set by calling {@link + * Throwable#initCause(Throwable)} on it. If no such constructor exists, an + * {@code IllegalArgumentException} is thrown. + * + * @throws X if {@code get} throws any checked exception except for an {@code + * ExecutionException} whose cause is not itself a checked exception + * @throws UncheckedExecutionException if {@code get} throws an {@code + * ExecutionException} with a {@code RuntimeException} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} + * with an {@code Error} as its cause + * @throws CancellationException if {@code get} throws a {@code + * CancellationException} + * @throws IllegalArgumentException if {@code exceptionClass} extends {@code + * RuntimeException} or does not have a suitable constructor + * @since 10.0 + */ + @Beta + public static <V, X extends Exception> V get( + Future<V> future, long timeout, TimeUnit unit, Class<X> exceptionClass) + throws X { + checkNotNull(future); + checkNotNull(unit); + checkArgument(!RuntimeException.class.isAssignableFrom(exceptionClass), + "Futures.get exception type (%s) must not be a RuntimeException", + exceptionClass); + try { + return future.get(timeout, unit); + } catch (InterruptedException e) { + currentThread().interrupt(); + throw newWithCause(exceptionClass, e); + } catch (TimeoutException e) { + throw newWithCause(exceptionClass, e); + } catch (ExecutionException e) { + wrapAndThrowExceptionOrError(e.getCause(), exceptionClass); + throw new AssertionError(); + } + } + + private static <X extends Exception> void wrapAndThrowExceptionOrError( + Throwable cause, Class<X> exceptionClass) throws X { + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } + if (cause instanceof RuntimeException) { + throw new UncheckedExecutionException(cause); + } + throw newWithCause(exceptionClass, cause); + } + + /** + * Returns the result of calling {@link Future#get()} uninterruptibly on a + * task known not to throw a checked exception. This makes {@code Future} more + * suitable for lightweight, fast-running tasks that, barring bugs in the + * code, will not fail. This gives it exception-handling behavior similar to + * that of {@code ForkJoinTask.join}. + * + * <p>Exceptions from {@code Future.get} are treated as follows: + * <ul> + * <li>Any {@link ExecutionException} has its <i>cause</i> wrapped in an + * {@link UncheckedExecutionException} (if the cause is an {@code + * Exception}) or {@link ExecutionError} (if the cause is an {@code + * Error}). + * <li>Any {@link InterruptedException} causes a retry of the {@code get} + * call. The interrupt is restored before {@code getUnchecked} returns. + * <li>Any {@link CancellationException} is propagated untouched. So is any + * other {@link RuntimeException} ({@code get} implementations are + * discouraged from throwing such exceptions). + * </ul> + * + * The overall principle is to eliminate all checked exceptions: to loop to + * avoid {@code InterruptedException}, to pass through {@code + * CancellationException}, and to wrap any exception from the underlying + * computation in an {@code UncheckedExecutionException} or {@code + * ExecutionError}. + * + * <p>For an uninterruptible {@code get} that preserves other exceptions, see + * {@link Uninterruptibles#getUninterruptibly(Future)}. + * + * @throws UncheckedExecutionException if {@code get} throws an {@code + * ExecutionException} with an {@code Exception} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} + * with an {@code Error} as its cause + * @throws CancellationException if {@code get} throws a {@code + * CancellationException} + * @since 10.0 + */ + @Beta + public static <V> V getUnchecked(Future<V> future) { + checkNotNull(future); + try { + return getUninterruptibly(future); + } catch (ExecutionException e) { + wrapAndThrowUnchecked(e.getCause()); + throw new AssertionError(); + } + } + + private static void wrapAndThrowUnchecked(Throwable cause) { + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } + /* + * It's a non-Error, non-Exception Throwable. From my survey of such + * classes, I believe that most users intended to extend Exception, so we'll + * treat it like an Exception. + */ + throw new UncheckedExecutionException(cause); + } + + /* + * TODO(user): FutureChecker interface for these to be static methods on? If + * so, refer to it in the (static-method) Futures.get documentation + */ + + /* + * Arguably we don't need a timed getUnchecked because any operation slow + * enough to require a timeout is heavyweight enough to throw a checked + * exception and therefore be inappropriate to use with getUnchecked. Further, + * it's not clear that converting the checked TimeoutException to a + * RuntimeException -- especially to an UncheckedExecutionException, since it + * wasn't thrown by the computation -- makes sense, and if we don't convert + * it, the user still has to write a try-catch block. + * + * If you think you would use this method, let us know. + */ + + private static <X extends Exception> X newWithCause( + Class<X> exceptionClass, Throwable cause) { + // getConstructors() guarantees this as long as we don't modify the array. + @SuppressWarnings("unchecked") + List<Constructor<X>> constructors = + (List) Arrays.asList(exceptionClass.getConstructors()); + for (Constructor<X> constructor : preferringStrings(constructors)) { + @Nullable X instance = newFromConstructor(constructor, cause); + if (instance != null) { + if (instance.getCause() == null) { + instance.initCause(cause); + } + return instance; + } + } + throw new IllegalArgumentException( + "No appropriate constructor for exception of type " + exceptionClass + + " in response to chained exception", cause); + } + + private static <X extends Exception> List<Constructor<X>> + preferringStrings(List<Constructor<X>> constructors) { + return WITH_STRING_PARAM_FIRST.sortedCopy(constructors); + } + + private static final Ordering<Constructor<?>> WITH_STRING_PARAM_FIRST = + Ordering.natural().onResultOf(new Function<Constructor<?>, Boolean>() { + @Override public Boolean apply(Constructor<?> input) { + return asList(input.getParameterTypes()).contains(String.class); + } + }).reverse(); + + @Nullable private static <X> X newFromConstructor( + Constructor<X> constructor, Throwable cause) { + Class<?>[] paramTypes = constructor.getParameterTypes(); + Object[] params = new Object[paramTypes.length]; + for (int i = 0; i < paramTypes.length; i++) { + Class<?> paramType = paramTypes[i]; + if (paramType.equals(String.class)) { + params[i] = cause.toString(); + } else if (paramType.equals(Throwable.class)) { + params[i] = cause; + } else { + return null; + } + } + try { + return constructor.newInstance(params); + } catch (IllegalArgumentException e) { + return null; + } catch (InstantiationException e) { + return null; + } catch (IllegalAccessException e) { + return null; + } catch (InvocationTargetException e) { + return null; + } + } + + /** + * Class that implements {@link #allAsList} and {@link #successfulAsList}. + * The idea is to create a (null-filled) List and register a listener with + * each component future to fill out the value in the List when that future + * completes. + */ + private static class ListFuture<V> extends AbstractFuture<List<V>> { + ImmutableList<? extends ListenableFuture<? extends V>> futures; + final boolean allMustSucceed; + final AtomicInteger remaining; + List<V> values; + + /** + * Constructor. + * + * @param futures all the futures to build the list from + * @param allMustSucceed whether a single failure or cancellation should + * propagate to this future + * @param listenerExecutor used to run listeners on all the passed in + * futures. + */ + ListFuture( + final ImmutableList<? extends ListenableFuture<? extends V>> futures, + final boolean allMustSucceed, final Executor listenerExecutor) { + this.futures = futures; + this.values = Lists.newArrayListWithCapacity(futures.size()); + this.allMustSucceed = allMustSucceed; + this.remaining = new AtomicInteger(futures.size()); + + init(listenerExecutor); + } + + private void init(final Executor listenerExecutor) { + // First, schedule cleanup to execute when the Future is done. + addListener(new Runnable() { + @Override + public void run() { + // By now the values array has either been set as the Future's value, + // or (in case of failure) is no longer useful. + ListFuture.this.values = null; + + // Let go of the memory held by other futures + ListFuture.this.futures = null; + } + }, MoreExecutors.sameThreadExecutor()); + + // Now begin the "real" initialization. + + // Corner case: List is empty. + if (futures.isEmpty()) { + set(Lists.newArrayList(values)); + return; + } + + // Populate the results list with null initially. + for (int i = 0; i < futures.size(); ++i) { + values.add(null); + } + + // Register a listener on each Future in the list to update + // the state of this future. + // Note that if all the futures on the list are done prior to completing + // this loop, the last call to addListener() will callback to + // setOneValue(), transitively call our cleanup listener, and set + // this.futures to null. + // We store a reference to futures to avoid the NPE. + ImmutableList<? extends ListenableFuture<? extends V>> localFutures = futures; + for (int i = 0; i < localFutures.size(); i++) { + final ListenableFuture<? extends V> listenable = localFutures.get(i); + final int index = i; + listenable.addListener(new Runnable() { + @Override + public void run() { + setOneValue(index, listenable); + } + }, listenerExecutor); + } + } + + /** + * Sets the value at the given index to that of the given future. + */ + private void setOneValue(int index, Future<? extends V> future) { + List<V> localValues = values; + if (isDone() || localValues == null) { + // Some other future failed or has been cancelled, causing this one to + // also be cancelled or have an exception set. This should only happen + // if allMustSucceed is true. + checkState(allMustSucceed, + "Future was done before all dependencies completed"); + return; + } + + try { + checkState(future.isDone(), + "Tried to set value from future which is not done"); + localValues.set(index, getUninterruptibly(future)); + } catch (CancellationException e) { + if (allMustSucceed) { + // Set ourselves as cancelled. Let the input futures keep running + // as some of them may be used elsewhere. + // (Currently we don't override interruptTask, so + // mayInterruptIfRunning==false isn't technically necessary.) + cancel(false); + } + } catch (ExecutionException e) { + if (allMustSucceed) { + // As soon as the first one fails, throw the exception up. + // The result of all other inputs is then ignored. + setException(e.getCause()); + } + } catch (RuntimeException e) { + if (allMustSucceed) { + setException(e); + } + } catch (Error e) { + // Propagate errors up ASAP - our superclass will rethrow the error + setException(e); + } finally { + int newRemaining = remaining.decrementAndGet(); + checkState(newRemaining >= 0, "Less than 0 remaining futures"); + if (newRemaining == 0) { + localValues = values; + if (localValues != null) { + set(Lists.newArrayList(localValues)); + } else { + checkState(isDone()); + } + } + } + } + + } + + /** + * A checked future that uses a function to map from exceptions to the + * appropriate checked type. + */ + private static class MappingCheckedFuture<V, X extends Exception> extends + AbstractCheckedFuture<V, X> { + + final Function<Exception, X> mapper; + + MappingCheckedFuture(ListenableFuture<V> delegate, + Function<Exception, X> mapper) { + super(delegate); + + this.mapper = checkNotNull(mapper); + } + + @Override + protected X mapException(Exception e) { + return mapper.apply(e); + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java b/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java new file mode 100644 index 0000000..645a648 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Utilities necessary for working with libraries that supply plain {@link + * Future} instances. Note that, whenver possible, it is strongly preferred to + * modify those libraries to return {@code ListenableFuture} directly. + * + * @author Sven Mawson + * @since 10.0 (replacing {@code Futures.makeListenable}, which + * existed in 1.0) + */ +@Beta +public final class JdkFutureAdapters { + /** + * Assigns a thread to the given {@link Future} to provide {@link + * ListenableFuture} functionality. + * + * <p><b>Warning:</b> If the input future does not already implement {@code + * ListenableFuture}, the returned future will emulate {@link + * ListenableFuture#addListener} by taking a thread from an internal, + * unbounded pool at the first call to {@code addListener} and holding it + * until the future is {@linkplain Future#isDone() done}. + * + * <p>Prefer to create {@code ListenableFuture} instances with {@link + * SettableFuture}, {@link MoreExecutors#listeningDecorator( + * java.util.concurrent.ExecutorService)}, {@link ListenableFutureTask}, + * {@link AbstractFuture}, and other utilities over creating plain {@code + * Future} instances to be upgraded to {@code ListenableFuture} after the + * fact. + */ + public static <V> ListenableFuture<V> listenInPoolThread( + Future<V> future) { + if (future instanceof ListenableFuture) { + return (ListenableFuture<V>) future; + } + return new ListenableFutureAdapter<V>(future); + } + + /** + * Submits a blocking task for the given {@link Future} to provide {@link + * ListenableFuture} functionality. + * + * <p><b>Warning:</b> If the input future does not already implement {@code + * ListenableFuture}, the returned future will emulate {@link + * ListenableFuture#addListener} by submitting a task to the given executor at + * at the first call to {@code addListener}. The task must be started by the + * executor promptly, or else the returned {@code ListenableFuture} may fail + * to work. The task's execution consists of blocking until the input future + * is {@linkplain Future#isDone() done}, so each call to this method may + * claim and hold a thread for an arbitrary length of time. Use of bounded + * executors or other executors that may fail to execute a task promptly may + * result in deadlocks. + * + * <p>Prefer to create {@code ListenableFuture} instances with {@link + * SettableFuture}, {@link MoreExecutors#listeningDecorator( + * java.util.concurrent.ExecutorService)}, {@link ListenableFutureTask}, + * {@link AbstractFuture}, and other utilities over creating plain {@code + * Future} instances to be upgraded to {@code ListenableFuture} after the + * fact. + * + * @since 12.0 + */ + public static <V> ListenableFuture<V> listenInPoolThread( + Future<V> future, Executor executor) { + checkNotNull(executor); + if (future instanceof ListenableFuture) { + return (ListenableFuture<V>) future; + } + return new ListenableFutureAdapter<V>(future, executor); + } + + /** + * An adapter to turn a {@link Future} into a {@link ListenableFuture}. This + * will wait on the future to finish, and when it completes, run the + * listeners. This implementation will wait on the source future + * indefinitely, so if the source future never completes, the adapter will + * never complete either. + * + * <p>If the delegate future is interrupted or throws an unexpected unchecked + * exception, the listeners will not be invoked. + */ + private static class ListenableFutureAdapter<V> extends ForwardingFuture<V> + implements ListenableFuture<V> { + + private static final ThreadFactory threadFactory = + new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("ListenableFutureAdapter-thread-%d") + .build(); + private static final Executor defaultAdapterExecutor = + Executors.newCachedThreadPool(threadFactory); + + private final Executor adapterExecutor; + + // The execution list to hold our listeners. + private final ExecutionList executionList = new ExecutionList(); + + // This allows us to only start up a thread waiting on the delegate future + // when the first listener is added. + private final AtomicBoolean hasListeners = new AtomicBoolean(false); + + // The delegate future. + private final Future<V> delegate; + + ListenableFutureAdapter(Future<V> delegate) { + this(delegate, defaultAdapterExecutor); + } + + ListenableFutureAdapter(Future<V> delegate, Executor adapterExecutor) { + this.delegate = checkNotNull(delegate); + this.adapterExecutor = checkNotNull(adapterExecutor); + } + + @Override + protected Future<V> delegate() { + return delegate; + } + + @Override + public void addListener(Runnable listener, Executor exec) { + executionList.add(listener, exec); + + // When a listener is first added, we run a task that will wait for + // the delegate to finish, and when it is done will run the listeners. + if (hasListeners.compareAndSet(false, true)) { + if (delegate.isDone()) { + // If the delegate is already done, run the execution list + // immediately on the current thread. + executionList.execute(); + return; + } + + adapterExecutor.execute(new Runnable() { + @Override + public void run() { + try { + delegate.get(); + } catch (Error e) { + throw e; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + // Threads from our private pool are never interrupted. + throw new AssertionError(e); + } catch (Throwable e) { + // ExecutionException / CancellationException / RuntimeException + // The task is done, run the listeners. + } + executionList.execute(); + } + }); + } + } + } + + private JdkFutureAdapters() {} +} diff --git a/guava/src/com/google/common/util/concurrent/ListenableFuture.java b/guava/src/com/google/common/util/concurrent/ListenableFuture.java new file mode 100644 index 0000000..eb05354 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ListenableFuture.java @@ -0,0 +1,135 @@ +/* + * 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 com.google.common.util.concurrent; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionException; + +/** + * 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> + * + * 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> + * + * 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> + * + * 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 the input {@code Future} is done at the time {@code addListener} is + * called, {@code addListener} will execute the listener inline. + * <li>If the input {@code Future} is not yet done, {@code addListener} will + * schedule the listener to be run by the thread that completes the input + * {@code Future}, which may be an internal system thread such as an RPC + * network thread. + * </ul> + * + * Also note that, regardless of which thread executes the 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 + * com.google.common.util.concurrent.Futures}. For a simplified but general + * listener interface, see {@link + * com.google.common.util.concurrent.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); +} diff --git a/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java b/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java new file mode 100644 index 0000000..28c2a0a --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java @@ -0,0 +1,90 @@ +/* + * 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 com.google.common.util.concurrent; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; + +import javax.annotation.Nullable; + +/** + * 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 final class ListenableFutureTask<V> extends FutureTask<V> + implements ListenableFuture<V> { + + // 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, @Nullable V result) { + return new ListenableFutureTask<V>(runnable, result); + } + + private ListenableFutureTask(Callable<V> callable) { + super(callable); + } + + private ListenableFutureTask(Runnable runnable, @Nullable 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(); + } +} diff --git a/guava/src/com/google/common/util/concurrent/ListeningExecutorService.java b/guava/src/com/google/common/util/concurrent/ListeningExecutorService.java new file mode 100644 index 0000000..9ea8e48 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ListeningExecutorService.java @@ -0,0 +1,88 @@ +/* + * 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 com.google.common.util.concurrent; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * An {@link ExecutorService} that returns {@link ListenableFuture} instances. To create an instance + * from an existing {@link ExecutorService}, call + * {@link MoreExecutors#listeningDecorator(ExecutorService)}. + * + * @author Chris Povirk + * @since 10.0 + */ +public interface ListeningExecutorService extends ExecutorService { + /** + * @return a {@code ListenableFuture} representing pending completion of the task + * @throws RejectedExecutionException {@inheritDoc} + */ + @Override + <T> ListenableFuture<T> submit(Callable<T> task); + + /** + * @return a {@code ListenableFuture} representing pending completion of the task + * @throws RejectedExecutionException {@inheritDoc} + */ + @Override + ListenableFuture<?> submit(Runnable task); + + /** + * @return a {@code ListenableFuture} representing pending completion of the task + * @throws RejectedExecutionException {@inheritDoc} + */ + @Override + <T> ListenableFuture<T> submit(Runnable task, T result); + + /** + * {@inheritDoc} + * + * <p>All elements in the returned list must be {@link ListenableFuture} instances. + * + * @return A list of {@code ListenableFuture} instances representing the tasks, in the same + * sequential order as produced by the iterator for the given task list, each of which has + * completed. + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException if any task is null + */ + @Override + <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) + throws InterruptedException; + + /** + * {@inheritDoc} + * + * <p>All elements in the returned list must be {@link ListenableFuture} instances. + * + * @return a list of {@code ListenableFuture} instances representing the tasks, in the same + * sequential order as produced by the iterator for the given task list. If the operation + * did not time out, each task will have completed. If it did time out, some of these + * tasks will not have completed. + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException if any task is null + */ + @Override + <T> List<Future<T>> invokeAll( + Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException; +} diff --git a/guava/src/com/google/common/util/concurrent/ListeningScheduledExecutorService.java b/guava/src/com/google/common/util/concurrent/ListeningScheduledExecutorService.java new file mode 100644 index 0000000..42dcdd2 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ListeningScheduledExecutorService.java @@ -0,0 +1,40 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.ScheduledExecutorService; + +/** + * A {@link ScheduledExecutorService} that returns {@link ListenableFuture} + * instances from its {@code ExecutorService} methods. Futures returned by the + * {@code schedule*} methods, by contrast, need not implement {@code + * ListenableFuture}. (To create an instance from an existing {@link + * ScheduledExecutorService}, call {@link + * MoreExecutors#listeningDecorator(ScheduledExecutorService)}. + * + * <p>TODO(cpovirk): make at least the one-time schedule() methods return a + * ListenableFuture, too? But then we'll need ListenableScheduledFuture... + * + * @author Chris Povirk + * @since 10.0 + */ +@Beta +public interface ListeningScheduledExecutorService + extends ScheduledExecutorService, ListeningExecutorService { +} diff --git a/guava/src/com/google/common/util/concurrent/Monitor.java b/guava/src/com/google/common/util/concurrent/Monitor.java new file mode 100644 index 0000000..d8c8693 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/Monitor.java @@ -0,0 +1,942 @@ +/* + * 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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; + +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; + +/** + * A synchronization abstraction supporting waiting on arbitrary boolean conditions. + * + * <p>This class is intended as a replacement for {@link ReentrantLock}. Code using {@code Monitor} + * is less error-prone and more readable than code using {@code ReentrantLock}, without significant + * performance loss. {@code Monitor} even has the potential for performance gain by optimizing the + * evaluation and signaling of conditions. Signaling is entirely + * <a href="http://en.wikipedia.org/wiki/Monitor_(synchronization)#Implicit_signaling"> + * implicit</a>. + * By eliminating explicit signaling, this class can guarantee that only one thread is awakened + * when a condition becomes true (no "signaling storms" due to use of {@link + * java.util.concurrent.locks.Condition#signalAll Condition.signalAll}) and that no signals are lost + * (no "hangs" due to incorrect use of {@link java.util.concurrent.locks.Condition#signal + * Condition.signal}). + * + * <p>A thread is said to <i>occupy</i> a monitor if it has <i>entered</i> the monitor but not yet + * <i>left</i>. Only one thread may occupy a given monitor at any moment. A monitor is also + * reentrant, so a thread may enter a monitor any number of times, and then must leave the same + * number of times. The <i>enter</i> and <i>leave</i> operations have the same synchronization + * semantics as the built-in Java language synchronization primitives. + * + * <p>A call to any of the <i>enter</i> methods with <b>void</b> return type should always be + * followed immediately by a <i>try/finally</i> block to ensure that the current thread leaves the + * monitor cleanly: <pre> {@code + * + * monitor.enter(); + * try { + * // do things while occupying the monitor + * } finally { + * monitor.leave(); + * }}</pre> + * + * A call to any of the <i>enter</i> methods with <b>boolean</b> return type should always appear as + * the condition of an <i>if</i> statement containing a <i>try/finally</i> block to ensure that the + * current thread leaves the monitor cleanly: <pre> {@code + * + * if (monitor.tryEnter()) { + * try { + * // do things while occupying the monitor + * } finally { + * monitor.leave(); + * } + * } else { + * // do other things since the monitor was not available + * }}</pre> + * + * <h2>Comparison with {@code synchronized} and {@code ReentrantLock}</h2> + * + * <p>The following examples show a simple threadsafe holder expressed using {@code synchronized}, + * {@link ReentrantLock}, and {@code Monitor}. + * + * <h3>{@code synchronized}</h3> + * + * <p>This version is the fewest lines of code, largely because the synchronization mechanism used + * is built into the language and runtime. But the programmer has to remember to avoid a couple of + * common bugs: The {@code wait()} must be inside a {@code while} instead of an {@code if}, and + * {@code notifyAll()} must be used instead of {@code notify()} because there are two different + * logical conditions being awaited. <pre> {@code + * + * public class SafeBox<V> { + * private V value; + * + * public synchronized V get() throws InterruptedException { + * while (value == null) { + * wait(); + * } + * V result = value; + * value = null; + * notifyAll(); + * return result; + * } + * + * public synchronized void set(V newValue) throws InterruptedException { + * while (value != null) { + * wait(); + * } + * value = newValue; + * notifyAll(); + * } + * }}</pre> + * + * <h3>{@code ReentrantLock}</h3> + * + * <p>This version is much more verbose than the {@code synchronized} version, and still suffers + * from the need for the programmer to remember to use {@code while} instead of {@code if}. + * However, one advantage is that we can introduce two separate {@code Condition} objects, which + * allows us to use {@code signal()} instead of {@code signalAll()}, which may be a performance + * benefit. <pre> {@code + * + * public class SafeBox<V> { + * private final ReentrantLock lock = new ReentrantLock(); + * private final Condition valuePresent = lock.newCondition(); + * private final Condition valueAbsent = lock.newCondition(); + * private V value; + * + * public V get() throws InterruptedException { + * lock.lock(); + * try { + * while (value == null) { + * valuePresent.await(); + * } + * V result = value; + * value = null; + * valueAbsent.signal(); + * return result; + * } finally { + * lock.unlock(); + * } + * } + * + * public void set(V newValue) throws InterruptedException { + * lock.lock(); + * try { + * while (value != null) { + * valueAbsent.await(); + * } + * value = newValue; + * valuePresent.signal(); + * } finally { + * lock.unlock(); + * } + * } + * }}</pre> + * + * <h3>{@code Monitor}</h3> + * + * <p>This version adds some verbosity around the {@code Guard} objects, but removes that same + * verbosity, and more, from the {@code get} and {@code set} methods. {@code Monitor} implements the + * same efficient signaling as we had to hand-code in the {@code ReentrantLock} version above. + * Finally, the programmer no longer has to hand-code the wait loop, and therefore doesn't have to + * remember to use {@code while} instead of {@code if}. <pre> {@code + * + * public class SafeBox<V> { + * private final Monitor monitor = new Monitor(); + * private final Monitor.Guard valuePresent = new Monitor.Guard(monitor) { + * public boolean isSatisfied() { + * return value != null; + * } + * }; + * private final Monitor.Guard valueAbsent = new Monitor.Guard(monitor) { + * public boolean isSatisfied() { + * return value == null; + * } + * }; + * private V value; + * + * public V get() throws InterruptedException { + * monitor.enterWhen(valuePresent); + * try { + * V result = value; + * value = null; + * return result; + * } finally { + * monitor.leave(); + * } + * } + * + * public void set(V newValue) throws InterruptedException { + * monitor.enterWhen(valueAbsent); + * try { + * value = newValue; + * } finally { + * monitor.leave(); + * } + * } + * }}</pre> + * + * @author Justin T. Sampson + * @since 10.0 + */ +@Beta +public final class Monitor { + // TODO: Use raw LockSupport or AbstractQueuedSynchronizer instead of ReentrantLock. + + /** + * A boolean condition for which a thread may wait. A {@code Guard} is associated with a single + * {@code Monitor}. The monitor may check the guard at arbitrary times from any thread occupying + * the monitor, so code should not be written to rely on how often a guard might or might not be + * checked. + * + * <p>If a {@code Guard} is passed into any method of a {@code Monitor} other than the one it is + * associated with, an {@link IllegalMonitorStateException} is thrown. + * + * @since 10.0 + */ + @Beta + public abstract static class Guard { + + final Monitor monitor; + final Condition condition; + + @GuardedBy("monitor.lock") + int waiterCount = 0; + + protected Guard(Monitor monitor) { + this.monitor = checkNotNull(monitor, "monitor"); + this.condition = monitor.lock.newCondition(); + } + + /** + * Evaluates this guard's boolean condition. This method is always called with the associated + * monitor already occupied. Implementations of this method must depend only on state protected + * by the associated monitor, and must not modify that state. + */ + public abstract boolean isSatisfied(); + + @Override + public final boolean equals(Object other) { + // Overridden as final to ensure identity semantics in Monitor.activeGuards. + return this == other; + } + + @Override + public final int hashCode() { + // Overridden as final to ensure identity semantics in Monitor.activeGuards. + return super.hashCode(); + } + + } + + /** + * Whether this monitor is fair. + */ + private final boolean fair; + + /** + * The lock underlying this monitor. + */ + private final ReentrantLock lock; + + /** + * The guards associated with this monitor that currently have waiters ({@code waiterCount > 0}). + * This is an ArrayList rather than, say, a HashSet so that iteration and almost all adds don't + * incur any object allocation overhead. + */ + @GuardedBy("lock") + private final ArrayList<Guard> activeGuards = Lists.newArrayListWithCapacity(1); + + /** + * Creates a monitor with a non-fair (but fast) ordering policy. Equivalent to {@code + * Monitor(false)}. + */ + public Monitor() { + this(false); + } + + /** + * Creates a monitor with the given ordering policy. + * + * @param fair whether this monitor should use a fair ordering policy rather than a non-fair (but + * fast) one + */ + public Monitor(boolean fair) { + this.fair = fair; + this.lock = new ReentrantLock(fair); + } + + /** + * Enters this monitor. Blocks indefinitely. + */ + public void enter() { + lock.lock(); + } + + /** + * Enters this monitor. Blocks indefinitely, but may be interrupted. + */ + public void enterInterruptibly() throws InterruptedException { + lock.lockInterruptibly(); + } + + /** + * Enters this monitor. Blocks at most the given time. + * + * @return whether the monitor was entered + */ + public boolean enter(long time, TimeUnit unit) { + final ReentrantLock lock = this.lock; + if (!fair && lock.tryLock()) { + return true; + } + long startNanos = System.nanoTime(); + long timeoutNanos = unit.toNanos(time); + long remainingNanos = timeoutNanos; + boolean interruptIgnored = false; + try { + while (true) { + try { + return lock.tryLock(remainingNanos, TimeUnit.NANOSECONDS); + } catch (InterruptedException ignored) { + interruptIgnored = true; + remainingNanos = (timeoutNanos - (System.nanoTime() - startNanos)); + } + } + } finally { + if (interruptIgnored) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Enters this monitor. Blocks at most the given time, and may be interrupted. + * + * @return whether the monitor was entered + */ + public boolean enterInterruptibly(long time, TimeUnit unit) throws InterruptedException { + return lock.tryLock(time, unit); + } + + /** + * Enters this monitor if it is possible to do so immediately. Does not block. + * + * <p><b>Note:</b> This method disregards the fairness setting of this monitor. + * + * @return whether the monitor was entered + */ + public boolean tryEnter() { + return lock.tryLock(); + } + + /** + * Enters this monitor when the guard is satisfied. Blocks indefinitely, but may be interrupted. + */ + public void enterWhen(Guard guard) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + boolean reentrant = lock.isHeldByCurrentThread(); + boolean success = false; + lock.lockInterruptibly(); + try { + waitInterruptibly(guard, reentrant); + success = true; + } finally { + if (!success) { + lock.unlock(); + } + } + } + + /** + * Enters this monitor when the guard is satisfied. Blocks indefinitely. + */ + public void enterWhenUninterruptibly(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + boolean reentrant = lock.isHeldByCurrentThread(); + boolean success = false; + lock.lock(); + try { + waitUninterruptibly(guard, reentrant); + success = true; + } finally { + if (!success) { + lock.unlock(); + } + } + } + + /** + * Enters this monitor when the guard is satisfied. Blocks at most the given time, including both + * the time to acquire the lock and the time to wait for the guard to be satisfied, and may be + * interrupted. + * + * @return whether the monitor was entered + */ + public boolean enterWhen(Guard guard, long time, TimeUnit unit) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + boolean reentrant = lock.isHeldByCurrentThread(); + long remainingNanos; + if (!fair && lock.tryLock()) { + remainingNanos = unit.toNanos(time); + } else { + long startNanos = System.nanoTime(); + if (!lock.tryLock(time, unit)) { + return false; + } + remainingNanos = unit.toNanos(time) - (System.nanoTime() - startNanos); + } + boolean satisfied = false; + try { + satisfied = waitInterruptibly(guard, remainingNanos, reentrant); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + return satisfied; + } + + /** + * Enters this monitor when the guard is satisfied. Blocks at most the given time, including + * both the time to acquire the lock and the time to wait for the guard to be satisfied. + * + * @return whether the monitor was entered + */ + public boolean enterWhenUninterruptibly(Guard guard, long time, TimeUnit unit) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + boolean reentrant = lock.isHeldByCurrentThread(); + boolean interruptIgnored = false; + try { + long remainingNanos; + if (!fair && lock.tryLock()) { + remainingNanos = unit.toNanos(time); + } else { + long startNanos = System.nanoTime(); + long timeoutNanos = unit.toNanos(time); + remainingNanos = timeoutNanos; + while (true) { + try { + if (lock.tryLock(remainingNanos, TimeUnit.NANOSECONDS)) { + break; + } else { + return false; + } + } catch (InterruptedException ignored) { + interruptIgnored = true; + } finally { + remainingNanos = (timeoutNanos - (System.nanoTime() - startNanos)); + } + } + } + boolean satisfied = false; + try { + satisfied = waitUninterruptibly(guard, remainingNanos, reentrant); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + return satisfied; + } finally { + if (interruptIgnored) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Enters this monitor if the guard is satisfied. Blocks indefinitely acquiring the lock, but + * does not wait for the guard to be satisfied. + * + * @return whether the monitor was entered + */ + public boolean enterIf(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + lock.lock(); + boolean satisfied = false; + try { + satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + return satisfied; + } + + /** + * Enters this monitor if the guard is satisfied. Blocks indefinitely acquiring the lock, but does + * not wait for the guard to be satisfied, and may be interrupted. + * + * @return whether the monitor was entered + */ + public boolean enterIfInterruptibly(Guard guard) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + boolean satisfied = false; + try { + satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + return satisfied; + } + + /** + * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the + * lock, but does not wait for the guard to be satisfied. + * + * @return whether the monitor was entered + */ + public boolean enterIf(Guard guard, long time, TimeUnit unit) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + if (!enter(time, unit)) { + return false; + } + boolean satisfied = false; + try { + satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + return satisfied; + } + + /** + * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the + * lock, but does not wait for the guard to be satisfied, and may be interrupted. + * + * @return whether the monitor was entered + */ + public boolean enterIfInterruptibly(Guard guard, long time, TimeUnit unit) + throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + if (!lock.tryLock(time, unit)) { + return false; + } + boolean satisfied = false; + try { + satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + return satisfied; + } + + /** + * Enters this monitor if it is possible to do so immediately and the guard is satisfied. Does not + * block acquiring the lock and does not wait for the guard to be satisfied. + * + * <p><b>Note:</b> This method disregards the fairness setting of this monitor. + * + * @return whether the monitor was entered + */ + public boolean tryEnterIf(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + if (!lock.tryLock()) { + return false; + } + boolean satisfied = false; + try { + satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + return satisfied; + } + + /** + * Waits for the guard to be satisfied. Waits indefinitely, but may be interrupted. May be + * called only by a thread currently occupying this monitor. + */ + public void waitFor(Guard guard) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + if (!lock.isHeldByCurrentThread()) { + throw new IllegalMonitorStateException(); + } + waitInterruptibly(guard, true); + } + + /** + * Waits for the guard to be satisfied. Waits indefinitely. May be called only by a thread + * currently occupying this monitor. + */ + public void waitForUninterruptibly(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + if (!lock.isHeldByCurrentThread()) { + throw new IllegalMonitorStateException(); + } + waitUninterruptibly(guard, true); + } + + /** + * Waits for the guard to be satisfied. Waits at most the given time, and may be interrupted. + * May be called only by a thread currently occupying this monitor. + * + * @return whether the guard is now satisfied + */ + public boolean waitFor(Guard guard, long time, TimeUnit unit) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + if (!lock.isHeldByCurrentThread()) { + throw new IllegalMonitorStateException(); + } + return waitInterruptibly(guard, unit.toNanos(time), true); + } + + /** + * Waits for the guard to be satisfied. Waits at most the given time. May be called only by a + * thread currently occupying this monitor. + * + * @return whether the guard is now satisfied + */ + public boolean waitForUninterruptibly(Guard guard, long time, TimeUnit unit) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + if (!lock.isHeldByCurrentThread()) { + throw new IllegalMonitorStateException(); + } + return waitUninterruptibly(guard, unit.toNanos(time), true); + } + + /** + * Leaves this monitor. May be called only by a thread currently occupying this monitor. + */ + public void leave() { + final ReentrantLock lock = this.lock; + if (!lock.isHeldByCurrentThread()) { + throw new IllegalMonitorStateException(); + } + try { + signalConditionsOfSatisfiedGuards(null); + } finally { + lock.unlock(); + } + } + + /** + * Returns whether this monitor is using a fair ordering policy. + */ + public boolean isFair() { + return lock.isFair(); + } + + /** + * Returns whether this monitor is occupied by any thread. This method is designed for use in + * monitoring of the system state, not for synchronization control. + */ + public boolean isOccupied() { + return lock.isLocked(); + } + + /** + * Returns whether the current thread is occupying this monitor (has entered more times than it + * has left). + */ + public boolean isOccupiedByCurrentThread() { + return lock.isHeldByCurrentThread(); + } + + /** + * Returns the number of times the current thread has entered this monitor in excess of the number + * of times it has left. Returns 0 if the current thread is not occupying this monitor. + */ + public int getOccupiedDepth() { + return lock.getHoldCount(); + } + + /** + * Returns an estimate of the number of threads waiting to enter this monitor. The value is only + * an estimate because the number of threads may change dynamically while this method traverses + * internal data structures. This method is designed for use in monitoring of the system state, + * not for synchronization control. + */ + public int getQueueLength() { + return lock.getQueueLength(); + } + + /** + * Returns whether any threads are waiting to enter this monitor. Note that because cancellations + * may occur at any time, a {@code true} return does not guarantee that any other thread will ever + * enter this monitor. This method is designed primarily for use in monitoring of the system + * state. + */ + public boolean hasQueuedThreads() { + return lock.hasQueuedThreads(); + } + + /** + * Queries whether the given thread is waiting to enter this monitor. Note that because + * cancellations may occur at any time, a {@code true} return does not guarantee that this thread + * will ever enter this monitor. This method is designed primarily for use in monitoring of the + * system state. + */ + public boolean hasQueuedThread(Thread thread) { + return lock.hasQueuedThread(thread); + } + + /** + * Queries whether any threads are waiting for the given guard to become satisfied. Note that + * because timeouts and interrupts may occur at any time, a {@code true} return does not guarantee + * that the guard becoming satisfied in the future will awaken any threads. This method is + * designed primarily for use in monitoring of the system state. + */ + public boolean hasWaiters(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + lock.lock(); + try { + return guard.waiterCount > 0; + } finally { + lock.unlock(); + } + } + + /** + * Returns an estimate of the number of threads waiting for the given guard to become satisfied. + * Note that because timeouts and interrupts may occur at any time, the estimate serves only as an + * upper bound on the actual number of waiters. This method is designed for use in monitoring of + * the system state, not for synchronization control. + */ + public int getWaitQueueLength(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + lock.lock(); + try { + return guard.waiterCount; + } finally { + lock.unlock(); + } + } + + @GuardedBy("lock") + private void signalConditionsOfSatisfiedGuards(@Nullable Guard interruptedGuard) { + final ArrayList<Guard> guards = this.activeGuards; + final int guardCount = guards.size(); + try { + for (int i = 0; i < guardCount; i++) { + Guard guard = guards.get(i); + if ((guard == interruptedGuard) && (guard.waiterCount == 1)) { + // That one waiter was just interrupted and is throwing InterruptedException rather than + // paying attention to the guard being satisfied, so find another waiter on another guard. + continue; + } + if (guard.isSatisfied()) { + guard.condition.signal(); + return; + } + } + } catch (Throwable throwable) { + for (int i = 0; i < guardCount; i++) { + Guard guard = guards.get(i); + guard.condition.signalAll(); + } + throw Throwables.propagate(throwable); + } + } + + @GuardedBy("lock") + private void incrementWaiters(Guard guard) { + int waiters = guard.waiterCount++; + if (waiters == 0) { + activeGuards.add(guard); + } + } + + @GuardedBy("lock") + private void decrementWaiters(Guard guard) { + int waiters = --guard.waiterCount; + if (waiters == 0) { + activeGuards.remove(guard); + } + } + + @GuardedBy("lock") + private void waitInterruptibly(Guard guard, boolean signalBeforeWaiting) + throws InterruptedException { + if (!guard.isSatisfied()) { + if (signalBeforeWaiting) { + signalConditionsOfSatisfiedGuards(null); + } + incrementWaiters(guard); + try { + final Condition condition = guard.condition; + do { + try { + condition.await(); + } catch (InterruptedException interrupt) { + try { + signalConditionsOfSatisfiedGuards(guard); + } catch (Throwable throwable) { + Thread.currentThread().interrupt(); + throw Throwables.propagate(throwable); + } + throw interrupt; + } + } while (!guard.isSatisfied()); + } finally { + decrementWaiters(guard); + } + } + } + + @GuardedBy("lock") + private void waitUninterruptibly(Guard guard, boolean signalBeforeWaiting) { + if (!guard.isSatisfied()) { + if (signalBeforeWaiting) { + signalConditionsOfSatisfiedGuards(null); + } + incrementWaiters(guard); + try { + final Condition condition = guard.condition; + do { + condition.awaitUninterruptibly(); + } while (!guard.isSatisfied()); + } finally { + decrementWaiters(guard); + } + } + } + + @GuardedBy("lock") + private boolean waitInterruptibly(Guard guard, long remainingNanos, boolean signalBeforeWaiting) + throws InterruptedException { + if (!guard.isSatisfied()) { + if (signalBeforeWaiting) { + signalConditionsOfSatisfiedGuards(null); + } + incrementWaiters(guard); + try { + final Condition condition = guard.condition; + do { + if (remainingNanos <= 0) { + return false; + } + try { + remainingNanos = condition.awaitNanos(remainingNanos); + } catch (InterruptedException interrupt) { + try { + signalConditionsOfSatisfiedGuards(guard); + } catch (Throwable throwable) { + Thread.currentThread().interrupt(); + throw Throwables.propagate(throwable); + } + throw interrupt; + } + } while (!guard.isSatisfied()); + } finally { + decrementWaiters(guard); + } + } + return true; + } + + @GuardedBy("lock") + private boolean waitUninterruptibly(Guard guard, long timeoutNanos, + boolean signalBeforeWaiting) { + if (!guard.isSatisfied()) { + long startNanos = System.nanoTime(); + if (signalBeforeWaiting) { + signalConditionsOfSatisfiedGuards(null); + } + boolean interruptIgnored = false; + try { + incrementWaiters(guard); + try { + final Condition condition = guard.condition; + long remainingNanos = timeoutNanos; + do { + if (remainingNanos <= 0) { + return false; + } + try { + remainingNanos = condition.awaitNanos(remainingNanos); + } catch (InterruptedException ignored) { + try { + signalConditionsOfSatisfiedGuards(guard); + } catch (Throwable throwable) { + Thread.currentThread().interrupt(); + throw Throwables.propagate(throwable); + } + interruptIgnored = true; + remainingNanos = (timeoutNanos - (System.nanoTime() - startNanos)); + } + } while (!guard.isSatisfied()); + } finally { + decrementWaiters(guard); + } + } finally { + if (interruptIgnored) { + Thread.currentThread().interrupt(); + } + } + } + return true; + } + +} diff --git a/guava/src/com/google/common/util/concurrent/MoreExecutors.java b/guava/src/com/google/common/util/concurrent/MoreExecutors.java new file mode 100644 index 0000000..8699b5a --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/MoreExecutors.java @@ -0,0 +1,587 @@ +/* + * 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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.collect.Lists; +import com.google.common.collect.Queues; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +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 + */ +public 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) { + executor.setThreadFactory(new ThreadFactoryBuilder() + .setDaemon(true) + .setThreadFactory(executor.getThreadFactory()) + .build()); + + ExecutorService service = Executors.unconfigurableExecutorService(executor); + + addDelayedShutdownHook(service, terminationTimeout, timeUnit); + + return service; + } + + /** + * 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) { + executor.setThreadFactory(new ThreadFactoryBuilder() + .setDaemon(true) + .setThreadFactory(executor.getThreadFactory()) + .build()); + + ScheduledExecutorService service = + Executors.unconfigurableScheduledExecutorService(executor); + + addDelayedShutdownHook(service, terminationTimeout, timeUnit); + + return service; + } + + /** + * 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( + final ExecutorService service, final long terminationTimeout, + final TimeUnit timeUnit) { + Runtime.getRuntime().addShutdownHook(new Thread(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. + } + } + }, "DelayedShutdownHook-for-" + service)); + } + + /** + * 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 getExitingExecutorService(executor, 120, TimeUnit.SECONDS); + } + + /** + * 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 getExitingScheduledExecutorService(executor, 120, TimeUnit.SECONDS); + } + + /** + * 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 ListeningExecutorService sameThreadExecutor() { + return new SameThreadExecutorService(); + } + + // See sameThreadExecutor javadoc for behavioral notes. + private static class SameThreadExecutorService + extends AbstractListeningExecutorService { + /** + * 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(); + } + } + } + + /** + * 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 + * SchedulingListeningExecutorService} 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 + * SchedulingListeningExecutorService}. + * + * <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 { + 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 { + @SuppressWarnings("hiding") + final ScheduledExecutorService delegate; + + ScheduledListeningDecorator(ScheduledExecutorService delegate) { + super(delegate); + this.delegate = checkNotNull(delegate); + } + + @Override + public ScheduledFuture<?> schedule( + Runnable command, long delay, TimeUnit unit) { + return delegate.schedule(command, delay, unit); + } + + @Override + public <V> ScheduledFuture<V> schedule( + Callable<V> callable, long delay, TimeUnit unit) { + return delegate.schedule(callable, delay, unit); + } + + @Override + public ScheduledFuture<?> scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit) { + return delegate.scheduleAtFixedRate(command, initialDelay, period, unit); + } + + @Override + public ScheduledFuture<?> scheduleWithFixedDelay( + Runnable command, long initialDelay, long delay, TimeUnit unit) { + return delegate.scheduleWithFixedDelay( + command, initialDelay, delay, unit); + } + } + + /* + * 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 { + 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; + } +} diff --git a/guava/src/com/google/common/util/concurrent/RateLimiter.java b/guava/src/com/google/common/util/concurrent/RateLimiter.java new file mode 100644 index 0000000..eaf3aa9 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/RateLimiter.java @@ -0,0 +1,658 @@ +/* + * Copyright (C) 2012 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Ticker; + +import java.util.concurrent.TimeUnit; + +import javax.annotation.concurrent.ThreadSafe; + +/** + * A rate limiter. Conceptually, a rate limiter distributes permits at a + * configurable rate. Each {@link #acquire()} blocks if necessary until a permit is + * available, and then takes it. Once acquired, permits need not be released. + * + * <p>Rate limiters are often used to restrict the rate at which some + * physical or logical resource is accessed. This is in contrast to {@link + * java.util.concurrent.Semaphore} which restricts the number of concurrent + * accesses instead of the rate (note though that concurrency and rate are closely related, + * e.g. see <a href="http://en.wikipedia.org/wiki/Little's_law">Little's Law</a>). + * + * <p>A {@code RateLimiter} is defined primarily by the rate at which permits + * are issued. Absent additional configuration, permits will be distributed at a + * fixed rate, defined in terms of permits per second. Permits will be distributed + * smoothly, with the delay between individual permits being adjusted to ensure + * that the configured rate is maintained. + * + * <p>It is possible to configure a {@code RateLimiter} to have a warmup + * period during which time the permits issued each second steadily increases until + * it hits the stable rate. + * + * <p>As an example, imagine that we have a list of tasks to execute, but we don't want to + * submit more than 2 per second: + *<pre> {@code + * final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second" + * void submitTasks(List<Runnable> tasks, Executor executor) { + * for (Runnable task : tasks) { + * rateLimiter.acquire(); // may wait + * executor.execute(task); + * } + * } + *}</pre> + * + * <p>As another example, imagine that we produce a stream of data, and we want to cap it + * at 5kb per second. This could be accomplished by requiring a permit per byte, and specifying + * a rate of 5000 permits per second: + *<pre> {@code + * final RateLimiter rateLimiter = RateLimiter.create(5000.0); // rate = 5000 permits per second + * void submitPacket(byte[] packet) { + * rateLimiter.acquire(packet.length); + * networkService.send(packet); + * } + *}</pre> + * + * <p>It is important to note that the number of permits requested <i>never</i> + * affect the throttling of the request itself (an invocation to {@code acquire(1)} + * and an invocation to {@code acquire(1000)} will result in exactly the same throttling, if any), + * but it affects the throttling of the <i>next</i> request. I.e., if an expensive task + * arrives at an idle RateLimiter, it will be granted immediately, but it is the <i>next</i> + * request that will experience extra throttling, thus paying for the cost of the expensive + * task. + * + * <p>Note: {@code RateLimiter} does not provide fairness guarantees. + * + * @author Dimitris Andreou + * @since 13.0 + */ +// TODO(user): switch to nano precision. A natural unit of cost is "bytes", and a micro precision +// would mean a maximum rate of "1MB/s", which might be small in some cases. +@ThreadSafe +@Beta +public abstract class RateLimiter { + /* + * How is the RateLimiter designed, and why? + * + * The primary feature of a RateLimiter is its "stable rate", the maximum rate that + * is should allow at normal conditions. This is enforced by "throttling" incoming + * requests as needed, i.e. compute, for an incoming request, the appropriate throttle time, + * and make the calling thread wait as much. + * + * The simplest way to maintain a rate of QPS is to keep the timestamp of the last + * granted request, and ensure that (1/QPS) seconds have elapsed since then. For example, + * for a rate of QPS=5 (5 tokens per second), if we ensure that a request isn't granted + * earlier than 200ms after the the last one, then we achieve the intended rate. + * If a request comes and the last request was granted only 100ms ago, then we wait for + * another 100ms. At this rate, serving 15 fresh permits (i.e. for an acquire(15) request) + * naturally takes 3 seconds. + * + * It is important to realize that such a RateLimiter has a very superficial memory + * of the past: it only remembers the last request. What if the RateLimiter was unused for + * a long period of time, then a request arrived and was immediately granted? + * This RateLimiter would immediately forget about that past underutilization. This may + * result in either underutilization or overflow, depending on the real world consequences + * of not using the expected rate. + * + * Past underutilization could mean that excess resources are available. Then, the RateLimiter + * should speed up for a while, to take advantage of these resources. This is important + * when the rate is applied to networking (limiting bandwidth), where past underutilization + * typically translates to "almost empty buffers", which can be filled immediately. + * + * On the other hand, past underutilization could mean that "the server responsible for + * handling the request has become less ready for future requests", i.e. its caches become + * stale, and requests become more likely to trigger expensive operations (a more extreme + * case of this example is when a server has just booted, and it is mostly busy with getting + * itself up to speed). + * + * To deal with such scenarios, we add an extra dimension, that of "past underutilization", + * modeled by "storedPermits" variable. This variable is zero when there is no + * underutilization, and it can grow up to maxStoredPermits, for sufficiently large + * underutilization. So, the requested permits, by an invocation acquire(permits), + * are served from: + * - stored permits (if available) + * - fresh permits (for any remaining permits) + * + * How this works is best explained with an example: + * + * For a RateLimiter that produces 1 token per second, every second + * that goes by with the RateLimiter being unused, we increase storedPermits by 1. + * Say we leave the RateLimiter unused for 10 seconds (i.e., we expected a request at time + * X, but we are at time X + 10 seconds before a request actually arrives; this is + * also related to the point made in the last paragraph), thus storedPermits + * becomes 10.0 (assuming maxStoredPermits >= 10.0). At that point, a request of acquire(3) + * arrives. We serve this request out of storedPermits, and reduce that to 7.0 (how this is + * translated to throttling time is discussed later). Immediately after, assume that an + * acquire(10) request arriving. We serve the request partly from storedPermits, + * using all the remaining 7.0 permits, and the remaining 3.0, we serve them by fresh permits + * produced by the rate limiter. + * + * We already know how much time it takes to serve 3 fresh permits: if the rate is + * "1 token per second", then this will take 3 seconds. But what does it mean to serve 7 + * stored permits? As explained above, there is no unique answer. If we are primarily + * interested to deal with underutilization, then we want stored permits to be given out + * /faster/ than fresh ones, because underutilization = free resources for the taking. + * If we are primarily interested to deal with overflow, then stored permits could + * be given out /slower/ than fresh ones. Thus, we require a (different in each case) + * function that translates storedPermits to throtting time. + * + * This role is played by storedPermitsToWaitTime(double storedPermits, double permitsToTake). + * The underlying model is a continuous function mapping storedPermits + * (from 0.0 to maxStoredPermits) onto the 1/rate (i.e. intervals) that is effective at the given + * storedPermits. "storedPermits" essentially measure unused time; we spend unused time + * buying/storing permits. Rate is "permits / time", thus "1 / rate = time / permits". + * Thus, "1/rate" (time / permits) times "permits" gives time, i.e., integrals on this + * function (which is what storedPermitsToWaitTime() computes) correspond to minimum intervals + * between subsequent requests, for the specified number of requested permits. + * + * Here is an example of storedPermitsToWaitTime: + * If storedPermits == 10.0, and we want 3 permits, we take them from storedPermits, + * reducing them to 7.0, and compute the throttling for these as a call to + * storedPermitsToWaitTime(storedPermits = 10.0, permitsToTake = 3.0), which will + * evaluate the integral of the function from 7.0 to 10.0. + * + * Using integrals guarantees that the effect of a single acquire(3) is equivalent + * to { acquire(1); acquire(1); acquire(1); }, or { acquire(2); acquire(1); }, etc, + * since the integral of the function in [7.0, 10.0] is equivalent to the sum of the + * integrals of [7.0, 8.0], [8.0, 9.0], [9.0, 10.0] (and so on), no matter + * what the function is. This guarantees that we handle correctly requests of varying weight + * (permits), /no matter/ what the actual function is - so we can tweak the latter freely. + * (The only requirement, obviously, is that we can compute its integrals). + * + * Note well that if, for this function, we chose a horizontal line, at height of exactly + * (1/QPS), then the effect of the function is non-existent: we serve storedPermits at + * exactly the same cost as fresh ones (1/QPS is the cost for each). We use this trick later. + * + * If we pick a function that goes /below/ that horizontal line, it means that we reduce + * the area of the function, thus time. Thus, the RateLimiter becomes /faster/ after a + * period of underutilization. If, on the other hand, we pick a function that + * goes /above/ that horizontal line, then it means that the area (time) is increased, + * thus storedPermits are more costly than fresh permits, thus the RateLimiter becomes + * /slower/ after a period of underutilization. + * + * Last, but not least: consider a RateLimiter with rate of 1 permit per second, currently + * completely unused, and an expensive acquire(100) request comes. It would be nonsensical + * to just wait for 100 seconds, and /then/ start the actual task. Why wait without doing + * anything? A much better approach is to /allow/ the request right away (as if it was an + * acquire(1) request instead), and postpone /subsequent/ requests as needed. In this version, + * we allow starting the task immediately, and postpone by 100 seconds future requests, + * thus we allow for work to get done in the meantime instead of waiting idly. + * + * This has important consequences: it means that the RateLimiter doesn't remember the time + * of the _last_ request, but it remembers the (expected) time of the _next_ request. This + * also enables us to tell immediately (see tryAcquire(timeout)) whether a particular + * timeout is enough to get us to the point of the next scheduling time, since we always + * maintain that. And what we mean by "an unused RateLimiter" is also defined by that + * notion: when we observe that the "expected arrival time of the next request" is actually + * in the past, then the difference (now - past) is the amount of time that the RateLimiter + * was formally unused, and it is that amount of time which we translate to storedPermits. + * (We increase storedPermits with the amount of permits that would have been produced + * in that idle time). So, if rate == 1 permit per second, and arrivals come exactly + * one second after the previous, then storedPermits is _never_ increased -- we would only + * increase it for arrivals _later_ than the expected one second. + */ + + /** + * Creates a {@code RateLimiter} with the specified stable throughput, given as + * "permits per second" (commonly referred to as <i>QPS</i>, queries per second). + * + * <p>The returned {@code RateLimiter} ensures that on average no more than {@code + * permitsPerSecond} are issued during any given second, with sustained requests + * being smoothly spread over each second. When the incoming request rate exceeds + * {@code permitsPerSecond} the rate limiter will release one permit every {@code + * (1.0 / permitsPerSecond)} seconds. When the rate limiter is unused, + * bursts of up to {@code permitsPerSecond} permits will be allowed, with subsequent + * requests being smoothly limited at the stable rate of {@code permitsPerSecond}. + * + * @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in + * how many permits become available per second. + */ + public static RateLimiter create(double permitsPerSecond) { + return create(SleepingTicker.SYSTEM_TICKER, permitsPerSecond); + } + + @VisibleForTesting + static RateLimiter create(SleepingTicker ticker, double permitsPerSecond) { + RateLimiter rateLimiter = new Bursty(ticker); + rateLimiter.setRate(permitsPerSecond); + return rateLimiter; + } + + /** + * Creates a {@code RateLimiter} with the specified stable throughput, given as + * "permits per second" (commonly referred to as <i>QPS</i>, queries per second), and a + * <i>warmup period</i>, during which the {@code RateLimiter} smoothly ramps up its rate, + * until it reaches its maximum rate at the end of the period (as long as there are enough + * requests to saturate it). Similarly, if the {@code RateLimiter} is left <i>unused</i> for + * a duration of {@code warmupPeriod}, it will gradually return to its "cold" state, + * i.e. it will go through the same warming up process as when it was first created. + * + * <p>The returned {@code RateLimiter} is intended for cases where the resource that actually + * fulfils the requests (e.g., a remote server) needs "warmup" time, rather than + * being immediately accessed at the stable (maximum) rate. + * + * <p>The returned {@code RateLimiter} starts in a "cold" state (i.e. the warmup period + * will follow), and if it is left unused for long enough, it will return to that state. + * + * @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in + * how many permits become available per second + * @param warmupPeriod the duration of the period where the {@code RateLimiter} ramps up its + * rate, before reaching its stable (maximum) rate + * @param unit the time unit of the warmupPeriod argument + */ + // TODO(user): add a burst size of 1-second-worth of permits, as in the metronome? + public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) { + return create(SleepingTicker.SYSTEM_TICKER, permitsPerSecond, warmupPeriod, unit); + } + + @VisibleForTesting + static RateLimiter create( + SleepingTicker ticker, double permitsPerSecond, long warmupPeriod, TimeUnit timeUnit) { + RateLimiter rateLimiter = new WarmingUp(ticker, warmupPeriod, timeUnit); + rateLimiter.setRate(permitsPerSecond); + return rateLimiter; + } + + @VisibleForTesting + static RateLimiter createBursty( + SleepingTicker ticker, double permitsPerSecond, int maxBurstSize) { + Bursty rateLimiter = new Bursty(ticker); + rateLimiter.setRate(permitsPerSecond); + rateLimiter.maxPermits = maxBurstSize; + return rateLimiter; + } + + /** + * The underlying timer; used both to measure elapsed time and sleep as necessary. A separate + * object to facilitate testing. + */ + private final SleepingTicker ticker; + + /** + * The timestamp when the RateLimiter was created; used to avoid possible overflow/time-wrapping + * errors. + */ + private final long offsetNanos; + + /** + * The currently stored permits. + */ + double storedPermits; + + /** + * The maximum number of stored permits. + */ + double maxPermits; + + /** + * The interval between two unit requests, at our stable rate. E.g., a stable rate of 5 permits + * per second has a stable interval of 200ms. + */ + double stableIntervalMicros; + + /** + * The time when the next request (no matter its size) will be granted. After granting a request, + * this is pushed further in the future. Large requests push this further than small requests. + */ + private long nextFreeTicketMicros = 0L; // could be either in the past or future + + private RateLimiter(SleepingTicker ticker) { + this.ticker = ticker; + this.offsetNanos = ticker.read(); + } + + /** + * Updates the stable rate of this {@code RateLimiter}, that is, the + * {@code permitsPerSecond} argument provided in the factory method that + * constructed the {@code RateLimiter}. Currently throttled threads will <b>not</b> + * be awakened as a result of this invocation, thus they do not observe the new rate; + * only subsequent requests will. + * + * <p>Note though that, since each request repays (by waiting, if necessary) the cost + * of the <i>previous</i> request, this means that the very next request + * after an invocation to {@code setRate} will not be affected by the new rate; + * it will pay the cost of the previous request, which is in terms of the previous rate. + * + * <p>The behavior of the {@code RateLimiter} is not modified in any other way, + * e.g. if the {@code RateLimiter} was configured with a warmup period of 20 seconds, + * it still has a warmup period of 20 seconds after this method invocation. + * + * @param permitsPerSecond the new stable rate of this {@code RateLimiter}. + */ + public final synchronized void setRate(double permitsPerSecond) { + Preconditions.checkArgument(permitsPerSecond > 0.0 + && !Double.isNaN(permitsPerSecond), "rate must be positive"); + resync(readSafeMicros()); + double stableIntervalMicros = TimeUnit.SECONDS.toMicros(1L) / permitsPerSecond; + this.stableIntervalMicros = stableIntervalMicros; + doSetRate(permitsPerSecond, stableIntervalMicros); + } + + abstract void doSetRate(double permitsPerSecond, double stableIntervalMicros); + + /** + * Returns the stable rate (as {@code permits per seconds}) with which this + * {@code RateLimiter} is configured with. The initial value of this is the same as + * the {@code permitsPerSecond} argument passed in the factory method that produced + * this {@code RateLimiter}, and it is only updated after invocations + * to {@linkplain #setRate}. + */ + public final synchronized double getRate() { + return TimeUnit.SECONDS.toMicros(1L) / stableIntervalMicros; + } + + /** + * Acquires a permit from this {@code RateLimiter}, blocking until the request can be granted. + * + * <p>This method is equivalent to {@code acquire(1)}. + */ + public void acquire() { + acquire(1); + } + + /** + * Acquires the given number of permits from this {@code RateLimiter}, blocking until the + * request be granted. + * + * @param permits the number of permits to acquire + */ + public void acquire(int permits) { + checkPermits(permits); + long microsToWait; + synchronized (this) { + microsToWait = reserveNextTicket(permits, readSafeMicros()); + } + ticker.sleepMicrosUninterruptibly(microsToWait); + } + + /** + * Acquires a permit from this {@code RateLimiter} if it can be obtained + * without exceeding the specified {@code timeout}, or returns {@code false} + * immediately (without waiting) if the permit would not have been granted + * before the timeout expired. + * + * <p>This method is equivalent to {@code tryAcquire(1, timeout, unit)}. + * + * @param timeout the maximum time to wait for the permit + * @param unit the time unit of the timeout argument + * @return {@code true} if the permit was acquired, {@code false} otherwise + */ + public boolean tryAcquire(long timeout, TimeUnit unit) { + return tryAcquire(1, timeout, unit); + } + + /** + * Acquires the given number of permits from this {@code RateLimiter} if it can be obtained + * without exceeding the specified {@code timeout}, or returns {@code false} + * immediately (without waiting) if the permits would not have been granted + * before the timeout expired. + * + * @param permits the number of permits to acquire + * @param timeout the maximum time to wait for the permits + * @param unit the time unit of the timeout argument + * @return {@code true} if the permits were acquired, {@code false} otherwise + */ + public boolean tryAcquire(int permits, long timeout, TimeUnit unit) { + checkPermits(permits); + long timeoutMicros = unit.toMicros(timeout); + long microsToWait; + synchronized (this) { + long nowMicros = readSafeMicros(); + if (nextFreeTicketMicros > nowMicros + timeoutMicros) { + return false; + } else { + microsToWait = reserveNextTicket(permits, nowMicros); + } + } + ticker.sleepMicrosUninterruptibly(microsToWait); + return true; + } + + private static void checkPermits(int permits) { + Preconditions.checkArgument(permits > 0, "Requested permits must be positive"); + } + + /** + * Reserves next ticket and returns the wait time that the caller must wait for. + */ + private long reserveNextTicket(double requiredPermits, long nowMicros) { + resync(nowMicros); + long microsToNextFreeTicket = nextFreeTicketMicros - nowMicros; + double storedPermitsToSpend = Math.min(requiredPermits, this.storedPermits); + double freshPermits = requiredPermits - storedPermitsToSpend; + + long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend) + + (long) (freshPermits * stableIntervalMicros); + + this.nextFreeTicketMicros = nextFreeTicketMicros + waitMicros; + this.storedPermits -= storedPermitsToSpend; + return microsToNextFreeTicket; + } + + /** + * Translates a specified portion of our currently stored permits which we want to + * spend/acquire, into a throttling time. Conceptually, this evaluates the integral + * of the underlying function we use, for the range of + * [(storedPermits - permitsToTake), storedPermits]. + * + * This always holds: {@code 0 <= permitsToTake <= storedPermits} + */ + abstract long storedPermitsToWaitTime(double storedPermits, double permitsToTake); + + private void resync(long nowMicros) { + // if nextFreeTicket is in the past, resync to now + if (nowMicros > nextFreeTicketMicros) { + storedPermits = Math.min(maxPermits, + storedPermits + (nowMicros - nextFreeTicketMicros) / stableIntervalMicros); + nextFreeTicketMicros = nowMicros; + } + } + + private long readSafeMicros() { + return TimeUnit.NANOSECONDS.toMicros(ticker.read() - offsetNanos); + } + + @Override + public String toString() { + return String.format("RateLimiter[stableRate=%3.1fqps]", 1000000.0 / stableIntervalMicros); + } + + /** + * This implements the following function: + * + * ^ throttling + * | + * 3*stable + / + * interval | /. + * (cold) | / . + * | / . <-- "warmup period" is the area of the trapezoid between + * 2*stable + / . halfPermits and maxPermits + * interval | / . + * | / . + * | / . + * stable +----------/ WARM . } + * interval | . UP . } <-- this rectangle (from 0 to maxPermits, and + * | . PERIOD. } height == stableInterval) defines the cooldown period, + * | . . } and we want cooldownPeriod == warmupPeriod + * |---------------------------------> storedPermits + * (halfPermits) (maxPermits) + * + * Before going into the details of this particular function, let's keep in mind the basics: + * 1) The state of the RateLimiter (storedPermits) is a vertical line in this figure. + * 2) When the RateLimiter is not used, this goes right (up to maxPermits) + * 3) When the RateLimiter is used, this goes left (down to zero), since if we have storedPermits, + * we serve from those first + * 4) When _unused_, we go right at the same speed (rate)! I.e., if our rate is + * 2 permits per second, and 3 unused seconds pass, we will always save 6 permits + * (no matter what our initial position was), up to maxPermits. + * If we invert the rate, we get the "stableInterval" (interval between two requests + * in a perfectly spaced out sequence of requests of the given rate). Thus, if you + * want to see "how much time it will take to go from X storedPermits to X+K storedPermits?", + * the answer is always stableInterval * K. In the same example, for 2 permits per second, + * stableInterval is 500ms. Thus to go from X storedPermits to X+6 storedPermits, we + * require 6 * 500ms = 3 seconds. + * + * In short, the time it takes to move to the right (save K permits) is equal to the + * rectangle of width == K and height == stableInterval. + * 4) When _used_, the time it takes, as explained in the introductory class note, is + * equal to the integral of our function, between X permits and X-K permits, assuming + * we want to spend K saved permits. + * + * In summary, the time it takes to move to the left (spend K permits), is equal to the + * area of the function of width == K. + * + * Let's dive into this function now: + * + * When we have storedPermits <= halfPermits (the left portion of the function), then + * we spend them at the exact same rate that + * fresh permits would be generated anyway (that rate is 1/stableInterval). We size + * this area to be equal to _half_ the specified warmup period. Why we need this? + * And why half? We'll explain shortly below (after explaining the second part). + * + * Stored permits that are beyond halfPermits, are mapped to an ascending line, that goes + * from stableInterval to 3 * stableInterval. The average height for that part is + * 2 * stableInterval, and is sized appropriately to have an area _equal_ to the + * specified warmup period. Thus, by point (4) above, it takes "warmupPeriod" amount of time + * to go from maxPermits to halfPermits. + * + * BUT, by point (3) above, it only takes "warmupPeriod / 2" amount of time to return back + * to maxPermits, from halfPermits! (Because the trapezoid has double the area of the rectangle + * of height stableInterval and equivalent width). We decided that the "cooldown period" + * time should be equivalent to "warmup period", thus a fully saturated RateLimiter + * (with zero stored permits, serving only fresh ones) can go to a fully unsaturated + * (with storedPermits == maxPermits) in the same amount of time it takes for a fully + * unsaturated RateLimiter to return to the stableInterval -- which happens in halfPermits, + * since beyond that point, we use a horizontal line of "stableInterval" height, simulating + * the regular rate. + * + * Thus, we have figured all dimensions of this shape, to give all the desired + * properties: + * - the width is warmupPeriod / stableInterval, to make cooldownPeriod == warmupPeriod + * - the slope starts at the middle, and goes from stableInterval to 3*stableInterval so + * to have halfPermits being spend in double the usual time (half the rate), while their + * respective rate is steadily ramping up + */ + private static class WarmingUp extends RateLimiter { + + final long warmupPeriodMicros; + /** + * The slope of the line from the stable interval (when permits == 0), to the cold interval + * (when permits == maxPermits) + */ + private double slope; + private double halfPermits; + + WarmingUp(SleepingTicker ticker, long warmupPeriod, TimeUnit timeUnit) { + super(ticker); + this.warmupPeriodMicros = timeUnit.toMicros(warmupPeriod); + } + + @Override + void doSetRate(double permitsPerSecond, double stableIntervalMicros) { + double oldMaxPermits = maxPermits; + maxPermits = warmupPeriodMicros / stableIntervalMicros; + halfPermits = maxPermits / 2.0; + // Stable interval is x, cold is 3x, so on average it's 2x. Double the time -> halve the rate + double coldIntervalMicros = stableIntervalMicros * 3.0; + slope = (coldIntervalMicros - stableIntervalMicros) / halfPermits; + if (oldMaxPermits == Double.POSITIVE_INFINITY) { + // if we don't special-case this, we would get storedPermits == NaN, below + storedPermits = 0.0; + } else { + storedPermits = (oldMaxPermits == 0.0) + ? maxPermits // initial state is cold + : storedPermits * maxPermits / oldMaxPermits; + } + } + + @Override + long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { + double availablePermitsAboveHalf = storedPermits - halfPermits; + long micros = 0; + // measuring the integral on the right part of the function (the climbing line) + if (availablePermitsAboveHalf > 0.0) { + double permitsAboveHalfToTake = Math.min(availablePermitsAboveHalf, permitsToTake); + micros = (long) (permitsAboveHalfToTake * (permitsToTime(availablePermitsAboveHalf) + + permitsToTime(availablePermitsAboveHalf - permitsAboveHalfToTake)) / 2.0); + permitsToTake -= permitsAboveHalfToTake; + } + // measuring the integral on the left part of the function (the horizontal line) + micros += (stableIntervalMicros * permitsToTake); + return micros; + } + + private double permitsToTime(double permits) { + return stableIntervalMicros + permits * slope; + } + } + + /** + * This implements a trivial function, where storedPermits are translated to + * zero throttling - thus, a client gets an infinite speedup for permits acquired out + * of the storedPermits pool. This is also used for the special case of the "metronome", + * where the width of the function is also zero; maxStoredPermits is zero, thus + * storedPermits and permitsToTake are always zero as well. Such a RateLimiter can + * not save permits when unused, thus all permits it serves are fresh, using the + * designated rate. + */ + private static class Bursty extends RateLimiter { + Bursty(SleepingTicker ticker) { + super(ticker); + } + + @Override + void doSetRate(double permitsPerSecond, double stableIntervalMicros) { + double oldMaxPermits = this.maxPermits; + /* + * We allow the equivalent work of up to one second to be granted with zero waiting, if the + * rate limiter has been unused for as much. This is to avoid potentially producing tiny + * wait interval between subsequent requests for sufficiently large rates, which would + * unnecessarily overconstrain the thread scheduler. + */ + maxPermits = permitsPerSecond; // one second worth of permits + storedPermits = (oldMaxPermits == 0.0) + ? 0.0 // initial state + : storedPermits * maxPermits / oldMaxPermits; + } + + @Override + long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { + return 0L; + } + } + + @VisibleForTesting + static abstract class SleepingTicker extends Ticker { + abstract void sleepMicrosUninterruptibly(long micros); + + static final SleepingTicker SYSTEM_TICKER = new SleepingTicker() { + @Override + public long read() { + return systemTicker().read(); + } + + @Override + public void sleepMicrosUninterruptibly(long micros) { + if (micros > 0) { + Uninterruptibles.sleepUninterruptibly(micros, TimeUnit.MICROSECONDS); + } + } + }; + } +} diff --git a/guava/src/com/google/common/util/concurrent/Service.java b/guava/src/com/google/common/util/concurrent/Service.java new file mode 100644 index 0000000..1c3bed6 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/Service.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +/** + * An object with an operational state, plus asynchronous {@link #start()} and {@link #stop()} + * lifecycle methods to transition between states. Example services include webservers, RPC servers + * and timers. + * + * <p>The normal lifecycle of a service is: + * <ul> + * <li>{@linkplain State#NEW NEW} -> + * <li>{@linkplain State#STARTING STARTING} -> + * <li>{@linkplain State#RUNNING RUNNING} -> + * <li>{@linkplain State#STOPPING STOPPING} -> + * <li>{@linkplain State#TERMINATED TERMINATED} + * </ul> + * + * <p>There are deviations from this if there are failures or if {@link Service#stop} is called + * before the {@link Service} reaches the {@linkplain State#RUNNING RUNNING} state. The set of legal + * transitions form a <a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph">DAG</a>, + * therefore every method of the listener will be called at most once. N.B. The {@link State#FAILED} + * and {@link State#TERMINATED} states are terminal states, once a service enters either of these + * states it cannot ever leave them. + * + * <p>Implementors of this interface are strongly encouraged to extend one of the abstract classes + * in this package which implement this interface and make the threading and state management + * easier. + * + * @author Jesse Wilson + * @author Luke Sandberg + * @since 9.0 (in 1.0 as {@code com.google.common.base.Service}) + */ +@Beta +public interface Service { + /** + * If the service state is {@link State#NEW}, this initiates service startup and returns + * immediately. If the service has already been started, this method returns immediately without + * taking action. A stopped service may not be restarted. + * + * @return a future for the startup result, regardless of whether this call initiated startup. + * Calling {@link ListenableFuture#get} will block until the service has finished + * starting, and returns one of {@link State#RUNNING}, {@link State#STOPPING} or + * {@link State#TERMINATED}. If the service fails to start, {@link ListenableFuture#get} + * will throw an {@link ExecutionException}, and the service's state will be + * {@link State#FAILED}. If it has already finished starting, {@link ListenableFuture#get} + * returns immediately. Cancelling this future has no effect on the service. + */ + ListenableFuture<State> start(); + + /** + * Initiates service startup (if necessary), returning once the service has finished starting. + * Unlike calling {@code start().get()}, this method throws no checked exceptions, and it cannot + * be {@linkplain Thread#interrupt interrupted}. + * + * @throws UncheckedExecutionException if startup failed + * @return the state of the service when startup finished. + */ + State startAndWait(); + + /** + * Returns {@code true} if this service is {@linkplain State#RUNNING running}. + */ + boolean isRunning(); + + /** + * Returns the lifecycle state of the service. + */ + State state(); + + /** + * If the service is {@linkplain State#STARTING starting} or {@linkplain State#RUNNING running}, + * this initiates service shutdown and returns immediately. If the service is + * {@linkplain State#NEW new}, it is {@linkplain State#TERMINATED terminated} without having been + * started nor stopped. If the service has already been stopped, this method returns immediately + * without taking action. + * + * @return a future for the shutdown result, regardless of whether this call initiated shutdown. + * Calling {@link ListenableFuture#get} will block until the service has finished shutting + * down, and either returns {@link State#TERMINATED} or throws an + * {@link ExecutionException}. If it has already finished stopping, + * {@link ListenableFuture#get} returns immediately. Cancelling this future has no effect + * on the service. + */ + ListenableFuture<State> stop(); + + /** + * Initiates service shutdown (if necessary), returning once the service has finished stopping. If + * this is {@link State#STARTING}, startup will be cancelled. If this is {@link State#NEW}, it is + * {@link State#TERMINATED terminated} without having been started nor stopped. Unlike calling + * {@code stop().get()}, this method throws no checked exceptions. + * + * @throws UncheckedExecutionException if the service has failed or fails during shutdown + * @return the state of the service when shutdown finished. + */ + State stopAndWait(); + + /** + * Registers a {@link Listener} to be {@linkplain Executor#execute executed} on the given + * executor. The listener will have the corresponding transition method called whenever the + * service changes state. The listener will not have previous state changes replayed, so it is + * suggested that listeners are added before the service starts. + * + * <p>There is no guaranteed ordering of execution of listeners, but any listener added through + * this method is guaranteed to be called whenever there is a state change. + * + * <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. + * + * @param listener the listener to run when the service changes state is complete + * @param executor the executor in which the the listeners callback methods will be run. For fast, + * lightweight listeners that would be safe to execute in any thread, consider + * {@link MoreExecutors#sameThreadExecutor}. + * @since 13.0 + */ + void addListener(Listener listener, Executor executor); + + /** + * The lifecycle states of a service. + * + * @since 9.0 (in 1.0 as {@code com.google.common.base.Service.State}) + */ + @Beta // should come out of Beta when Service does + enum State { + /** + * A service in this state is inactive. It does minimal work and consumes + * minimal resources. + */ + NEW, + + /** + * A service in this state is transitioning to {@link #RUNNING}. + */ + STARTING, + + /** + * A service in this state is operational. + */ + RUNNING, + + /** + * A service in this state is transitioning to {@link #TERMINATED}. + */ + STOPPING, + + /** + * A service in this state has completed execution normally. It does minimal work and consumes + * minimal resources. + */ + TERMINATED, + + /** + * A service in this state has encountered a problem and may not be operational. It cannot be + * started nor stopped. + */ + FAILED + } + + /** + * A listener for the various state changes that a {@link Service} goes through in its lifecycle. + * + * @author Luke Sandberg + * @since 13.0 + */ + @Beta // should come out of Beta when Service does + interface Listener { + /** + * Called when the service transitions from {@linkplain State#NEW NEW} to + * {@linkplain State#STARTING STARTING}. This occurs when {@link Service#start} or + * {@link Service#startAndWait} is called the first time. + */ + void starting(); + + /** + * Called when the service transitions from {@linkplain State#STARTING STARTING} to + * {@linkplain State#RUNNING RUNNING}. This occurs when a service has successfully started. + */ + void running(); + + /** + * Called when the service transitions to the {@linkplain State#STOPPING STOPPING} state. The + * only valid values for {@code from} are {@linkplain State#STARTING STARTING} or + * {@linkplain State#RUNNING RUNNING}. This occurs when {@link Service#stop} is called. + * + * @param from The previous state that is being transitioned from. + */ + void stopping(State from); + + /** + * Called when the service transitions to the {@linkplain State#TERMINATED TERMINATED} state. + * The {@linkplain State#TERMINATED TERMINATED} state is a terminal state in the transition + * diagram. Therefore, if this method is called, no other methods will be called on the + * {@link Listener}. + * + * @param from The previous state that is being transitioned from. The only valid values for + * this are {@linkplain State#NEW NEW}, {@linkplain State#RUNNING RUNNING} or + * {@linkplain State#STOPPING STOPPING}. + */ + void terminated(State from); + + /** + * Called when the service transitions to the {@linkplain State#FAILED FAILED} state. The + * {@linkplain State#FAILED FAILED} state is a terminal state in the transition diagram. + * Therefore, if this method is called, no other methods will be called on the {@link Listener}. + * + * @param from The previous state that is being transitioned from. Failure can occur in any + * state with the exception of {@linkplain State#NEW NEW} or + * {@linkplain State#TERMINATED TERMINATED}. + * @param failure The exception that caused the failure. + */ + void failed(State from, Throwable failure); + } +} diff --git a/guava/src/com/google/common/util/concurrent/SettableFuture.java b/guava/src/com/google/common/util/concurrent/SettableFuture.java new file mode 100644 index 0000000..23e14f9 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/SettableFuture.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2009 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 com.google.common.util.concurrent; + +import javax.annotation.Nullable; + +/** + * A {@link ListenableFuture} whose result may be set by a {@link #set(Object)} + * or {@link #setException(Throwable)} call. It may also be cancelled. + * + * @author Sven Mawson + * @since 9.0 (in 1.0 as {@code ValueFuture}) + */ +public final class SettableFuture<V> extends AbstractFuture<V> { + + /** + * Creates a new {@code SettableFuture} in the default state. + */ + public static <V> SettableFuture<V> create() { + return new SettableFuture<V>(); + } + + /** + * Explicit private constructor, use the {@link #create} factory method to + * create instances of {@code SettableFuture}. + */ + private SettableFuture() {} + + /** + * Sets the value of this future. This method will return {@code true} if + * the value was successfully set, or {@code false} if the future has already + * been set or cancelled. + * + * @param value the value the future should hold. + * @return true if the value was successfully set. + */ + @Override + public boolean set(@Nullable V value) { + return super.set(value); + } + + /** + * Sets the future to having failed with the given exception. This exception + * will be wrapped in an {@code ExecutionException} and thrown from the {@code + * get} methods. This method will return {@code true} if the exception was + * successfully set, or {@code false} if the future has already been set or + * cancelled. + * + * @param throwable the exception the future should hold. + * @return true if the exception was successfully set. + */ + @Override + public boolean setException(Throwable throwable) { + return super.setException(throwable); + } +} diff --git a/guava/src/com/google/common/util/concurrent/SimpleTimeLimiter.java b/guava/src/com/google/common/util/concurrent/SimpleTimeLimiter.java new file mode 100644 index 0000000..0f7c3e4 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/SimpleTimeLimiter.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2006 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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ObjectArrays; +import com.google.common.collect.Sets; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A TimeLimiter that runs method calls in the background using an + * {@link ExecutorService}. If the time limit expires for a given method call, + * the thread running the call will be interrupted. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@Beta +public final class SimpleTimeLimiter implements TimeLimiter { + + private final ExecutorService executor; + + /** + * Constructs a TimeLimiter instance using the given executor service to + * execute proxied method calls. + * <p> + * <b>Warning:</b> using a bounded executor + * may be counterproductive! If the thread pool fills up, any time callers + * spend waiting for a thread may count toward their time limit, and in + * this case the call may even time out before the target method is ever + * invoked. + * + * @param executor the ExecutorService that will execute the method calls on + * the target objects; for example, a {@link + * Executors#newCachedThreadPool()}. + */ + public SimpleTimeLimiter(ExecutorService executor) { + this.executor = checkNotNull(executor); + } + + /** + * Constructs a TimeLimiter instance using a {@link + * Executors#newCachedThreadPool()} to execute proxied method calls. + * + * <p><b>Warning:</b> using a bounded executor may be counterproductive! If + * the thread pool fills up, any time callers spend waiting for a thread may + * count toward their time limit, and in this case the call may even time out + * before the target method is ever invoked. + */ + public SimpleTimeLimiter() { + this(Executors.newCachedThreadPool()); + } + + @Override + public <T> T newProxy(final T target, Class<T> interfaceType, + final long timeoutDuration, final TimeUnit timeoutUnit) { + checkNotNull(target); + checkNotNull(interfaceType); + checkNotNull(timeoutUnit); + checkArgument(timeoutDuration > 0, "bad timeout: " + timeoutDuration); + checkArgument(interfaceType.isInterface(), + "interfaceType must be an interface type"); + + final Set<Method> interruptibleMethods + = findInterruptibleMethods(interfaceType); + + InvocationHandler handler = new InvocationHandler() { + @Override + public Object invoke(Object obj, final Method method, final Object[] args) + throws Throwable { + Callable<Object> callable = new Callable<Object>() { + @Override + public Object call() throws Exception { + try { + return method.invoke(target, args); + } catch (InvocationTargetException e) { + throwCause(e, false); + throw new AssertionError("can't get here"); + } + } + }; + return callWithTimeout(callable, timeoutDuration, timeoutUnit, + interruptibleMethods.contains(method)); + } + }; + return newProxy(interfaceType, handler); + } + + // TODO: should this actually throw only ExecutionException? + @Override + public <T> T callWithTimeout(Callable<T> callable, long timeoutDuration, + TimeUnit timeoutUnit, boolean amInterruptible) throws Exception { + checkNotNull(callable); + checkNotNull(timeoutUnit); + checkArgument(timeoutDuration > 0, "timeout must be positive: %s", + timeoutDuration); + Future<T> future = executor.submit(callable); + try { + if (amInterruptible) { + try { + return future.get(timeoutDuration, timeoutUnit); + } catch (InterruptedException e) { + future.cancel(true); + throw e; + } + } else { + return Uninterruptibles.getUninterruptibly(future, + timeoutDuration, timeoutUnit); + } + } catch (ExecutionException e) { + throw throwCause(e, true); + } catch (TimeoutException e) { + future.cancel(true); + throw new UncheckedTimeoutException(e); + } + } + + private static Exception throwCause(Exception e, boolean combineStackTraces) + throws Exception { + Throwable cause = e.getCause(); + if (cause == null) { + throw e; + } + if (combineStackTraces) { + StackTraceElement[] combined = ObjectArrays.concat(cause.getStackTrace(), + e.getStackTrace(), StackTraceElement.class); + cause.setStackTrace(combined); + } + if (cause instanceof Exception) { + throw (Exception) cause; + } + if (cause instanceof Error) { + throw (Error) cause; + } + // The cause is a weird kind of Throwable, so throw the outer exception. + throw e; + } + + private static Set<Method> findInterruptibleMethods(Class<?> interfaceType) { + Set<Method> set = Sets.newHashSet(); + for (Method m : interfaceType.getMethods()) { + if (declaresInterruptedEx(m)) { + set.add(m); + } + } + return set; + } + + private static boolean declaresInterruptedEx(Method method) { + for (Class<?> exType : method.getExceptionTypes()) { + // debate: == or isAssignableFrom? + if (exType == InterruptedException.class) { + return true; + } + } + return false; + } + + // TODO: replace with version in common.reflect if and when it's open-sourced + private static <T> T newProxy( + Class<T> interfaceType, InvocationHandler handler) { + Object object = Proxy.newProxyInstance(interfaceType.getClassLoader(), + new Class<?>[] { interfaceType }, handler); + return interfaceType.cast(object); + } +} diff --git a/guava/src/com/google/common/util/concurrent/Striped.java b/guava/src/com/google/common/util/concurrent/Striped.java new file mode 100644 index 0000000..3c426f0 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/Striped.java @@ -0,0 +1,376 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Functions; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.Iterables; +import com.google.common.collect.MapMaker; +import com.google.common.math.IntMath; +import com.google.common.primitives.Ints; + +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * A striped {@code Lock/Semaphore/ReadWriteLock}. This offers the underlying lock striping + * similar to that of {@code ConcurrentHashMap} in a reusable form, and extends it for + * semaphores and read-write locks. Conceptually, lock striping is the technique of dividing a lock + * into many <i>stripes</i>, increasing the granularity of a single lock and allowing independent + * operations to lock different stripes and proceed concurrently, instead of creating contention + * for a single lock. + * + * <p>The guarantee provided by this class is that equal keys lead to the same lock (or semaphore), + * i.e. {@code if (key1.equals(key2))} then {@code striped.get(key1) == striped.get(key2)} + * (assuming {@link Object#hashCode()} is correctly implemented for the keys). Note + * that if {@code key1} is <strong>not</strong> equal to {@code key2}, it is <strong>not</strong> + * guaranteed that {@code striped.get(key1) != striped.get(key2)}; the elements might nevertheless + * be mapped to the same lock. The lower the number of stripes, the higher the probability of this + * happening. + * + * <p>There are three flavors of this class: {@code Striped<Lock>}, {@code Striped<Semaphore>}, + * and {@code Striped<ReadWriteLock>}. For each type, two implementations are offered: + * {@linkplain #lock(int) strong} and {@linkplain #lazyWeakLock(int) weak} + * {@code Striped<Lock>}, {@linkplain #semaphore(int, int) strong} and {@linkplain + * #lazyWeakSemaphore(int, int) weak} {@code Striped<Semaphore>}, and {@linkplain + * #readWriteLock(int) strong} and {@linkplain #lazyWeakReadWriteLock(int) weak} + * {@code Striped<ReadWriteLock>}. <i>Strong</i> means that all stripes (locks/semaphores) are + * initialized eagerly, and are not reclaimed unless {@code Striped} itself is reclaimable. + * <i>Weak</i> means that locks/semaphores are created lazily, and they are allowed to be reclaimed + * if nobody is holding on to them. This is useful, for example, if one wants to create a {@code + * Striped<Lock>} of many locks, but worries that in most cases only a small portion of these + * would be in use. + * + * <p>Prior to this class, one might be tempted to use {@code Map<K, Lock>}, where {@code K} + * represents the task. This maximizes concurrency by having each unique key mapped to a unique + * lock, but also maximizes memory footprint. On the other extreme, one could use a single lock + * for all tasks, which minimizes memory footprint but also minimizes concurrency. Instead of + * choosing either of these extremes, {@code Striped} allows the user to trade between required + * concurrency and memory footprint. For example, if a set of tasks are CPU-bound, one could easily + * create a very compact {@code Striped<Lock>} of {@code availableProcessors() * 4} stripes, + * instead of possibly thousands of locks which could be created in a {@code Map<K, Lock>} + * structure. + * + * @author Dimitris Andreou + * @since 13.0 + */ +@Beta +public abstract class Striped<L> { + private Striped() {} + + /** + * Returns the stripe that corresponds to the passed key. It is always guaranteed that if + * {@code key1.equals(key2)}, then {@code get(key1) == get(key2)}. + * + * @param key an arbitrary, non-null key + * @return the stripe that the passed key corresponds to + */ + public abstract L get(Object key); + + /** + * Returns the stripe at the specified index. Valid indexes are 0, inclusively, to + * {@code size()}, exclusively. + * + * @param index the index of the stripe to return; must be in {@code [0...size())} + * @return the stripe at the specified index + */ + public abstract L getAt(int index); + + /** + * Returns the index to which the given key is mapped, so that getAt(indexFor(key)) == get(key). + */ + abstract int indexFor(Object key); + + /** + * Returns the total number of stripes in this instance. + */ + public abstract int size(); + + /** + * Returns the stripes that correspond to the passed objects, in ascending (as per + * {@link #getAt(int)}) order. Thus, threads that use the stripes in the order returned + * by this method are guaranteed to not deadlock each other. + * + * <p>It should be noted that using a {@code Striped<L>} with relatively few stripes, and + * {@code bulkGet(keys)} with a relative large number of keys can cause an excessive number + * of shared stripes (much like the birthday paradox, where much fewer than anticipated birthdays + * are needed for a pair of them to match). Please consider carefully the implications of the + * number of stripes, the intended concurrency level, and the typical number of keys used in a + * {@code bulkGet(keys)} operation. See <a href="http://www.mathpages.com/home/kmath199.htm">Balls + * in Bins model</a> for mathematical formulas that can be used to estimate the probability of + * collisions. + * + * @param keys arbitrary non-null keys + * @return the stripes corresponding to the objects (one per each object, derived by delegating + * to {@link #get(Object)}; may contain duplicates), in an increasing index order. + */ + public Iterable<L> bulkGet(Iterable<?> keys) { + // Initially using the array to store the keys, then reusing it to store the respective L's + final Object[] array = Iterables.toArray(keys, Object.class); + int[] stripes = new int[array.length]; + for (int i = 0; i < array.length; i++) { + stripes[i] = indexFor(array[i]); + } + Arrays.sort(stripes); + for (int i = 0; i < array.length; i++) { + array[i] = getAt(stripes[i]); + } + /* + * Note that the returned Iterable holds references to the returned stripes, to avoid + * error-prone code like: + * + * Striped<Lock> stripedLock = Striped.lazyWeakXXX(...)' + * Iterable<Lock> locks = stripedLock.bulkGet(keys); + * for (Lock lock : locks) { + * lock.lock(); + * } + * operation(); + * for (Lock lock : locks) { + * lock.unlock(); + * } + * + * If we only held the int[] stripes, translating it on the fly to L's, the original locks + * might be garbage collected after locking them, ending up in a huge mess. + */ + @SuppressWarnings("unchecked") // we carefully replaced all keys with their respective L's + List<L> asList = (List<L>) Arrays.asList(array); + return Collections.unmodifiableList(asList); + } + + // Static factories + + /** + * Creates a {@code Striped<Lock>} with eagerly initialized, strongly referenced locks, with the + * specified fairness. Every lock is reentrant. + * + * @param stripes the minimum number of stripes (locks) required + * @return a new {@code Striped<Lock>} + */ + public static Striped<Lock> lock(int stripes) { + return new CompactStriped<Lock>(stripes, new Supplier<Lock>() { + public Lock get() { + return new PaddedLock(); + } + }); + } + + /** + * Creates a {@code Striped<Lock>} with lazily initialized, weakly referenced locks, with the + * specified fairness. Every lock is reentrant. + * + * @param stripes the minimum number of stripes (locks) required + * @return a new {@code Striped<Lock>} + */ + public static Striped<Lock> lazyWeakLock(int stripes) { + return new LazyStriped<Lock>(stripes, new Supplier<Lock>() { + public Lock get() { + return new ReentrantLock(false); + } + }); + } + + /** + * Creates a {@code Striped<Semaphore>} with eagerly initialized, strongly referenced semaphores, + * with the specified number of permits and fairness. + * + * @param stripes the minimum number of stripes (semaphores) required + * @param permits the number of permits in each semaphore + * @return a new {@code Striped<Semaphore>} + */ + public static Striped<Semaphore> semaphore(int stripes, final int permits) { + return new CompactStriped<Semaphore>(stripes, new Supplier<Semaphore>() { + public Semaphore get() { + return new PaddedSemaphore(permits); + } + }); + } + + /** + * Creates a {@code Striped<Semaphore>} with lazily initialized, weakly referenced semaphores, + * with the specified number of permits and fairness. + * + * @param stripes the minimum number of stripes (semaphores) required + * @param permits the number of permits in each semaphore + * @return a new {@code Striped<Semaphore>} + */ + public static Striped<Semaphore> lazyWeakSemaphore(int stripes, final int permits) { + return new LazyStriped<Semaphore>(stripes, new Supplier<Semaphore>() { + public Semaphore get() { + return new Semaphore(permits, false); + } + }); + } + + /** + * Creates a {@code Striped<ReadWriteLock>} with eagerly initialized, strongly referenced + * read-write locks, with the specified fairness. Every lock is reentrant. + * + * @param stripes the minimum number of stripes (locks) required + * @return a new {@code Striped<ReadWriteLock>} + */ + public static Striped<ReadWriteLock> readWriteLock(int stripes) { + return new CompactStriped<ReadWriteLock>(stripes, READ_WRITE_LOCK_SUPPLIER); + } + + /** + * Creates a {@code Striped<ReadWriteLock>} with lazily initialized, weakly referenced + * read-write locks, with the specified fairness. Every lock is reentrant. + * + * @param stripes the minimum number of stripes (locks) required + * @return a new {@code Striped<ReadWriteLock>} + */ + public static Striped<ReadWriteLock> lazyWeakReadWriteLock(int stripes) { + return new LazyStriped<ReadWriteLock>(stripes, READ_WRITE_LOCK_SUPPLIER); + } + + // ReentrantReadWriteLock is large enough to make padding probably unnecessary + private static final Supplier<ReadWriteLock> READ_WRITE_LOCK_SUPPLIER = + new Supplier<ReadWriteLock>() { + public ReadWriteLock get() { + return new ReentrantReadWriteLock(); + } + }; + + private abstract static class PowerOfTwoStriped<L> extends Striped<L> { + /** Capacity (power of two) minus one, for fast mod evaluation */ + final int mask; + + PowerOfTwoStriped(int stripes) { + Preconditions.checkArgument(stripes > 0, "Stripes must be positive"); + this.mask = stripes > Ints.MAX_POWER_OF_TWO ? ALL_SET : ceilToPowerOfTwo(stripes) - 1; + } + + @Override final int indexFor(Object key) { + int hash = smear(key.hashCode()); + return hash & mask; + } + + @Override public final L get(Object key) { + return getAt(indexFor(key)); + } + } + + /** + * Implementation of Striped where 2^k stripes are represented as an array of the same length, + * eagerly initialized. + */ + private static class CompactStriped<L> extends PowerOfTwoStriped<L> { + /** Size is a power of two. */ + private final Object[] array; + + private CompactStriped(int stripes, Supplier<L> supplier) { + super(stripes); + Preconditions.checkArgument(stripes <= Ints.MAX_POWER_OF_TWO, "Stripes must be <= 2^30)"); + + this.array = new Object[mask + 1]; + for (int i = 0; i < array.length; i++) { + array[i] = supplier.get(); + } + } + + @SuppressWarnings("unchecked") // we only put L's in the array + @Override public L getAt(int index) { + return (L) array[index]; + } + + @Override public int size() { + return array.length; + } + } + + /** + * Implementation of Striped where up to 2^k stripes can be represented, using a Cache + * where the key domain is [0..2^k). To map a user key into a stripe, we take a k-bit slice of the + * user key's (smeared) hashCode(). The stripes are lazily initialized and are weakly referenced. + */ + private static class LazyStriped<L> extends PowerOfTwoStriped<L> { + final ConcurrentMap<Integer, L> cache; + final int size; + + LazyStriped(int stripes, Supplier<L> supplier) { + super(stripes); + this.size = (mask == ALL_SET) ? Integer.MAX_VALUE : mask + 1; + this.cache = new MapMaker().weakValues().makeComputingMap(Functions.forSupplier(supplier)); + } + + @Override public L getAt(int index) { + Preconditions.checkElementIndex(index, size()); + return cache.get(index); + } + + @Override public int size() { + return size; + } + } + + /** + * A bit mask were all bits are set. + */ + private static final int ALL_SET = ~0; + + private static int ceilToPowerOfTwo(int x) { + return 1 << IntMath.log2(x, RoundingMode.CEILING); + } + + /* + * This method was 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/licenses/publicdomain + * + * As of 2010/06/11, this method is identical to the (package private) hash + * method in OpenJDK 7's java.util.HashMap class. + */ + // Copied from java/com/google/common/collect/Hashing.java + private static int smear(int hashCode) { + hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12); + return hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4); + } + + private static class PaddedLock extends ReentrantLock { + /* + * Padding from 40 into 64 bytes, same size as cache line. Might be beneficial to add + * a fourth long here, to minimize chance of interference between consecutive locks, + * but I couldn't observe any benefit from that. + */ + @SuppressWarnings("unused") + long q1, q2, q3; + + PaddedLock() { + super(false); + } + } + + private static class PaddedSemaphore extends Semaphore { + // See PaddedReentrantLock comment + @SuppressWarnings("unused") + long q1, q2, q3; + + PaddedSemaphore(int permits) { + super(permits, false); + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java b/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java new file mode 100644 index 0000000..167ad11 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java @@ -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 com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +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> + * 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. + * @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; + } + }; + } +} diff --git a/guava/src/com/google/common/util/concurrent/TimeLimiter.java b/guava/src/com/google/common/util/concurrent/TimeLimiter.java new file mode 100644 index 0000000..a9938de --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/TimeLimiter.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2006 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +/** + * Produces proxies that impose a time limit on method + * calls to the proxied object. For example, to return the value of + * {@code target.someMethod()}, but substitute {@code DEFAULT_VALUE} if this + * method call takes over 50 ms, you can use this code: + * <pre> + * TimeLimiter limiter = . . .; + * TargetType proxy = limiter.newProxy( + * target, TargetType.class, 50, TimeUnit.MILLISECONDS); + * try { + * return proxy.someMethod(); + * } catch (UncheckedTimeoutException e) { + * return DEFAULT_VALUE; + * } + * </pre> + * Please see {@code SimpleTimeLimiterTest} for more usage examples. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@Beta +public interface TimeLimiter { + + /** + * Returns an instance of {@code interfaceType} that delegates all method + * calls to the {@code target} object, enforcing the specified time limit on + * each call. This time-limited delegation is also performed for calls to + * {@link Object#equals}, {@link Object#hashCode}, and + * {@link Object#toString}. + * <p> + * If the target method call finishes before the limit is reached, the return + * value or exception is propagated to the caller exactly as-is. If, on the + * other hand, the time limit is reached, the proxy will attempt to abort the + * call to the target, and will throw an {@link UncheckedTimeoutException} to + * the caller. + * <p> + * It is important to note that the primary purpose of the proxy object is to + * return control to the caller when the timeout elapses; aborting the target + * method call is of secondary concern. The particular nature and strength + * of the guarantees made by the proxy is implementation-dependent. However, + * it is important that each of the methods on the target object behaves + * appropriately when its thread is interrupted. + * + * @param target the object to proxy + * @param interfaceType the interface you wish the returned proxy to + * implement + * @param timeoutDuration with timeoutUnit, the maximum length of time that + * callers are willing to wait on each method call to the proxy + * @param timeoutUnit with timeoutDuration, the maximum length of time that + * callers are willing to wait on each method call to the proxy + * @return a time-limiting proxy + * @throws IllegalArgumentException if {@code interfaceType} is a regular + * class, enum, or annotation type, rather than an interface + */ + <T> T newProxy(T target, Class<T> interfaceType, + long timeoutDuration, TimeUnit timeoutUnit); + + /** + * Invokes a specified Callable, timing out after the specified time limit. + * If the target method call finished before the limit is reached, the return + * value or exception is propagated to the caller exactly as-is. If, on the + * other hand, the time limit is reached, we attempt to abort the call to the + * target, and throw an {@link UncheckedTimeoutException} to the caller. + * <p> + * <b>Warning:</b> The future of this method is in doubt. It may be nuked, or + * changed significantly. + * + * @param callable the Callable to execute + * @param timeoutDuration with timeoutUnit, the maximum length of time to wait + * @param timeoutUnit with timeoutDuration, the maximum length of time to wait + * @param interruptible whether to respond to thread interruption by aborting + * the operation and throwing InterruptedException; if false, the + * operation is allowed to complete or time out, and the current thread's + * interrupt status is re-asserted. + * @return the result returned by the Callable + * @throws InterruptedException if {@code interruptible} is true and our + * thread is interrupted during execution + * @throws UncheckedTimeoutException if the time limit is reached + * @throws Exception + */ + <T> T callWithTimeout(Callable<T> callable, long timeoutDuration, + TimeUnit timeoutUnit, boolean interruptible) throws Exception; +} diff --git a/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java b/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java new file mode 100644 index 0000000..bdff7db --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java @@ -0,0 +1,65 @@ +/* + * 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 com.google.common.util.concurrent; + +import static java.util.logging.Level.SEVERE; + +import com.google.common.annotations.VisibleForTesting; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.logging.Logger; + +/** + * Factories for {@link UncaughtExceptionHandler} instances. + * + * @author Gregory Kick + * @since 8.0 + */ +public final class UncaughtExceptionHandlers { + private UncaughtExceptionHandlers() {} + + /** + * Returns an exception handler that exits the system. This is particularly useful for the main + * thread, which may start up other, non-daemon threads, but fail to fully initialize the + * application successfully. + * + * <p>Example usage: + * <pre>public static void main(String[] args) { + * Thread.currentThread().setUncaughtExceptionHandler(UncaughtExceptionHandlers.systemExit()); + * ... + * </pre> + */ + public static UncaughtExceptionHandler systemExit() { + return new Exiter(Runtime.getRuntime()); + } + + @VisibleForTesting static final class Exiter implements UncaughtExceptionHandler { + private static final Logger logger = Logger.getLogger(Exiter.class.getName()); + + private final Runtime runtime; + + Exiter(Runtime runtime) { + this.runtime = runtime; + } + + @Override public void uncaughtException(Thread t, Throwable e) { + // cannot use FormattingLogger due to a dependency loop + logger.log(SEVERE, String.format("Caught an exception in %s. Shutting down.", t), e); + runtime.exit(1); + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java b/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java new file mode 100644 index 0000000..ad84535 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java @@ -0,0 +1,69 @@ +/* + * 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 com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * 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 + */ +@Beta +@GwtCompatible +public 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; +} diff --git a/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java b/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java new file mode 100644 index 0000000..d821c84 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2006 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 com.google.common.util.concurrent; + +/** + * Unchecked version of {@link java.util.concurrent.TimeoutException}. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +public class UncheckedTimeoutException extends RuntimeException { + public UncheckedTimeoutException() {} + + public UncheckedTimeoutException(String message) { + super(message); + } + + public UncheckedTimeoutException(Throwable cause) { + super(cause); + } + + public UncheckedTimeoutException(String message, Throwable cause) { + super(message, cause); + } + + private static final long serialVersionUID = 0; +} diff --git a/guava/src/com/google/common/util/concurrent/Uninterruptibles.java b/guava/src/com/google/common/util/concurrent/Uninterruptibles.java new file mode 100644 index 0000000..89f30b8 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/Uninterruptibles.java @@ -0,0 +1,278 @@ +/* + * 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 com.google.common.util.concurrent; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Utilities for treating interruptible operations as uninterruptible. + * In all cases, if a thread is interrupted during such a call, the call + * continues to block until the result is available or the timeout elapses, + * and only then re-interrupts the thread. + * + * @author Anthony Zana + * @since 10.0 + */ +@Beta +public final class Uninterruptibles { + + // Implementation Note: As of 3-7-11, the logic for each blocking/timeout + // methods is identical, save for method being invoked. + + /** + * Invokes {@code latch.}{@link CountDownLatch#await() await()} + * uninterruptibly. + */ + public static void awaitUninterruptibly(CountDownLatch latch) { + boolean interrupted = false; + try { + while (true) { + try { + latch.await(); + return; + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes + * {@code latch.}{@link CountDownLatch#await(long, TimeUnit) + * await(timeout, unit)} uninterruptibly. + */ + public static boolean awaitUninterruptibly(CountDownLatch latch, + long timeout, TimeUnit unit) { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while (true) { + try { + // CountDownLatch treats negative timeouts just like zero. + return latch.await(remainingNanos, NANOSECONDS); + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly. + */ + public static void joinUninterruptibly(Thread toJoin) { + boolean interrupted = false; + try { + while (true) { + try { + toJoin.join(); + return; + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code future.}{@link Future#get() get()} uninterruptibly. + * To get uninterruptibility and remove checked exceptions, see + * {@link Futures#getUnchecked}. + * + * <p>If instead, you wish to treat {@link InterruptedException} uniformly + * with other exceptions, see {@link Futures#get(Future, Class) Futures.get} + * or {@link Futures#makeChecked}. + */ + public static <V> V getUninterruptibly(Future<V> future) + throws ExecutionException { + boolean interrupted = false; + try { + while (true) { + try { + return future.get(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes + * {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} + * uninterruptibly. + * + * <p>If instead, you wish to treat {@link InterruptedException} uniformly + * with other exceptions, see {@link Futures#get(Future, Class) Futures.get} + * or {@link Futures#makeChecked}. + */ + public static <V> V getUninterruptibly( + Future<V> future, long timeout, TimeUnit unit) + throws ExecutionException, TimeoutException { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while (true) { + try { + // Future treats negative timeouts just like zero. + return future.get(remainingNanos, NANOSECONDS); + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes + * {@code unit.}{@link TimeUnit#timedJoin(Thread, long) + * timedJoin(toJoin, timeout)} uninterruptibly. + */ + public static void joinUninterruptibly(Thread toJoin, + long timeout, TimeUnit unit) { + Preconditions.checkNotNull(toJoin); + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + while (true) { + try { + // TimeUnit.timedJoin() treats negative timeouts just like zero. + NANOSECONDS.timedJoin(toJoin, remainingNanos); + return; + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly. + */ + public static <E> E takeUninterruptibly(BlockingQueue<E> queue) { + boolean interrupted = false; + try { + while (true) { + try { + return queue.take(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)} + * uninterruptibly. + */ + public static <E> void putUninterruptibly(BlockingQueue<E> queue, E element) { + boolean interrupted = false; + try { + while (true) { + try { + queue.put(element); + return; + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + // TODO(user): Support Sleeper somehow (wrapper or interface method)? + /** + * Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} + * uninterruptibly. + */ + public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(sleepFor); + long end = System.nanoTime() + remainingNanos; + while (true) { + try { + // TimeUnit.sleep() treats negative timeouts just like zero. + NANOSECONDS.sleep(remainingNanos); + return; + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + // TODO(user): Add support for waitUninterruptibly. + + private Uninterruptibles() {} +} diff --git a/guava/src/com/google/common/util/concurrent/package-info.java b/guava/src/com/google/common/util/concurrent/package-info.java new file mode 100644 index 0000000..6ea5069 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/package-info.java @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/** + * Concurrency utilities. + * + * <p>Commonly used types include {@link + * com.google.common.util.concurrent.ListenableFuture} and {@link + * com.google.common.util.concurrent.Service}. + * + * <p>Commonly used utilities include {@link + * com.google.common.util.concurrent.Futures}, {@link + * com.google.common.util.concurrent.MoreExecutors}, and {@link + * com.google.common.util.concurrent.ThreadFactoryBuilder}. + * + * <p>This package is a part of the open-source + * <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. + */ +@ParametersAreNonnullByDefault +package com.google.common.util.concurrent; + +import javax.annotation.ParametersAreNonnullByDefault; + |