diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/DsfPlugin.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/DsfPlugin.java
index abcae5d1d16..0e05acf461c 100644
--- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/DsfPlugin.java
+++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/DsfPlugin.java
@@ -76,6 +76,24 @@ public class DsfPlugin extends Plugin {
System.out.println(message);
}
}
+
+ public static String getDebugTime() {
+ StringBuilder traceBuilder = new StringBuilder();
+
+ // Record the time
+ long time = System.currentTimeMillis();
+ long seconds = (time / 1000) % 1000;
+ if (seconds < 100) traceBuilder.append('0');
+ if (seconds < 10) traceBuilder.append('0');
+ traceBuilder.append(seconds);
+ traceBuilder.append(',');
+ long millis = time % 1000;
+ if (millis < 100) traceBuilder.append('0');
+ if (millis < 10) traceBuilder.append('0');
+ traceBuilder.append(millis);
+ return traceBuilder.toString();
+ }
+
}
diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/ConfinedToDsfExecutor.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/ConfinedToDsfExecutor.java
index 3b880946af5..9feab36de06 100644
--- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/ConfinedToDsfExecutor.java
+++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/ConfinedToDsfExecutor.java
@@ -30,7 +30,7 @@ import java.lang.annotation.Target;
* It should be null if it cannot be determined from the given object.
*/
@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})
@Inherited
@Documented
public @interface ConfinedToDsfExecutor {
diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java
index 1453b258d82..44e07542379 100644
--- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java
+++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java
@@ -91,7 +91,6 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
}
}
}
-
//
// Utilities used for tracing.
@@ -132,7 +131,7 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
int fSequenceNumber = -1;
/** Trace of where the runnable/callable was submitted to the executor */
- StackTraceElement[] fSubmittedAt = null;
+ StackTraceWrapper fSubmittedAt = null;
/** Reference to the runnable/callable that submitted this runnable/callable to the executor */
TracingWrapper fSubmittedBy = null;
@@ -142,8 +141,8 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
*/
TracingWrapper(int offset) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
- fSubmittedAt = new StackTraceElement[stackTrace.length - offset];
- System.arraycopy(stackTrace, offset - 1, fSubmittedAt, 0, fSubmittedAt.length);
+ fSubmittedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - offset]);
+ System.arraycopy(stackTrace, offset - 1, fSubmittedAt.fStackTraceElements, 0, fSubmittedAt.fStackTraceElements.length);
if (isInExecutorThread() && fCurrentlyExecuting != null) {
fSubmittedBy = fCurrentlyExecuting;
}
@@ -158,16 +157,7 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
StringBuilder traceBuilder = new StringBuilder();
// Record the time
- long time = System.currentTimeMillis();
- long seconds = (time / 1000) % 1000;
- if (seconds < 100) traceBuilder.append('0');
- if (seconds < 10) traceBuilder.append('0');
- traceBuilder.append(seconds);
- traceBuilder.append(',');
- long millis = time % 1000;
- if (millis < 100) traceBuilder.append('0');
- if (millis < 10) traceBuilder.append('0');
- traceBuilder.append(millis);
+ traceBuilder.append(DsfPlugin.getDebugTime());
traceBuilder.append(' ');
// Record the executor #
@@ -187,7 +177,7 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
}
if (dsfExecutable.fCreatedAt != null) {
traceBuilder.append(" at ");
- traceBuilder.append(dsfExecutable.fCreatedAt[0].toString());
+ traceBuilder.append(dsfExecutable.fCreatedAt.fStackTraceElements[0].toString());
}
}
}
@@ -200,7 +190,7 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
traceBuilder.append(fSubmittedBy.fSequenceNumber);
}
traceBuilder.append(" at ");
- traceBuilder.append(fSubmittedAt[0].toString());
+ traceBuilder.append(fSubmittedAt.fStackTraceElements[0].toString());
// Finally, the executable's toString().
traceBuilder.append("\n ");
@@ -214,6 +204,7 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
abstract protected Object getExecutable();
}
+
class TracingWrapperRunnable extends TracingWrapper implements Runnable {
final Runnable fRunnable;
public TracingWrapperRunnable(Runnable runnable, int offset) {
@@ -225,6 +216,13 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
public void run() {
traceExecution();
+
+ // If debugging a DSF exeutable, mark that it was executed.
+ if (DEBUG_EXECUTOR && fRunnable instanceof DsfExecutable) {
+ ((DsfExecutable)fRunnable).setExecuted();
+ }
+
+ // Finally invoke the runnable code.
fRunnable.run();
}
}
@@ -240,6 +238,13 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
public T call() throws Exception {
traceExecution();
+
+ // If debugging a DSF exeutable, mark that it was executed.
+ if (DEBUG_EXECUTOR && fCallable instanceof DsfExecutable) {
+ ((DsfExecutable)fCallable).setExecuted();
+ }
+
+ // Finally invoke the runnable code.
return fCallable.call();
}
}
diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/Done.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/Done.java
index 7136de89b8c..2f5f26ac0c0 100644
--- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/Done.java
+++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/Done.java
@@ -53,7 +53,7 @@ abstract public class Done extends DsfRunnable {
* @return Returns true if there was an error that was propagated and
* the caller can stop processing result.
*/
- protected boolean propagateErrorToClient(DsfExecutor executor, Done clientDone, String message) {
+ protected boolean propagateError(DsfExecutor executor, Done clientDone, String message) {
if (clientDone.getStatus().getSeverity() == IStatus.CANCEL) {
return true;
}
diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutable.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutable.java
index 6ed2646a003..4b2f96e3ee8 100644
--- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutable.java
+++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutable.java
@@ -13,6 +13,9 @@ package org.eclipse.dd.dsf.concurrent;
import java.util.HashSet;
import java.util.Set;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.dd.dsf.DsfPlugin;
+
/**
* Base class for DSF-instrumented alternative to the Runnable/Callable interfaces.
*
@@ -23,15 +26,48 @@ import java.util.Set;
*/
@Immutable
public class DsfExecutable {
- final StackTraceElement[] fCreatedAt;
+ /**
+ * Flag indicating that tracing of the DSF executor is enabled. It enables
+ * storing of the "creator" information as well as tracing of disposed
+ * runnables that have not been submitted to the executor.
+ */
+ static boolean DEBUG_EXECUTOR = false;
+
+ /**
+ * Flag indicating that assertions are enabled. It enables storing of the
+ * "creator" executable for debugging purposes.
+ */
+ static boolean ASSERTIONS_ENABLED = false;
+
+ static {
+ assert ASSERTIONS_ENABLED = true;
+ DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
+ Platform.getDebugOption("org.eclipse.dd.dsf/debug/executor")); //$NON-NLS-1$
+ assert ASSERTIONS_ENABLED = true;
+ }
+
+ /**
+ * Field that holds the stack trace of where this executable was created.
+ * Used for tracing and debugging only.
+ */
+ final StackTraceWrapper fCreatedAt;
+
+ /**
+ * Field holding the reference of the executable that created this
+ * executable. Used for tracing only.
+ */
final DefaultDsfExecutor.TracingWrapper fCreatedBy;
+ /**
+ * Flag indicating whether this executable was ever executed by an
+ * executor. Used for tracing only.
+ */
+ private boolean fExecuted = false;
+
@SuppressWarnings("unchecked")
public DsfExecutable() {
// Use assertion flag (-ea) to jre to avoid affecting performance when not debugging.
- boolean assertsEnabled = false;
- assert assertsEnabled = true;
- if (assertsEnabled || DefaultDsfExecutor.DEBUG_EXECUTOR) {
+ if (ASSERTIONS_ENABLED || DEBUG_EXECUTOR) {
// Find the runnable/callable that is currently running.
DefaultDsfExecutor executor = DefaultDsfExecutor.fThreadToExecutorMap.get(Thread.currentThread());
if (executor != null) {
@@ -53,11 +89,38 @@ public class DsfExecutable {
for (i = 3; i < stackTrace.length; i++) {
if ( !classNamesSet.contains(stackTrace[i].getClassName()) ) break;
}
- fCreatedAt = new StackTraceElement[stackTrace.length - i];
- System.arraycopy(stackTrace, i, fCreatedAt, 0, fCreatedAt.length);
+ fCreatedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - i]);
+ System.arraycopy(stackTrace, i, fCreatedAt.fStackTraceElements, 0, fCreatedAt.fStackTraceElements.length);
} else {
fCreatedAt = null;
fCreatedBy = null;
}
}
+
+ /**
+ * Marks this executable to indicate that it has been executed by the
+ * executor. To be invoked only by DsfExecutor.
+ */
+ void setExecuted() {
+ fExecuted = true;
+ }
+
+ @Override
+ protected void finalize() {
+ if (DEBUG_EXECUTOR && !fExecuted) {
+ StringBuilder traceBuilder = new StringBuilder();
+
+ // Record the time
+ traceBuilder.append(DsfPlugin.getDebugTime());
+ traceBuilder.append(' ');
+
+ // Record the event
+ traceBuilder.append("DsfExecutable was never executed:\n ");
+ traceBuilder.append(this);
+ traceBuilder.append("\nCreated at:");
+ traceBuilder.append(fCreatedAt);
+
+ DsfPlugin.debug(traceBuilder.toString());
+ }
+ }
}
diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfQuery.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfQuery.java
index 351f84bd674..fce82bb2f65 100644
--- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfQuery.java
+++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfQuery.java
@@ -10,13 +10,13 @@
*******************************************************************************/
package org.eclipse.dd.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 java.util.concurrent.locks.AbstractQueuedSynchronizer;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.dd.dsf.DsfPlugin;
-import org.eclipse.dd.dsf.service.IDsfService;
/**
* A convenience class that allows a client to retrieve data from services
@@ -28,135 +28,124 @@ import org.eclipse.dd.dsf.service.IDsfService;
* @see java.util.concurrent.Callable
*/
@ThreadSafe
-abstract public class DsfQuery {
-
- private V fResult;
- private boolean fValid;
- private DsfExecutor fExecutor;
- private Future fFuture;
- private boolean fWaiting;
- private IStatus fStatus = Status.OK_STATUS;
-
- public DsfQuery(DsfExecutor executor) {
- fExecutor = executor;
+abstract public class DsfQuery extends DsfRunnable
+ implements Future
+{
+ /** The synchronization object for this query */
+ final Sync fSync = new Sync();
+
+ public V get() throws InterruptedException, ExecutionException { return fSync.doGet(); }
+
+ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return fSync.doGet();
}
-
+
/**
- * Start data retrieval.
- * Client must implement this method to do whatever is needed to retrieve data.
- * Retrieval can be (but does not have to be) asynchronious - it meas this method can return
- * before data is retrieved. When data is ready Proxy must be notified by calling done() method.
- */
- protected abstract void execute();
-
- /**
- * Allows deriving classes to implement their own snipped additional
- * cancellation code.
+ * Don't try to interrupt the DSF executor thread, just ignore the request
+ * if set.
*/
- protected void revokeChildren(V result) {};
-
- /**
- * Get data associated with this proxy. This method is thread safe and
- * it will block until data is ready. Because it's a blocking call and it waits
- * for commands to be processed on the dispatch thread, this methods itself
- * CANNOT be called on the dispatch thread.
- */
- public synchronized V get() {
- assert !fExecutor.isInExecutorThread();
- if(!fValid) {
- if (!fWaiting) {
- fFuture = fExecutor.submit(new DsfRunnable() {
- public void run() {
- // Note: not sure if this try-catch is desirable. It might encourage
- // implementors to not catch its own exceptions. If the query code takes
- // more than one dispatch, then this code will not be helpful anyway.
- try {
- DsfQuery.this.execute();
- } catch (Throwable t) {
- doneException(t);
- }
- }
- });
- }
-
- fWaiting = true;
- try {
- while(fWaiting) {
- wait();
- }
- } catch (InterruptedException e) {
- fStatus = new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR,
- "Interrupted exception while waiting for result.", e);
- fValid = true;
- }
- assert fValid;
- }
- return fResult;
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return fSync.doCancel();
}
- /**
- * Same as get(), but with code to automatically re-threw the exception if one
- * was reported by the run() method.
- */
- public V getWithThrows() throws CoreException {
- V retVal = get();
- if (!getStatus().isOK()) {
- throw new CoreException(getStatus());
- }
- return retVal;
- }
+ public boolean isCancelled() { return fSync.doIsCancelled(); }
- public IStatus getStatus() { return fStatus; }
+ public boolean isDone() { return fSync.doIsDone(); }
- /** Abort current operation and keep old proxy data */
- public synchronized void cancel() {
- assert fExecutor.isInExecutorThread();
- assert !fWaiting || !fValid;
- if (fWaiting) {
- fFuture.cancel(false);
- fWaiting = false;
- notifyAll();
- } else if (fValid) {
- revokeChildren(fResult);
- }
- fValid = true;
+
+ protected void done(V result) {
+ fSync.doSet(result);
}
- /** Abort current operation and set proxy data to 'result' */
- public synchronized void cancel(V newResult) {
- fResult = newResult;
- cancel();
- }
-
- public Object getCachedResult() {
- return fResult;
- }
-
- public boolean isValid() { return fValid; }
-
- public synchronized void done(V result) {
- // Valid could be true if request was cancelled while data was
- // being retrieved, and then done() was called.
- if (fValid) return;
-
- fResult = result;
- fValid = true;
- if (fWaiting) {
- fWaiting = false;
- notifyAll();
- }
- }
-
- public synchronized void doneError(IStatus errorStatus) {
- if (fValid) return;
- fStatus = errorStatus;
- done(null);
- }
-
- public synchronized void doneException(Throwable t) {
- if (fValid) return;
- doneError(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR,
- "Exception while computing result.", t));
+ protected void doneException(Throwable t) {
+ fSync.doSetException(t);
}
+
+ abstract protected void execute();
+
+ public void run() {
+ if (fSync.doRun()) {
+ execute();
+ }
+ }
+
+ @SuppressWarnings("serial")
+ final class Sync extends AbstractQueuedSynchronizer {
+ private static final int STATE_RUNNING = 1;
+ private static final int STATE_DONE = 2;
+ private static final int STATE_CANCELLED = 4;
+
+ private V fResult;
+ private Throwable fException;
+
+ private boolean ranOrCancelled(int state) {
+ return (state & (STATE_DONE | STATE_CANCELLED)) != 0;
+ }
+
+ protected int tryAcquireShared(int ignore) {
+ return doIsDone()? 1 : -1;
+ }
+
+ protected boolean tryReleaseShared(int ignore) {
+ return true;
+ }
+
+ boolean doIsCancelled() {
+ return getState() == STATE_CANCELLED;
+ }
+
+ boolean doIsDone() {
+ return ranOrCancelled(getState());
+ }
+
+ V doGet() throws InterruptedException, ExecutionException {
+ acquireSharedInterruptibly(0);
+ if (getState() == STATE_CANCELLED) throw new CancellationException();
+ if (fException != null) throw new ExecutionException(fException);
+ return fResult;
+ }
+
+ V doGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
+ if (!tryAcquireSharedNanos(0, nanosTimeout)) throw new TimeoutException();
+ if (getState() == STATE_CANCELLED) throw new CancellationException();
+ if (fException != null) throw new ExecutionException(fException);
+ return fResult;
+ }
+
+ void doSet(V v) {
+ while(true) {
+ int s = getState();
+ if (ranOrCancelled(s)) return;
+ if (compareAndSetState(s, STATE_DONE)) break;
+ }
+ fResult = v;
+ releaseShared(0);
+ }
+
+ void doSetException(Throwable t) {
+ while(true) {
+ int s = getState();
+ if (ranOrCancelled(s)) return;
+ if (compareAndSetState(s, STATE_DONE)) break;
+ }
+ fException = t;
+ fResult = null;
+ releaseShared(0);
+ }
+
+ boolean doCancel() {
+ while(true) {
+ int s = getState();
+ if (ranOrCancelled(s)) return false;
+ if (compareAndSetState(s, STATE_CANCELLED)) break;
+ }
+ releaseShared(0);
+ return true;
+ }
+
+ boolean doRun() {
+ return compareAndSetState(0, STATE_RUNNING);
+ }
+ }
}
diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfSequence.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfSequence.java
index 3de19cd88ae..6fe0903cd82 100644
--- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfSequence.java
+++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfSequence.java
@@ -10,8 +10,17 @@
*******************************************************************************/
package org.eclipse.dd.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 java.util.concurrent.locks.AbstractQueuedSynchronizer;
+
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.DsfPlugin;
@@ -39,202 +48,406 @@ import org.eclipse.dd.dsf.DsfPlugin;
* has to be re-implemented every time. The Sequence class tries to address
* this problem by containing this pattern in a single class.
*/
-abstract public class DsfSequence {
+@ThreadSafe
+abstract public class DsfSequence extends DsfRunnable implements Future