blob: 8f738b66ef5d672e06bd3009310574b56f5f8bec [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2010, 2013 EclipseSource and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
package org.eclipse.equinox.concurrent.future;
import org.eclipse.core.runtime.*;
/**
* <p>
* Future implementation for a single operation.
* </p>
* <p>
* Subclasses may be created if desired. Note that if subclasses are created,
* that they should/must be very careful with respect to overriding the
* synchronized methods in this class.
* </p>
* @since 1.1
*/
public class SingleOperationFuture<ResultType> extends
AbstractFuture<ResultType> {
private static final String PLUGIN_ID = "org.eclipse.equinox.concurrent";
private ResultType resultValue = null;
private IStatus status = null;
private TimeoutException timeoutException = null;
protected IProgressMonitor progressMonitor;
public SingleOperationFuture() {
this((IProgressMonitor) null);
}
public SingleOperationFuture(IProgressMonitor progressMonitor) {
super();
this.progressMonitor = new FutureProgressMonitor(
(progressMonitor == null) ? new NullProgressMonitor()
: progressMonitor);
}
public synchronized ResultType get() throws InterruptedException,
OperationCanceledException {
throwIfCanceled();
while (!isDone())
wait();
throwIfCanceled();
return resultValue;
}
public synchronized ResultType get(long waitTimeInMillis)
throws InterruptedException, TimeoutException,
OperationCanceledException {
// If waitTime out of bounds then throw illegal argument exception
if (waitTimeInMillis < 0)
throw new IllegalArgumentException("waitTimeInMillis must be => 0"); //$NON-NLS-1$
// If we've been canceled then throw
throwIfCanceled();
// If we've previously experienced a timeout then throw
if (timeoutException != null)
throw timeoutException;
// If we're already done, then return result
if (isDone())
return resultValue;
// Otherwise, wait for some time, then throw if canceled during wait,
// return value if
// Compute start time and set waitTime
long startTime = System.currentTimeMillis();
long waitTime = waitTimeInMillis;
// we've received one during wait or throw timeout exception if too much
// time has elapsed
for (;;) {
wait(waitTime);
throwIfCanceled();
if (isDone())
return resultValue;
waitTime = waitTimeInMillis
- (System.currentTimeMillis() - startTime);
if (waitTime <= 0)
throw createTimeoutException(waitTimeInMillis);
}
}
public synchronized boolean isDone() {
return (status != null);
}
/**
* This method is not intended to be called by clients. Rather it should
* only be used by {@link IExecutor}s.
*
* @noreference
*/
public void runWithProgress(final IProgressRunnable<?> runnable) {
Assert.isNotNull(runnable);
if (!isCanceled()) {
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
synchronized (SingleOperationFuture.this) {
if (!isCanceled())
setException(exception);
}
}
public void run() throws Exception {
@SuppressWarnings("unchecked")
ResultType result = (ResultType) runnable
.run(getProgressMonitor());
synchronized (SingleOperationFuture.this) {
if (!isCanceled())
set(result);
}
}
});
}
}
public synchronized IStatus getStatus() {
return status;
}
public boolean hasValue() {
// for a single operation future, hasValue means that the single
// operation has completed, and there will be no more.
return isDone();
}
public synchronized boolean cancel() {
if (isDone())
return false;
if (isCanceled())
return false;
setStatus(new Status(IStatus.CANCEL, PLUGIN_ID, IStatus.CANCEL,
"Operation canceled", null)); //$NON-NLS-1$ //$NON-NLS-2$
getProgressMonitor().setCanceled(true);
notifyAll();
return true;
}
protected synchronized void setException(Throwable ex) {
setStatus(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR,
"Exception during operation", ex)); //$NON-NLS-1$ //$NON-NLS-2$
notifyAll();
}
protected synchronized void set(ResultType newValue) {
resultValue = newValue;
setStatus(Status.OK_STATUS);
notifyAll();
}
private synchronized void setStatus(IStatus status) {
this.status = status;
}
private TimeoutException createTimeoutException(long timeout) {
setStatus(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR,
"Operation timeout after " + timeout + "ms", null)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
timeoutException = new TimeoutException(
"Single operation timeout", timeout); //$NON-NLS-1$
return timeoutException;
}
private void throwIfCanceled() throws OperationCanceledException {
IProgressMonitor pm = getProgressMonitor();
if (pm != null && pm.isCanceled()) {
throw new OperationCanceledException("Single operation canceled"); //$NON-NLS-1$
}
}
public synchronized IProgressMonitor getProgressMonitor() {
return progressMonitor;
}
public synchronized boolean isCanceled() {
return getProgressMonitor().isCanceled();
}
}