/************************************************************************* * Copyright 2009-2012 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. * * This file may incorporate work covered under the following copyright * and permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, * with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE, * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS. * * This file may incorporate work covered under the following copyright * and permission notice: * * Copyright (C) 2009 Google Inc. * * 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.eucalyptus.util.concurrent; 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; import java.util.concurrent.locks.AbstractQueuedSynchronizer; /** * <p>An abstract implementation of the {@link Future} interface. This class * is an abstraction of {@link FutureTask} to support use * for tasks other than {@link Runnable}s. It uses an * {@link AbstractQueuedSynchronizer} to deal with concurrency issues and * guarantee thread safety. It could be used as a base class to * {@code FutureTask}, or any other implementor of the {@code Future} interface. * * <p>This class implements all methods in {@code Future}. Subclasses should * provide a way to set the result of the computation through the protected * methods {@link #set(Object)}, {@link #setException(Throwable)}, or * {@link #cancel()}. If subclasses want to implement cancellation they can * override the {@link #cancel(boolean)} method with a real implementation, the * default implementation doesn't support cancellation. * * <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. Because this class does not implement * cancellation it is left to the subclass to distinguish between created * and running tasks. * * @author Sven Mawson * @since 1 */ public abstract class AbstractFuture<V> implements Future<V> { /** Synchronization control for AbstractFutures. */ private final Sync<V> sync = new Sync<V>(); /* * Blocks until either the task completes or the timeout expires. Uses the * sync blocking-with-timeout support provided by AQS. */ public V get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException { return sync.get(unit.toNanos(timeout)); } /* * Blocks until the task completes or we get interrupted. Uses the * interruptible blocking support provided by AQS. */ public V get() throws InterruptedException, ExecutionException { return sync.get(); } /* * Checks if the sync is not in the running state. */ public boolean isDone() { return sync.isDone(); } /* * Checks if the sync is in the cancelled state. */ public boolean isCancelled() { return sync.isCancelled(); } /* * Default implementation of cancel that never cancels the future. * Subclasses should override this to implement cancellation if desired. */ public boolean cancel(boolean mayInterruptIfRunning) { return false; } /** * 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 call {@link #done()} if the * state was successfully changed. * * @param value the value that was the result of the task. * @return true if the state was successfully changed. */ protected boolean set(V value) { boolean result = sync.set(value); if (result) { done(); } 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 call {@link #done()} 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(throwable); if (result) { done(); } // 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; } /** * Subclasses should invoke this method to mark the future as cancelled. * This will set the state of the future to {@link * AbstractFuture.Sync#CANCELLED} and call {@link #done()} if the state was * successfully changed. * * @return true if the state was successfully changed. */ protected final boolean cancel() { boolean result = sync.cancel(); if (result) { done(); } return result; } /* * Called by the success, failed, or cancelled methods to indicate that the * value is now available and the latch can be released. Subclasses can * use this method to deal with any actions that should be undertaken when * the task has completed. */ protected void done() { // Default implementation does nothing. } /** * <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 ExecutionException 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 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(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}. * * @param v the value to set as the result of the computation. * @param t the exception to set as the result of the computation. * @param finalState the state to transition to. */ private boolean complete(V v, Throwable t, int finalState) { if (compareAndSetState(RUNNING, COMPLETING)) { this.value = v; this.exception = t == null ? null : new ExecutionException(t); releaseShared(finalState); return true; } // The state was not RUNNING, so there are no valid transitions. return false; } } }