001: /**
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */package com.tc.util.concurrent;
004:
005: import com.tc.exception.TCInternalError;
006: import com.tc.logging.TCLogger;
007: import com.tc.logging.TCLogging;
008: import com.tc.util.TCTimeoutException;
009:
010: /**
011: * Class to hold "future" results. This class inspired by <code>FutureResult</code> from util.concurrent, but without
012: * mandating the use of Runnable and/or Callable interfaces
013: *
014: * @author teck
015: */
016: public class TCFuture {
017: private static final TCLogger logger = TCLogging
018: .getLogger(TCFuture.class);
019: private volatile boolean set;
020: private volatile boolean cancel;
021: private volatile boolean timedOut;
022: private volatile Throwable exception;
023: private volatile Object value;
024: private final Object lock;
025:
026: public TCFuture() {
027: this (null);
028: }
029:
030: public TCFuture(Object lock) {
031: this .lock = lock == null ? this : lock;
032: cancel = false;
033: value = null;
034: exception = null;
035: set = false;
036: }
037:
038: /**
039: * Get the value of this future result, potentially blocking indefinitely until it is avaiable
040: *
041: * @return the value set in this future (which may be null)
042: * @throws InterruptedException if the current thread is interrupted while waiting for the result to be set
043: */
044: public Object get() throws InterruptedException,
045: TCExceptionResultException {
046: try {
047: return get(0);
048: } catch (TCTimeoutException e) {
049: throw new TCInternalError(
050: "Timeout not supposed to happen here");
051: }
052: }
053:
054: /**
055: * Get the value of this future result within the scope of the given timeout
056: *
057: * @param timeout time (in milliseconds) to wait before throwing a timeout exception. A value of zero will cause this
058: * method to wait indefinitely and a timeout exception will never be thrown
059: * @return the value set in this future (which may be null)
060: * @throws InterruptedException if the current thread is interrupted while waiting for the result to be set
061: * @throws TCTimeoutException if timeout period expires
062: * @throws TCExceptionResultExecption if another thread sets the future result to an exception.
063: * @see setException(Throwable t)
064: */
065: public Object get(long timeout) throws InterruptedException,
066: TCTimeoutException, TCExceptionResultException {
067: return get(timeout, true);
068: }
069:
070: /**
071: * Get the value of this future result within the scope of the given timeout
072: *
073: * @param timeout time (in milliseconds) to wait before throwing a timeout exception. A value of zero will cause this
074: * method to wait indefinitely and a timeout exception will never be thrown
075: * @param flagIfTimedOut if set to true and a TCTimeoutException is thrown waiting for the result the timedOut()
076: * method will return true, otherwise it will continue to return false
077: * @return the value set in this future (which may be null)
078: * @throws InterruptedException if the current thread is interrupted while waiting for the result to be set
079: * @throws TCTimeoutException if timeout period expires
080: * @throws TCExceptionResultExecption if another thread sets the future result to an exception.
081: * @see setException(Throwable t)
082: */
083: public Object get(long timeout, boolean flagIfTimedOut)
084: throws InterruptedException, TCTimeoutException,
085: TCExceptionResultException {
086: synchronized (this .lock) {
087: if (cancel) {
088: throw new InterruptedException(
089: "Future already cancelled");
090: }
091:
092: while (!set) {
093: if (timeout < 0) {
094: throw new TCTimeoutException("Timeout of "
095: + timeout + " milliseconds occurred");
096: }
097:
098: lock.wait(timeout);
099:
100: if (cancel) {
101: throw new InterruptedException(
102: "Future was cancelled while waiting");
103: }
104:
105: if ((!set) && (timeout != 0)) {
106: this .timedOut = flagIfTimedOut;
107: throw new TCTimeoutException("Timeout of "
108: + timeout + " milliseconds occured");
109: }
110: }
111:
112: if (exception == null) {
113: return value;
114: } else if (exception != null) {
115: // NOTE: this won't work with JDK1.3
116: throw new TCExceptionResultException(exception);
117: }
118:
119: throw new TCInternalError("Neither exception nor value set");
120: }
121: }
122:
123: /**
124: * Set the value of this future to an exception result. Thread(s) waiting for the result (in method <code>get()</code>)
125: * will be awoken. If this future has been <code>cancel()</code> 'ed, setting the value will have no effect
126: *
127: * @param ex the exception result for this future
128: * @throws IllegalStateException if a result has already been set in this future
129: */
130: public void setException(Throwable ex) {
131: if (ex == null) {
132: throw new IllegalArgumentException(
133: "exception result cannot be null");
134: }
135:
136: synchronized (this .lock) {
137: if (cancel) {
138: logger
139: .warn("Exception result set in future after it was cancelled");
140: return;
141: }
142:
143: if (set) {
144: throw new IllegalStateException(
145: "Future result already set");
146: }
147:
148: set = true;
149: this .exception = ex;
150: this .lock.notifyAll();
151: }
152: }
153:
154: /**
155: * Set the value of this future. Thread(s) waiting for the result (in method <code>get()</code>) will be awoken. If
156: * this future has been <code>cancel()</code> 'ed, setting the value will have no effect
157: *
158: * @param value the value to set into this future
159: * @throws IllegalStateException if the value has already been set in this future
160: */
161: public void set(Object value) {
162: synchronized (this .lock) {
163:
164: if (cancel) {
165: logger
166: .warn("Value set in future after it was cancelled");
167: return;
168: }
169:
170: if (set) {
171: throw new IllegalStateException("Value already set");
172: }
173:
174: set = true;
175: this .value = value;
176: this .lock.notifyAll();
177: }
178: }
179:
180: /**
181: * Cancel this future instance. Cancelling a future will cause any threads waiting on this future to receive an
182: * interrupted exception instead of a result value. Calling <code>cancel()</code> after a value has been set does
183: * not "unset" the value
184: */
185: public void cancel() {
186: synchronized (this .lock) {
187: if (set) {
188: logger
189: .warn("Attempt to cancel an already set future value");
190: }
191:
192: cancel = true;
193: this .lock.notifyAll();
194: }
195: }
196:
197: /**
198: * @return true if a call to get(long,boolean) specified true as an argument and the call timed out, false otherwise.
199: */
200: public boolean timedOut() {
201: return this.timedOut;
202: }
203:
204: }
|