/******************************************************************************* * Copyright (c) 2006, 2011 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.tcf.debug.test.util; 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.tcf.protocol.Protocol; /** * Copied and adapted from org.eclipse.cdt.dsf.concurrent. * * 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(DataCallback<Data> callback) { * callback.setData(fSlowService.getData()); * callback.done(); * } * } * * DsfExecutor executor = getExecutor(); * DataQuery query = new DataQuery(); * executor.submit(query); * * try { * Data data = query.get(); * } * * </pre> * <p> * @see java.util.concurrent.Callable * */ abstract public class Query<V> implements Future<V> { private class QueryCallback extends DataCallback<V> { boolean fExecuted = false; boolean fCompleted = false; private QueryCallback() { super(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 QueryCallback fCallback = new QueryCallback(); /** * The no-argument constructor */ public Query() {} public V get() throws InterruptedException, ExecutionException { invoke(); Throwable error; V data; synchronized (fCallback) { while (!isDone()) { fCallback.wait(); } error = fCallback.getError(); data = fCallback.getData(); } if (error instanceof CancellationException) { throw new CancellationException(); } else if (error != null) { throw new ExecutionException(error); } return data; } public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { invoke(); long timeLeft = unit.toMillis(timeout); long timeoutTime = System.currentTimeMillis() + unit.toMillis(timeout); Throwable error; V data; synchronized (fCallback) { while (!isDone()) { if (timeLeft <= 0) { throw new TimeoutException(); } fCallback.wait(timeLeft); timeLeft = timeoutTime - System.currentTimeMillis(); } error = fCallback.getError(); data = fCallback.getData(); } if (error instanceof CancellationException) { throw new CancellationException(); } else if (error != null) { throw new ExecutionException(error); } return data; } /** * Don't try to interrupt the DSF executor thread, just ignore the request * if set. */ public boolean cancel(boolean mayInterruptIfRunning) { boolean completed = false; synchronized (fCallback) { completed = fCallback.isCompleted(); if (!completed) { fCallback.cancel(); fCallback.notifyAll(); } } return !completed; } public boolean isCancelled() { return fCallback.isCanceled(); } public boolean isDone() { synchronized (fCallback) { // If future is canceled, return right away. return fCallback.isCompleted() || fCallback.isCanceled(); } } abstract protected void execute(DataCallback<V> callback); public void invoke() { Protocol.invokeLater(new Runnable() { public void run() { if (fCallback.setExecuted()) { execute(fCallback); } } }); } }