From c72c78d35cbde865ba396befa1e816d7eb710a1d Mon Sep 17 00:00:00 2001
From: Pawel Piech <pawel.piech@windriver.com>
Date: Mon, 27 Sep 2010 23:27:18 +0000
Subject: [PATCH] Bug 325943 -  [concurrent] Robustify Sequence against
 RejectedExecutionException

---
 .../eclipse/cdt/dsf/concurrent/Sequence.java  | 37 ++++++--
 .../dsf/concurrent/DsfSequenceTests.java      | 90 +++++++++++++++----
 2 files changed, 103 insertions(+), 24 deletions(-)

diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java
index da0f4f1913e..79c868780a8 100644
--- a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java
+++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java
@@ -421,9 +421,18 @@ abstract public class Sequence extends DsfRunnable implements Future<Object> {
                 
                 @Override
                 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
                 public String toString() {
                     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
              * encounter the exception.
              */ 
-            abortExecution(new Status(
-                IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0, 
-                "Unhandled exception when executing Sequence " + this + ", step #" + fCurrentStepIdx,  //$NON-NLS-1$ //$NON-NLS-2$
-                t));
+            abortExecution(
+                new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0, 
+                           "Unhandled exception when executing Sequence " + this + ", step #" + fCurrentStepIdx,  //$NON-NLS-1$ //$NON-NLS-2$
+                           t), 
+                true);
             
             /*
              * 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 
      * 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) {
             fProgressMonitor.subTask(fRollbackTaskName);
         }
@@ -558,9 +573,13 @@ abstract public class Sequence extends DsfRunnable implements Future<Object> {
             fRequestMonitor.setStatus(error);
         }
         fSync.doAbort(new CoreException(error));
-        
-        // Roll back starting with previous step, since current step failed.
-        rollBackStep(fCurrentStepIdx - 1);
+
+        if (rollBack) {
+            // Roll back starting with previous step, since current step failed.
+            rollBackStep(fCurrentStepIdx - 1);
+        } else {
+            finish();
+        }
     }
 
     /**
diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java
index e9019429dbd..4e1c0b2f53b 100644
--- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java
+++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java
@@ -43,9 +43,13 @@ public class DsfSequenceTests {
     
     @After 
     public void shutdownExecutor() throws ExecutionException, InterruptedException {
-        fExecutor.submit(new DsfRunnable() { public void run() {
-            fExecutor.shutdown();
-        }}).get();
+        if (!fExecutor.isShutdown()) {
+            // Some tests shut down the executor deliberatly)
+            
+            fExecutor.submit(new DsfRunnable() { public void run() {
+                fExecutor.shutdown();
+            }}).get();
+        }
         if (fExecutor.exceptionsCaught()) {
             Throwable[] exceptions = fExecutor.getExceptions();
             throw new ExecutionException(exceptions[0]);
@@ -81,8 +85,8 @@ public class DsfSequenceTests {
         Sequence sequence = new Sequence(fExecutor) {
             @Override public Step[] getSteps() { return steps; }
         };
-        Assert.assertTrue(!sequence.isDone());
-        Assert.assertTrue(!sequence.isCancelled());
+        Assert.assertFalse(sequence.isDone());
+        Assert.assertFalse(sequence.isCancelled());
         
         fExecutor.execute(sequence);
         sequence.get();
@@ -92,7 +96,7 @@ public class DsfSequenceTests {
         
         // Check post conditions
         Assert.assertTrue(sequence.isDone());
-        Assert.assertTrue(!sequence.isCancelled());
+        Assert.assertFalse(sequence.isCancelled());
     }
 
 
@@ -129,8 +133,8 @@ public class DsfSequenceTests {
         SimpleReflectionSequence sequence = new SimpleReflectionSequence();
 
         //Sequence sequence = new SimpleReflectionSequence();
-        Assert.assertTrue(!sequence.isDone());
-        Assert.assertTrue(!sequence.isCancelled());
+        Assert.assertFalse(sequence.isDone());
+        Assert.assertFalse(sequence.isCancelled());
         
         fExecutor.execute(sequence);
         sequence.get();
@@ -140,7 +144,7 @@ public class DsfSequenceTests {
         
         // Check post conditions
         Assert.assertTrue(sequence.isDone());
-        Assert.assertTrue(!sequence.isCancelled());
+        Assert.assertFalse(sequence.isCancelled());
     }
 
     
@@ -183,7 +187,7 @@ public class DsfSequenceTests {
         };
         fExecutor.execute(sequence);
      
-        // Block and wait for sequence to bomplete.
+        // Block and wait for sequence to complete.
         try {
             sequence.get();
         } finally {
@@ -194,7 +198,63 @@ public class DsfSequenceTests {
             
             // Check state from Future interface
             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$
     }
@@ -257,7 +317,7 @@ public class DsfSequenceTests {
             
             // Check state from Future interface
             Assert.assertTrue(sequence.isDone());
-            Assert.assertTrue(!sequence.isCancelled());            
+            Assert.assertFalse(sequence.isCancelled());            
         }
         Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
     }
@@ -320,7 +380,7 @@ public class DsfSequenceTests {
             
             // Check state from Future interface
             Assert.assertTrue(sequence.isDone());
-            Assert.assertTrue(!sequence.isCancelled());                        
+            Assert.assertFalse(sequence.isCancelled());                        
         }
         Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
     }
@@ -352,7 +412,7 @@ public class DsfSequenceTests {
         } finally {
             // Check state from Future interface
             Assert.assertTrue(sequence.isDone());
-            Assert.assertTrue(!sequence.isCancelled());            
+            Assert.assertFalse(sequence.isCancelled());            
         }
         Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$
     }
@@ -375,7 +435,7 @@ public class DsfSequenceTests {
         // Cancel before invoking the sequence.
         sequence.cancel(false);
 
-        Assert.assertTrue(!sequence.isDone());
+        Assert.assertFalse(sequence.isDone());
         Assert.assertTrue(sequence.isCancelled());
 
         // Start the sequence