TCFuture.java :  » Net » Terracotta » com » tc » util » concurrent » Java Open Source

Java Open Source » Net » Terracotta 
Terracotta » com » tc » util » concurrent » TCFuture.java
/**
 * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice.  All rights reserved.
 */
package com.tc.util.concurrent;

import com.tc.exception.TCInternalError;
import com.tc.logging.TCLogger;
import com.tc.logging.TCLogging;
import com.tc.util.TCTimeoutException;

/**
 * Class to hold "future" results. This class inspired by <code>FutureResult</code> from util.concurrent, but without
 * mandating the use of Runnable and/or Callable interfaces
 * 
 * @author teck
 */
public class TCFuture {
  private static final TCLogger logger = TCLogging.getLogger(TCFuture.class);
  private volatile boolean      set;
  private volatile boolean      cancel;
  private volatile boolean      timedOut;
  private volatile Throwable    exception;
  private volatile Object       value;
  private final Object lock;

  public TCFuture() {
    this(null);
  }
  
  public TCFuture(Object lock) {
    this.lock = lock == null ? this : lock;
    cancel = false;
    value = null;
    exception = null;
    set = false;
  }

  /**
   * Get the value of this future result, potentially blocking indefinitely until it is avaiable
   * 
   * @return the value set in this future (which may be null)
   * @throws InterruptedException if the current thread is interrupted while waiting for the result to be set
   */
  public Object get() throws InterruptedException, TCExceptionResultException {
    try {
      return get(0);
    } catch (TCTimeoutException e) {
      throw new TCInternalError("Timeout not supposed to happen here");
    }
  }

  /**
   * Get the value of this future result within the scope of the given timeout
   * 
   * @param timeout time (in milliseconds) to wait before throwing a timeout exception. A value of zero will cause this
   *        method to wait indefinitely and a timeout exception will never be thrown
   * @return the value set in this future (which may be null)
   * @throws InterruptedException if the current thread is interrupted while waiting for the result to be set
   * @throws TCTimeoutException if timeout period expires
   * @throws TCExceptionResultExecption if another thread sets the future result to an exception.
   * @see setException(Throwable t)
   */
  public Object get(long timeout) throws InterruptedException, TCTimeoutException, TCExceptionResultException {
    return get(timeout, true);
  }

  /**
   * Get the value of this future result within the scope of the given timeout
   * 
   * @param timeout time (in milliseconds) to wait before throwing a timeout exception. A value of zero will cause this
   *        method to wait indefinitely and a timeout exception will never be thrown
   * @param flagIfTimedOut if set to true and a TCTimeoutException is thrown waiting for the result the timedOut()
   *        method will return true, otherwise it will continue to return false
   * @return the value set in this future (which may be null)
   * @throws InterruptedException if the current thread is interrupted while waiting for the result to be set
   * @throws TCTimeoutException if timeout period expires
   * @throws TCExceptionResultExecption if another thread sets the future result to an exception.
   * @see setException(Throwable t)
   */
  public Object get(long timeout, boolean flagIfTimedOut) throws InterruptedException, TCTimeoutException,
      TCExceptionResultException {
    synchronized (this.lock) {
      if (cancel) { throw new InterruptedException("Future already cancelled"); }

      while (!set) {
        if (timeout < 0) { throw new TCTimeoutException("Timeout of " + timeout + " milliseconds occurred"); }

        lock.wait(timeout);

        if (cancel) { throw new InterruptedException("Future was cancelled while waiting"); }

        if ((!set) && (timeout != 0)) {
          this.timedOut = flagIfTimedOut;
          throw new TCTimeoutException("Timeout of " + timeout + " milliseconds occured");
        }
      }

      if (exception == null) {
        return value;
      } else if (exception != null) {
        // NOTE: this won't work with JDK1.3
        throw new TCExceptionResultException(exception);
      }

      throw new TCInternalError("Neither exception nor value set");
    }
  }

  /**
   * Set the value of this future to an exception result. Thread(s) waiting for the result (in method <code>get()</code>)
   * will be awoken. If this future has been <code>cancel()</code> 'ed, setting the value will have no effect
   * 
   * @param ex the exception result for this future
   * @throws IllegalStateException if a result has already been set in this future
   */
  public void setException(Throwable ex) {
    if (ex == null) { throw new IllegalArgumentException("exception result cannot be null"); }

    synchronized (this.lock) {
      if (cancel) {
        logger.warn("Exception result set in future after it was cancelled");
        return;
      }

      if (set) { throw new IllegalStateException("Future result already set"); }

      set = true;
      this.exception = ex;
      this.lock.notifyAll();
    }
  }

  /**
   * Set the value of this future. Thread(s) waiting for the result (in method <code>get()</code>) will be awoken. If
   * this future has been <code>cancel()</code> 'ed, setting the value will have no effect
   * 
   * @param value the value to set into this future
   * @throws IllegalStateException if the value has already been set in this future
   */
  public void set(Object value) {
    synchronized (this.lock) {

      if (cancel) {
        logger.warn("Value set in future after it was cancelled");
        return;
      }

      if (set) { throw new IllegalStateException("Value already set"); }

      set = true;
      this.value = value;
      this.lock.notifyAll();
    }
  }

  /**
   * Cancel this future instance. Cancelling a future will cause any threads waiting on this future to receive an
   * interrupted exception instead of a result value. Calling <code>cancel()</code> after a value has been set does
   * not "unset" the value
   */
  public void cancel() {
    synchronized (this.lock) {
      if (set) {
        logger.warn("Attempt to cancel an already set future value");
      }

      cancel = true;
      this.lock.notifyAll();
    }
  }

  /**
   * @return true if a call to get(long,boolean) specified true as an argument and the call timed out, false otherwise.
   */
  public boolean timedOut() {
    return this.timedOut;
  }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.