/*******************************************************************************
* Copyright (c) 2006, 2015 Wind River Systems 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.concurrent;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.cdt.dsf.internal.DsfPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
/**
* A convenience class that allows a client to retrieve data from services
* synchronously from a non-dispatch thread. This class is different from
* a Callable<V> in that it allows the implementation code to calculate
* the result in several dispatches, rather than requiring it to return the
* data at end of Callable#call method.
* <p>
* Usage:<br/>
* <pre>
* class DataQuery extends Query<Data> {
* protected void execute(DataRequestMonitor<Data> rm) {
* rm.setData(fSlowService.getData());
* rm.done();
* }
* }
*
* DsfExecutor executor = getExecutor();
* DataQuery query = new DataQuery();
* executor.submit(query);
*
* try {
* Data data = query.get();
* }
*
* </pre>
* <p>
* @see java.util.concurrent.Callable
*
* @since 1.0
*/
@ThreadSafe
abstract public class Query<V> extends DsfRunnable
implements Future<V>
{
private class QueryRm extends DataRequestMonitor<V> {
boolean fExecuted = false;
boolean fCompleted = false;
private QueryRm() {
super(ImmediateExecutor.getInstance(), null);
}
@Override
public synchronized void handleCompleted() {
fCompleted = true;
notifyAll();
}
public synchronized boolean isCompleted() {
return fCompleted;
}
public synchronized boolean setExecuted() {
if (fExecuted || isCanceled()) {
// already executed or canceled
return false;
}
fExecuted = true;
return true;
}
};
private final QueryRm fRm = new QueryRm();
/**
* The no-argument constructor
*/
public Query() {}
@Override
public V get() throws InterruptedException, ExecutionException {
IStatus status;
V data;
synchronized (fRm) {
while (!isDone()) {
fRm.wait();
}
status = fRm.getStatus();
data = fRm.getData();
}
if (status.getSeverity() == IStatus.CANCEL) {
throw new CancellationException();
} else if (status.getSeverity() != IStatus.OK) {
throw new ExecutionException(new CoreException(status));
}
return data;
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
long timeLeft = unit.toMillis(timeout);
long timeoutTime = System.currentTimeMillis() + unit.toMillis(timeout);
IStatus status;
V data;
synchronized (fRm) {
while (!isDone()) {
if (timeLeft <= 0) {
throw new TimeoutException();
}
fRm.wait(timeLeft);
timeLeft = timeoutTime - System.currentTimeMillis();
}
status = fRm.getStatus();
data = fRm.getData();
}
if (status.getSeverity() == IStatus.CANCEL) {
throw new CancellationException();
} else if (status.getSeverity() != IStatus.OK) {
throw new ExecutionException(new CoreException(status));
}
return data;
}
/**
* Don't try to interrupt the DSF executor thread, just ignore the request
* if set.
*/
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
boolean completed = false;
synchronized (fRm) {
completed = fRm.isCompleted();
if (!completed) {
fRm.cancel();
fRm.notifyAll();
}
}
return !completed;
}
@Override
public boolean isCancelled() { return fRm.isCanceled(); }
@Override
public boolean isDone() {
synchronized (fRm) {
// If future is canceled, return right away.
return fRm.isCompleted() || fRm.isCanceled();
}
}
abstract protected void execute(DataRequestMonitor<V> rm);
@Override
public void run() {
if (fRm.setExecuted()) {
execute(fRm);
}
}
/**
* Completes the query with the given exception.
*
* @deprecated Query implementations should call the request monitor to
* set the exception status directly.
*/
@Deprecated
protected void doneException(Throwable t) {
fRm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Exception", t)); //$NON-NLS-1$
fRm.done();
}
}