mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-06-07 17:56:01 +02:00
Bug 325943 - [concurrent] Robustify Sequence against RejectedExecutionException
This commit is contained in:
parent
b2dd37899a
commit
c72c78d35c
2 changed files with 103 additions and 24 deletions
|
@ -421,9 +421,18 @@ abstract public class Sequence extends DsfRunnable implements Future<Object> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleErrorOrWarning() {
|
protected void handleErrorOrWarning() {
|
||||||
abortExecution(getStatus());
|
abortExecution(getStatus(), true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleRejectedExecutionException() {
|
||||||
|
abortExecution(
|
||||||
|
new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0,
|
||||||
|
"Executor shut down while executing Sequence " + this + ", step #" + fCurrentStepIdx, //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
null),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Sequence \"" + fTaskName + "\", result for executing step #" + fStepIdx + " = " + getStatus(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
return "Sequence \"" + fTaskName + "\", result for executing step #" + fStepIdx + " = " + getStatus(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||||
|
@ -450,10 +459,11 @@ abstract public class Sequence extends DsfRunnable implements Future<Object> {
|
||||||
* when the execute submits other runnables, and the other runnables
|
* when the execute submits other runnables, and the other runnables
|
||||||
* encounter the exception.
|
* encounter the exception.
|
||||||
*/
|
*/
|
||||||
abortExecution(new Status(
|
abortExecution(
|
||||||
IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0,
|
new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0,
|
||||||
"Unhandled exception when executing Sequence " + this + ", step #" + fCurrentStepIdx, //$NON-NLS-1$ //$NON-NLS-2$
|
"Unhandled exception when executing Sequence " + this + ", step #" + fCurrentStepIdx, //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
t));
|
t),
|
||||||
|
true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we caught the exception, it will not be logged by
|
* Since we caught the exception, it will not be logged by
|
||||||
|
@ -548,8 +558,13 @@ abstract public class Sequence extends DsfRunnable implements Future<Object> {
|
||||||
/**
|
/**
|
||||||
* Tells the sequence that its execution is to be aborted and it
|
* 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.
|
* should start rolling back the sequence as if it was cancelled by user.
|
||||||
|
*
|
||||||
|
* @param status Status to use for reporting the error.
|
||||||
|
* @param rollBack Whether to start rolling back the sequence after abort.
|
||||||
|
* If this parameter is <code>false</code> then the sequence will also
|
||||||
|
* finish.
|
||||||
*/
|
*/
|
||||||
private void abortExecution(final IStatus error) {
|
private void abortExecution(final IStatus error, boolean rollBack) {
|
||||||
if (fRollbackTaskName != null) {
|
if (fRollbackTaskName != null) {
|
||||||
fProgressMonitor.subTask(fRollbackTaskName);
|
fProgressMonitor.subTask(fRollbackTaskName);
|
||||||
}
|
}
|
||||||
|
@ -559,8 +574,12 @@ abstract public class Sequence extends DsfRunnable implements Future<Object> {
|
||||||
}
|
}
|
||||||
fSync.doAbort(new CoreException(error));
|
fSync.doAbort(new CoreException(error));
|
||||||
|
|
||||||
|
if (rollBack) {
|
||||||
// Roll back starting with previous step, since current step failed.
|
// Roll back starting with previous step, since current step failed.
|
||||||
rollBackStep(fCurrentStepIdx - 1);
|
rollBackStep(fCurrentStepIdx - 1);
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,9 +43,13 @@ public class DsfSequenceTests {
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void shutdownExecutor() throws ExecutionException, InterruptedException {
|
public void shutdownExecutor() throws ExecutionException, InterruptedException {
|
||||||
|
if (!fExecutor.isShutdown()) {
|
||||||
|
// Some tests shut down the executor deliberatly)
|
||||||
|
|
||||||
fExecutor.submit(new DsfRunnable() { public void run() {
|
fExecutor.submit(new DsfRunnable() { public void run() {
|
||||||
fExecutor.shutdown();
|
fExecutor.shutdown();
|
||||||
}}).get();
|
}}).get();
|
||||||
|
}
|
||||||
if (fExecutor.exceptionsCaught()) {
|
if (fExecutor.exceptionsCaught()) {
|
||||||
Throwable[] exceptions = fExecutor.getExceptions();
|
Throwable[] exceptions = fExecutor.getExceptions();
|
||||||
throw new ExecutionException(exceptions[0]);
|
throw new ExecutionException(exceptions[0]);
|
||||||
|
@ -81,8 +85,8 @@ public class DsfSequenceTests {
|
||||||
Sequence sequence = new Sequence(fExecutor) {
|
Sequence sequence = new Sequence(fExecutor) {
|
||||||
@Override public Step[] getSteps() { return steps; }
|
@Override public Step[] getSteps() { return steps; }
|
||||||
};
|
};
|
||||||
Assert.assertTrue(!sequence.isDone());
|
Assert.assertFalse(sequence.isDone());
|
||||||
Assert.assertTrue(!sequence.isCancelled());
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
|
|
||||||
fExecutor.execute(sequence);
|
fExecutor.execute(sequence);
|
||||||
sequence.get();
|
sequence.get();
|
||||||
|
@ -92,7 +96,7 @@ public class DsfSequenceTests {
|
||||||
|
|
||||||
// Check post conditions
|
// Check post conditions
|
||||||
Assert.assertTrue(sequence.isDone());
|
Assert.assertTrue(sequence.isDone());
|
||||||
Assert.assertTrue(!sequence.isCancelled());
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,8 +133,8 @@ public class DsfSequenceTests {
|
||||||
SimpleReflectionSequence sequence = new SimpleReflectionSequence();
|
SimpleReflectionSequence sequence = new SimpleReflectionSequence();
|
||||||
|
|
||||||
//Sequence sequence = new SimpleReflectionSequence();
|
//Sequence sequence = new SimpleReflectionSequence();
|
||||||
Assert.assertTrue(!sequence.isDone());
|
Assert.assertFalse(sequence.isDone());
|
||||||
Assert.assertTrue(!sequence.isCancelled());
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
|
|
||||||
fExecutor.execute(sequence);
|
fExecutor.execute(sequence);
|
||||||
sequence.get();
|
sequence.get();
|
||||||
|
@ -140,7 +144,7 @@ public class DsfSequenceTests {
|
||||||
|
|
||||||
// Check post conditions
|
// Check post conditions
|
||||||
Assert.assertTrue(sequence.isDone());
|
Assert.assertTrue(sequence.isDone());
|
||||||
Assert.assertTrue(!sequence.isCancelled());
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -183,7 +187,7 @@ public class DsfSequenceTests {
|
||||||
};
|
};
|
||||||
fExecutor.execute(sequence);
|
fExecutor.execute(sequence);
|
||||||
|
|
||||||
// Block and wait for sequence to bomplete.
|
// Block and wait for sequence to complete.
|
||||||
try {
|
try {
|
||||||
sequence.get();
|
sequence.get();
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -194,7 +198,63 @@ public class DsfSequenceTests {
|
||||||
|
|
||||||
// Check state from Future interface
|
// Check state from Future interface
|
||||||
Assert.assertTrue(sequence.isDone());
|
Assert.assertTrue(sequence.isDone());
|
||||||
Assert.assertTrue(!sequence.isCancelled());
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
|
}
|
||||||
|
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = ExecutionException.class)
|
||||||
|
public void rejectedTest() throws InterruptedException, ExecutionException {
|
||||||
|
// Create a counter for tracking number of steps performed and steps
|
||||||
|
// rolled back.
|
||||||
|
class IntegerHolder { int fInteger; }
|
||||||
|
final IntegerHolder stepCounter = new IntegerHolder();
|
||||||
|
final IntegerHolder rollBackCounter = new IntegerHolder();
|
||||||
|
|
||||||
|
// Create the steps of the sequence
|
||||||
|
final Sequence.Step[] steps = new Sequence.Step[] {
|
||||||
|
new Sequence.Step() {
|
||||||
|
@Override public void execute(RequestMonitor requestMonitor) {
|
||||||
|
stepCounter.fInteger++;
|
||||||
|
requestMonitor.done();
|
||||||
|
}
|
||||||
|
@Override public void rollBack(RequestMonitor requestMonitor) {
|
||||||
|
rollBackCounter.fInteger++;
|
||||||
|
requestMonitor.done();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Sequence.Step() {
|
||||||
|
@Override public void execute(RequestMonitor requestMonitor) {
|
||||||
|
stepCounter.fInteger++;
|
||||||
|
// Shutdown exectutor to force a RejectedExecutionException
|
||||||
|
fExecutor.shutdown();
|
||||||
|
requestMonitor.done();
|
||||||
|
}
|
||||||
|
@Override public void rollBack(RequestMonitor requestMonitor) {
|
||||||
|
rollBackCounter.fInteger++;
|
||||||
|
requestMonitor.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create and start.
|
||||||
|
Sequence sequence = new Sequence(fExecutor) {
|
||||||
|
@Override public Step[] getSteps() { return steps; }
|
||||||
|
};
|
||||||
|
fExecutor.execute(sequence);
|
||||||
|
|
||||||
|
// Block and wait for sequence to complete.
|
||||||
|
try {
|
||||||
|
sequence.get();
|
||||||
|
} finally {
|
||||||
|
// Both steps should be performed
|
||||||
|
Assert.assertTrue(stepCounter.fInteger == 2);
|
||||||
|
// No steps should be rolled back.
|
||||||
|
Assert.assertTrue(rollBackCounter.fInteger == 0);
|
||||||
|
|
||||||
|
// Check state from Future interface
|
||||||
|
Assert.assertTrue(sequence.isDone());
|
||||||
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
}
|
}
|
||||||
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
@ -257,7 +317,7 @@ public class DsfSequenceTests {
|
||||||
|
|
||||||
// Check state from Future interface
|
// Check state from Future interface
|
||||||
Assert.assertTrue(sequence.isDone());
|
Assert.assertTrue(sequence.isDone());
|
||||||
Assert.assertTrue(!sequence.isCancelled());
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
}
|
}
|
||||||
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
@ -320,7 +380,7 @@ public class DsfSequenceTests {
|
||||||
|
|
||||||
// Check state from Future interface
|
// Check state from Future interface
|
||||||
Assert.assertTrue(sequence.isDone());
|
Assert.assertTrue(sequence.isDone());
|
||||||
Assert.assertTrue(!sequence.isCancelled());
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
}
|
}
|
||||||
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
@ -352,7 +412,7 @@ public class DsfSequenceTests {
|
||||||
} finally {
|
} finally {
|
||||||
// Check state from Future interface
|
// Check state from Future interface
|
||||||
Assert.assertTrue(sequence.isDone());
|
Assert.assertTrue(sequence.isDone());
|
||||||
Assert.assertTrue(!sequence.isCancelled());
|
Assert.assertFalse(sequence.isCancelled());
|
||||||
}
|
}
|
||||||
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
@ -375,7 +435,7 @@ public class DsfSequenceTests {
|
||||||
// Cancel before invoking the sequence.
|
// Cancel before invoking the sequence.
|
||||||
sequence.cancel(false);
|
sequence.cancel(false);
|
||||||
|
|
||||||
Assert.assertTrue(!sequence.isDone());
|
Assert.assertFalse(sequence.isDone());
|
||||||
Assert.assertTrue(sequence.isCancelled());
|
Assert.assertTrue(sequence.isCancelled());
|
||||||
|
|
||||||
// Start the sequence
|
// Start the sequence
|
||||||
|
|
Loading…
Add table
Reference in a new issue