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

io.atlassian.util.concurrent.SettableFuture Maven / Gradle / Ivy

Go to download

This project contains utility classes that are used by various products and projects inside Atlassian and may have some utility to the world at large.

There is a newer version: 4.0.1
Show newest version
/**
 * Copyright 2008 Atlassian Pty Ltd 
 * 
 * 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 io.atlassian.util.concurrent;

import net.jcip.annotations.ThreadSafe;

import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

/**
 * {@link io.atlassian.util.concurrent.SettableFuture} is a
 * {@link java.util.concurrent.Future} implementation where the responsibility
 * for producing the result is external to the future instance, unlike
 * {@link java.util.concurrent.FutureTask} where the future holds the operation
 * (a {@link java.util.concurrent.Callable} or {@link java.lang.Runnable}
 * instance) and the first thread that calls
 * {@link java.util.concurrent.FutureTask#run()} executes the operation.
 * 

* This is useful in situations where all the inputs may not be available at * construction time. */ @ThreadSafe public class SettableFuture implements Future { private final AtomicReference> ref = new AtomicReference>(); private final CountDownLatch latch = new CountDownLatch(1); /** * Set the value returned by {@link #get()} and {@link #get(long, TimeUnit)} *

* Note that this can only be done once unless the value of the second set * equals the first value otherwise an exception will be thrown. It also * cannot be set if this future has been cancelled or an exception has been * set. * * @param value the value to be set. * @return a {@link io.atlassian.util.concurrent.SettableFuture} object. */ public final SettableFuture set(final T value) { setAndCheckValue(new ReferenceValue(value)); return this; } /** * Set the exception thrown as the causal exception of an ExecutionException * by {@link #get()} and {@link #get(long, TimeUnit)} *

* Note that this can only be done once unless the value of the second * {@link #setException(Throwable)} equals the first value otherwise an * exception will be thrown (as most exceptions do not implement equals this * effectively means the same reference). It also cannot be set if this future * has been cancelled or a a value has been set. * * @param throwable a {@link java.lang.Throwable}. * @return a {@link io.atlassian.util.concurrent.SettableFuture}. */ public final SettableFuture setException(final Throwable throwable) { setAndCheckValue(new ThrowableValue(throwable)); return this; } /** * Get the value of the future. Blocking * * @return a T. * @throws java.lang.InterruptedException if any. * @throws java.util.concurrent.ExecutionException if any. */ public final T get() throws InterruptedException, ExecutionException { latch.await(); return ref.get().get(); } /** {@inheritDoc} */ public final T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (!latch.await(timeout, unit)) { throw new TimedOutException(timeout, unit); } return ref.get().get(); } /** {@inheritDoc} */ public final boolean isDone() { return ref.get() != null; } /** {@inheritDoc} */ public final boolean isCancelled() { return isDone() && (ref.get() instanceof CancelledValue); } /** {@inheritDoc} */ public final boolean cancel(final boolean mayInterruptIfRunning) { return setValue(new CancelledValue()) == null; } /** * Set the inner value and check that if there is an old value it equals the * one we are setting. * * @param value to set. */ private void setAndCheckValue(final Value value) { final Value oldValue = setValue(value); if ((oldValue != null) && !value.equals(oldValue)) { throw new IllegalStateException("cannot change value after it has been set"); } } /** * Set the inner value. * * @param value to set. * @return the old value if set or null. */ private Value setValue(final Value value) { while (true) { final Value oldValue = ref.get(); if (oldValue != null) { return oldValue; } // /CLOVER:OFF if (!ref.compareAndSet(null, value)) { continue; } // /CLOVER:ON latch.countDown(); return null; } } /** the inner value */ private static interface Value { T get() throws ExecutionException; } /** holds a reference */ private static class ReferenceValue implements Value { private final T value; ReferenceValue(final T value) { this.value = value; } public T get() { return value; } @Override public boolean equals(final Object obj) { // no need to check for reference equality, not possible if (!(obj instanceof ReferenceValue)) { return false; } final ReferenceValue other = (ReferenceValue) obj; return (value == null) ? (other.value == null) : value.equals(other.value); } // /CLOVER:OFF @Override public int hashCode() { throw new UnsupportedOperationException(); } // /CLOVER:ON } /** holds an exception */ private static class ThrowableValue implements Value { private final Throwable throwable; ThrowableValue(final Throwable throwable) { this.throwable = throwable; } public T get() throws ExecutionException { throw new ExecutionException(throwable); } @Override public boolean equals(final Object obj) { // no need to check for reference equality, not possible if (!(obj instanceof ThrowableValue)) { return false; } return throwable.equals(((ThrowableValue) obj).throwable); } // /CLOVER:OFF @Override public int hashCode() { throw new UnsupportedOperationException(); } // /CLOVER:ON } // doesn't need to implement equals as cancel doesn't check private static class CancelledValue implements Value { public T get() throws ExecutionException { throw new CancellationException(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy