| /****************************************************************************** |
| * Copyright (c) 2009, 2013 EclipseSource and others. All rights reserved. This |
| * program and the accompanying materials are made available under the terms of |
| * the Eclipse Public License v1.0 which accompanies this distribution, and is |
| * available at http://www.eclipse.org/legal/epl-v10.html |
| * |
| * 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(); |
| } |
| } |