1
0
Fork 0
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:
Pawel Piech 2010-09-27 23:27:18 +00:00
parent b2dd37899a
commit c72c78d35c
2 changed files with 103 additions and 24 deletions

View file

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

View file

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