/****************************************************************************** * 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(); } }