1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-03 14:25:37 +02:00

Refactored DsfSequence class (bug# 159048).

Also added tracing of executables that were garbage-disposed without ever being run.
This commit is contained in:
Pawel Piech 2006-10-13 22:34:59 +00:00
parent 6ed5cc8d0e
commit 3f86dad9d5
8 changed files with 619 additions and 300 deletions

View file

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

View file

@ -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 {

View file

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

View file

@ -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;
}

View file

@ -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.
* <p>
@ -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());
}
}
}

View file

@ -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<V> {
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<V> extends DsfRunnable
implements Future<V>
{
/** 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);
}
}
}

View file

@ -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<Object> {
/**
* The abstract class that each step has to implement
* The abstract class that each step has to implement.
*/
abstract public class Step {
public void execute() { stepFinished(); }
public void rollBack() { stepRolledBack(); }
abstract public static class Step {
private DsfSequence fSequence;
/**
* Sets the sequence that this step belongs to. It is only accessible
* by the sequence itself, and is not meant to be called by sequence
* sub-classes.
*/
void setSequence(DsfSequence sequence) { fSequence = sequence; }
/** Returns the sequence that this step is running in. */
public DsfSequence getSequence() { return fSequence; }
/**
* Executes the next step. Overriding classes should perform the
* work in this method.
* @param done Result token to submit to executor when step is finished.
*/
public void execute(Done done) {
getSequence().getExecutor().execute(done);
}
/**
* Roll back gives the step implementation a chance to undo the
* operation that was performed by execute().
* @param done Result token to submit to executor when rolling back the step is finished.
*/
public void rollBack(Done done) {
getSequence().getExecutor().execute(done);
}
/**
* Returns the number of progress monitor ticks corresponding to this
* step.
*/
public int getTicks() { return 1; }
}
private DsfExecutor fExecutor;
private Step[] fSteps;
private Done fDoneQC;
private String fTaskName;
private String fRollbackTaskName;
private IProgressMonitor fProgressMonitor = new NullProgressMonitor();
/** The synchronization object for this future */
final Sync fSync = new Sync();
/**
* Executor that this sequence is running in. It is used by the sequence
* to submit the runnables for steps, and for submitting the result.
*/
final private DsfExecutor fExecutor;
/**
* Result callback to invoke when the sequence is finished. Intended to
* be used when the sequence is created and invoked from the executor
* thread. Otherwise, the {@link Future#get()} method is the appropriate
* method of retrieving the result.
*/
final private Done fDone;
/** Status indicating the success/failure of the test. Used internally only. */
@ConfinedToDsfExecutor("getExecutor")
private IStatus fStatus = Status.OK_STATUS;
@ConfinedToDsfExecutor("getExecutor")
private int fCurrentStepIdx = 0;
boolean fCancelled = false;
/**
* Default constructor. If this constructor is used, the steps need to be initialized
* before the sequence can be invoked.
* @param executor the DSF executor which will be used to invoke all steps
*/
public DsfSequence(DsfExecutor executor) {
this(executor, null);
/** Task name for this sequence used with the progress monitor */
final private String fTaskName;
/** Task name used when the sequence is being rolled back. */
final private String fRollbackTaskName;
final private IProgressMonitor fProgressMonitor;
/** Convenience constructor with limited arguments. */
public DsfSequence(DsfExecutor executor) {
this(executor, new NullProgressMonitor(), "", "", null);
}
/** Convenience constructor with limited arguments. */
public DsfSequence(DsfExecutor executor, Done done) {
this(executor, new NullProgressMonitor(), "", "", done);
}
/**
* Constructor that initialized the steps.
* @param executor the DSF executor which will be used to invoke all steps
* @param steps sequence steps
* Constructor that initialized the steps and the result callback.
* @param executor The DSF executor which will be used to invoke all steps.
* @param pm Progress monitor for monitoring this sequence. This parameter cannot be null.
* @param taskName Name that will be used in call to {@link IProgressMonitor#beginTask(String, int)},
* when the task is started.
* @param rollbackTaskName Name that will be used in call to {@link IProgressMonitor#subTask(String)}
* if the task is cancelled or aborted.
* @param Result that will be submitted to executor when sequence is finished. Can be null if calling from
* non-executor thread and using {@link Future#get()} method to wait for the sequence result.
*/
public DsfSequence(DsfExecutor executor, Step[] steps) {
public DsfSequence(DsfExecutor executor, IProgressMonitor pm, String taskName, String rollbackTaskName, Done done) {
fExecutor = executor;
fSteps = steps;
fProgressMonitor = pm;
fTaskName = taskName;
fRollbackTaskName = rollbackTaskName;
fDone = done;
}
/**
* Returns the steps to be executed. It is up to the deriving class to
* supply the steps and to ensure that the list of steps will not be
* modified after the sequence is constructed.
*/
abstract public Step[] getSteps();
/** Returns the DSF executor for this sequence */
public DsfExecutor getExecutor() { return fExecutor; }
/**
* Sets the done callback to be submitted when the sequence is finished.
* If the sequence is submitted by a caller in the dispatch thread, this is
* the way that the original caller can be notified of the sequence
* completion. If the caller blocks and waits for the sequence
* completion, the Done callback is not necessary.
* @param doneQC callback to submit when sequence completes, can be null
*/
public void setDone(Done doneQC) {
fDoneQC = doneQC;
}
/**
* Returns the Done callback that is registered with the Sequence
* @param doneQC callback that will be submitted when sequence completes,
* null if there is no callback configured
*/
public Done getDone() { return fDoneQC; }
/** Sets the steps to be executed. */
public void setSteps(Step[] steps) {
assert fCurrentStepIdx == 0;
fSteps = steps;
}
/** Returns the steps to be executed. */
public Step[] getSteps() { return fSteps; }
public Done getDone() { return fDone; }
/**
* Returns index of the step that is currently being executed.
* <br>NOTE: After sequence is invoked, this method should be called
* only in the DSF executor thread.
* @return
* The get method blocks until sequence is complete, but always returns null.
* @see java.concurrent.Future#get
*/
public int getCurrentIdx() { return fCurrentStepIdx; }
/**
* Sets the progress monitor that will be called by the sequence with udpates.
* @param pm
*/
public void setProgressMonitor(IProgressMonitor pm) { fProgressMonitor = pm; }
/**
* Sets the task name for this sequence. To be used with progress monitor;
* @param taskName
*/
public void setTaskName(String taskName) { fTaskName = taskName; }
/**
* Sets the task name to be used with progress monitor, if this sequence needs
* to be rolled back as result of cancellation or error.
* @param taskName
*/
public void setRollBackTaskName(String n) { fRollbackTaskName = n; }
/** Submits this sequence to the executor. */
public void invokeLater() {
getExecutor().submit( new DsfRunnable() { public void run() { doInvoke(); } });
}
/**
* Submits this sequence to the DSF executor, and blocks waiting for the
* sequence to complete.
* <br>NOTE: This method is NOT to be called on the DSF executor thread.
*/
public synchronized void invoke() {
assert !fExecutor.isInExecutorThread() :
"Cannot be called on dispatch thread: " + this;
setDone(new Done() {
public void run() {
synchronized(DsfSequence.this) { DsfSequence.this.notifyAll(); }
}
});
invokeLater();
try {
wait();
} catch (InterruptedException e) {}
public Object get() throws InterruptedException, ExecutionException {
fSync.doGet();
return null;
}
private void doInvoke() {
assert fCurrentStepIdx == 0;
if (fTaskName != null) {
fProgressMonitor.subTask(fTaskName);
/**
* The get method blocks until sequence is complete or until timeout is
* reached, but always returns null.
* @see java.concurrent.Future#get
*/
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
fSync.doGet();
return null;
}
/**
* Don't try to interrupt the DSF executor thread, just ignore the request
* if set.
*/
public boolean cancel(boolean mayInterruptIfRunning) {
return fSync.doCancel();
}
public boolean isCancelled() { return fSync.doIsCancelled(); }
public boolean isDone() { return fSync.doIsDone(); }
public void run() {
// Change the state to running.
if (fSync.doRun()) {
// Set the reference to this sequence in each step.
int totalTicks = 0;
for (Step step : getSteps()) {
step.setSequence(this);
totalTicks += step.getTicks();
}
// Set the task name
if (fTaskName != null) {
fProgressMonitor.beginTask(fTaskName, totalTicks);
}
// Call the first step
executeStep(0);
} else {
fSync.doFinish();
}
fSteps[fCurrentStepIdx].execute();
}
/**
* Cancells the execution of this sequence. The roll-back will start when
* the current step completes.
*
*/
public void cancel() {
fCancelled = true;
}
/**
* To be called only by the step implementation, Tells the sequence to
* submit the next step.
*/
public void stepFinished() {
getExecutor().submit(new DsfRunnable() { public void run() {
fProgressMonitor.worked(getSteps()[fCurrentStepIdx].getTicks());
fCurrentStepIdx++;
if (fCurrentStepIdx < fSteps.length) {
if (fCancelled) {
abort(new Status(
IStatus.CANCEL, DsfPlugin.PLUGIN_ID, -1,
"Cancelled" + fTaskName != null ? ": " + fTaskName : "",
null));
}
fSteps[fCurrentStepIdx].execute();
private void executeStep(int nextStepIndex) {
if (isCancelled()) {
cancelExecution();
} else {
if (nextStepIndex < getSteps().length) {
fCurrentStepIdx = nextStepIndex;
getSteps()[fCurrentStepIdx].execute(new Done() {
final private int fStepIdx = fCurrentStepIdx;
public void run() {
// Check if we're still the correct step.
assert fStepIdx == fCurrentStepIdx;
// Proceed to the next step.
if (getStatus().isOK()) {
fProgressMonitor.worked(getSteps()[fStepIdx].getTicks());
executeStep(fStepIdx + 1);
} else {
abortExecution(getStatus());
}
}
public String toString() {
return "DsfSequence \"" + fTaskName + "\", result for executing step #" + fStepIdx + " = " + getStatus();
}
});
} else {
if (fDoneQC != null) getExecutor().submit(fDoneQC);
finish();
}
}});
}
}
/**
* To be called only by the step implementation. Tells the sequence to
* roll back next step.
*/
public void stepRolledBack() {
getExecutor().submit(new DsfRunnable() { public void run() {
fProgressMonitor.worked(getSteps()[fCurrentStepIdx].getTicks());
fCurrentStepIdx--;
if (fCurrentStepIdx >= 0) {
fSteps[fCurrentStepIdx].rollBack();
} else {
if (fDoneQC != null) getExecutor().submit(fDoneQC);
}
}});
private void rollBackStep(int stepIdx) {
if (stepIdx >= 0) {
fCurrentStepIdx = stepIdx;
getSteps()[fCurrentStepIdx].rollBack(new Done() {
final private int fStepIdx = fCurrentStepIdx;
public void run() {
// Check if we're still the correct step.
assert fStepIdx == fCurrentStepIdx;
// Proceed to the next step.
if (getStatus().isOK()) {
fProgressMonitor.worked(getSteps()[fStepIdx].getTicks());
rollBackStep(fStepIdx - 1);
} else {
abortRollBack(getStatus());
}
};
@Override
public String toString() {
return "DsfSequence \"" + fTaskName + "\", result for rolling back step #" + fStepIdx + " = " + getStatus();
}
});
} else {
finish();
}
}
/**
* To be called only by step implementation. Tells the sequence
* that its execution is to be aborted and it should start rolling back
* the sequence as if it was cancelled by user.
* @param error
* Tells the sequence that its execution is to be aborted and it
* should start rolling back the sequence as if it was cancelled by user.
*/
public void abort(final IStatus error) {
getExecutor().submit(new DsfRunnable() { public void run() {
if (fRollbackTaskName != null) {
fProgressMonitor.subTask(fRollbackTaskName);
}
fDoneQC.setStatus(error);
fCurrentStepIdx--;
if (fCurrentStepIdx >= 0) {
fSteps[fCurrentStepIdx].rollBack();
} else {
if (fDoneQC != null) getExecutor().submit(fDoneQC);
}
}});
private void cancelExecution() {
if (fRollbackTaskName != null) {
fProgressMonitor.subTask(fRollbackTaskName);
}
fStatus = new Status(IStatus.CANCEL, DsfPlugin.PLUGIN_ID, "Sequence \"" + fTaskName + "\" cancelled.");
if (fDone != null) {
fDone.setStatus(fStatus);
}
/*
* No need to call fSync, it should have been taken care of by
* Future#cancel method.
*
* Note that we're rolling back starting with the current step,
* because the current step was fully executed. This is unlike
* abortExecution() where the current step caused the roll-back.
*/
rollBackStep(fCurrentStepIdx);
}
/**
* Tells the sequence that its execution is to be aborted and it
* should start rolling back the sequence as if it was cancelled by user.
*/
private void abortExecution(final IStatus error) {
if (fRollbackTaskName != null) {
fProgressMonitor.subTask(fRollbackTaskName);
}
fStatus = error;
if (fDone != null) {
fDone.setStatus(error);
}
fSync.doAbort(new CoreException(error));
// Roll back starting with previous step, since current step failed.
rollBackStep(fCurrentStepIdx - 1);
}
/**
* Tells the sequence that that is rolling back, to abort roll back, and
* notify the clients.
*/
private void abortRollBack(final IStatus error) {
if (fRollbackTaskName != null) {
fProgressMonitor.subTask(fRollbackTaskName);
}
/*
* Compose new status based on previous status information and new
* error information.
*/
MultiStatus newStatus =
new MultiStatus(DsfPlugin.PLUGIN_ID, error.getCode(),
"Sequence \"" + fTaskName + "\" failed while rolling back.", null);
newStatus.merge(error);
newStatus.merge(fStatus);
fStatus = newStatus;
if (fDone != null) {
fDone.setStatus(newStatus);
}
finish();
}
private void finish() {
if (fDone != null) getExecutor().submit(fDone);
fSync.doFinish();
}
@SuppressWarnings("serial")
final class Sync extends AbstractQueuedSynchronizer {
private static final int STATE_RUNNING = 1;
private static final int STATE_FINISHED = 2;
private static final int STATE_ABORTING = 4;
private static final int STATE_ABORTED = 8;
private static final int STATE_CANCELLING = 16;
private static final int STATE_CANCELLED = 32;
private Throwable fException;
private boolean isFinished(int state) {
return (state & (STATE_FINISHED | STATE_CANCELLED | STATE_ABORTED)) != 0;
}
protected int tryAcquireShared(int ignore) {
return doIsDone()? 1 : -1;
}
protected boolean tryReleaseShared(int ignore) {
return true;
}
boolean doIsCancelled() {
int state = getState();
return (state & (STATE_CANCELLING | STATE_CANCELLED)) != 0;
}
boolean doIsDone() {
return isFinished(getState());
}
void doGet() throws InterruptedException, ExecutionException {
acquireSharedInterruptibly(0);
if (getState() == STATE_CANCELLED) throw new CancellationException();
if (fException != null) throw new ExecutionException(fException);
}
void 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);
}
void doAbort(Throwable t) {
while(true) {
int s = getState();
if (isFinished(s)) return;
if (compareAndSetState(s, STATE_ABORTING)) break;
}
fException = t;
}
boolean doCancel() {
while(true) {
int s = getState();
if (isFinished(s)) return false;
if (s == STATE_ABORTING) return false;
if (compareAndSetState(s, STATE_CANCELLING)) break;
}
return true;
}
void doFinish() {
while(true) {
int s = getState();
if (isFinished(s)) return;
if (s == STATE_ABORTING) {
if (compareAndSetState(s, STATE_ABORTED)) break;
} else if (s == STATE_CANCELLING) {
if (compareAndSetState(s, STATE_CANCELLED)) break;
} else {
if (compareAndSetState(s, STATE_FINISHED)) break;
}
}
releaseShared(0);
}
boolean doRun() {
return compareAndSetState(0, STATE_RUNNING);
}
}
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (c) 2006 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.dd.dsf.concurrent;
/**
* Untility class for easy pretty-printing stack traces. Local to the
* concurrent package.
*/
@Immutable
class StackTraceWrapper {
final StackTraceElement[] fStackTraceElements;
StackTraceWrapper(StackTraceElement[] elements) { fStackTraceElements = elements; }
public String toString() {
StringBuilder builder = new StringBuilder(fStackTraceElements.length * 30);
for (int i = 0; i < fStackTraceElements.length && i < 10; i++) {
builder.append(fStackTraceElements[i]);
if (i < fStackTraceElements.length && i < 10) builder.append("\n at ");
}
return builder.toString();
}
}