You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2015/09/22 08:01:12 UTC

[3/4] maven-surefire git commit: [SUREFIRE-524] Forked Process not terminated if maven process aborted. Provide means to clean up.

[SUREFIRE-524] Forked Process not terminated if maven process aborted. Provide means to clean up.


Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/cb97ba70
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/cb97ba70
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/cb97ba70

Branch: refs/heads/master
Commit: cb97ba70cb9ebe685f8f2a06e87b538795b5dd9b
Parents: c8ddd6b
Author: Tibor17 <ti...@lycos.com>
Authored: Tue Sep 22 07:52:49 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Tue Sep 22 07:52:49 2015 +0200

----------------------------------------------------------------------
 .../plugin/failsafe/IntegrationTestMojo.java    |  18 +++
 .../plugin/surefire/AbstractSurefireMojo.java   |  15 ++-
 .../surefire/SurefireExecutionParameters.java   |   2 +
 .../surefire/booterclient/BooterSerializer.java |   1 +
 .../surefire/booterclient/ForkStarter.java      |  34 +++---
 .../lazytestprovider/NotifiableTestStream.java  |   4 +-
 .../lazytestprovider/TestLessInputStream.java   |  15 +--
 .../TestProvidingInputStream.java               |  13 ++-
 ...erDeserializerProviderConfigurationTest.java |   2 +-
 ...terDeserializerStartupConfigurationTest.java |   2 +-
 .../TestLessInputStreamBuilderTest.java         |  13 ++-
 .../maven/plugin/surefire/SurefirePlugin.java   |  18 +++
 .../src/site/apt/examples/shutdown.apt.vm       |  78 +++++++++++++
 maven-surefire-plugin/src/site/apt/index.apt.vm |   2 +
 maven-surefire-plugin/src/site/site.xml         |   1 +
 .../surefire/booter/BaseProviderFactory.java    |  14 ++-
 .../apache/maven/surefire/booter/Command.java   |  33 +++++-
 .../surefire/booter/MasterProcessCommand.java   |  12 +-
 .../surefire/booter/MasterProcessReader.java    | 116 +++++++++++++++----
 .../apache/maven/surefire/booter/Shutdown.java  |  84 ++++++++++++++
 .../maven/surefire/booter/ShutdownAware.java    |  31 +++++
 .../surefire/booter/SurefireReflector.java      |  24 +++-
 .../providerapi/ProviderParameters.java         |   3 +
 .../surefire/util/internal/StringUtils.java     |  26 +++++
 .../booter/MasterProcessCommandTest.java        |   8 +-
 .../maven/surefire/booter/BooterConstants.java  |   1 +
 .../surefire/booter/BooterDeserializer.java     |   4 +-
 .../maven/surefire/booter/ForkedBooter.java     |  30 ++---
 .../surefire/booter/ProviderConfiguration.java  |  14 ++-
 .../maven/surefire/booter/ProviderFactory.java  |   1 +
 .../maven/surefire/junit4/JUnit4Provider.java   |   4 +-
 .../surefire/junitcore/JUnitCoreProvider.java   |   4 +-
 .../maven/surefire/testng/TestNGProvider.java   |   4 +-
 33 files changed, 539 insertions(+), 92 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
----------------------------------------------------------------------
diff --git a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
index b359048..5e0c7ea 100644
--- a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
+++ b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
@@ -315,6 +315,19 @@ public class IntegrationTestMojo
     @Parameter( property = "failsafe.skipAfterFailureCount", defaultValue = "0" )
     private int skipAfterFailureCount;
 
+    /**
+     * After the plugin process is shutdown by sending SIGTERM signal (CTRL+C), SHUTDOWN command is received by every
+     * forked JVM. By default (shutdown=testset) forked JVM would not continue with new test which means that
+     * the current test may still continue to run.<br/>
+     * The parameter can be configured with other two values "exit" and "kill".<br/>
+     * Using "exit" forked JVM executes System.exit(1) after the plugin process has received SIGTERM signal.<br/>
+     * Using "kill" the JVM executes Runtime.halt(1) and kills itself.
+     *
+     * @since 2.19
+     */
+    @Parameter( property = "failsafe.shutdown", defaultValue = "testset" )
+    private String shutdown;
+
     protected int getRerunFailingTestsCount()
     {
         return rerunFailingTestsCount;
@@ -613,6 +626,11 @@ public class IntegrationTestMojo
         return skipAfterFailureCount;
     }
 
+    public String getShutdown()
+    {
+        return shutdown;
+    }
+
     @Override
     public List<String> getIncludes()
     {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index ff2e51e..d67e29f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -76,6 +76,7 @@ import org.apache.maven.surefire.booter.ClasspathConfiguration;
 import org.apache.maven.surefire.booter.KeyValueSource;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
 import org.apache.maven.surefire.booter.ProviderParameterNames;
+import org.apache.maven.surefire.booter.Shutdown;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.apache.maven.surefire.booter.SurefireExecutionException;
@@ -827,6 +828,7 @@ public abstract class AbstractSurefireMojo
             warnIfUselessUseSystemClassLoaderParameter();
             warnIfDefunctGroupsCombinations();
             warnIfRerunClashes();
+            warnIfWrongShutdownValue();
         }
         return true;
     }
@@ -1467,7 +1469,8 @@ public abstract class AbstractSurefireMojo
                                           reporterConfiguration,
                                           testNg, // Not really used in provider. Limited to de/serializer.
                                           testSuiteDefinition, providerProperties, null,
-                                          false, cli, getSkipAfterFailureCount() );
+                                          false, cli, getSkipAfterFailureCount(),
+                                          Shutdown.parameterOf( getShutdown() ) );
     }
 
     private static Map<String, String> toStringProperties( Properties properties )
@@ -1973,6 +1976,7 @@ public abstract class AbstractSurefireMojo
         checksum.add( getTestSourceDirectory() );
         checksum.add( getTest() );
         checksum.add( getIncludes() );
+        checksum.add( getShutdown() );
         checksum.add( getExcludes() );
         checksum.add( getLocalRepository() );
         checksum.add( getSystemProperties() );
@@ -2365,6 +2369,15 @@ public abstract class AbstractSurefireMojo
         }
     }
 
+    private void warnIfWrongShutdownValue()
+        throws MojoFailureException
+    {
+        if ( !Shutdown.isKnown( getShutdown() ) )
+        {
+            throw new MojoFailureException( "Parameter \"shutdown\" should have values " + Shutdown.listParameters() );
+        }
+    }
+
     final class TestNgProviderInfo
         implements ProviderInfo
     {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
index 6bd09ff..247f5e8 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
@@ -120,4 +120,6 @@ public interface SurefireExecutionParameters
     void setFailIfNoSpecifiedTests( boolean failIfNoSpecifiedTests );
 
     int getSkipAfterFailureCount();
+
+    String getShutdown();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
index 139e14b..21807f5 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
@@ -134,6 +134,7 @@ class BooterSerializer
         properties.setProperty( FAILIFNOTESTS, String.valueOf( booterConfiguration.isFailIfNoTests() ) );
         properties.setProperty( PROVIDER_CONFIGURATION, providerConfiguration.getProviderClassName() );
         properties.setProperty( FAIL_FAST_COUNT, String.valueOf( booterConfiguration.getSkipAfterFailureCount() ) );
+        properties.setProperty( SHUTDOWN, booterConfiguration.getShutdown().name() );
         List<CommandLineOption> mainCliOptions = booterConfiguration.getMainCliOptions();
         if ( mainCliOptions != null )
         {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 39680b1..4742b7c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -39,6 +39,7 @@ import org.apache.maven.surefire.booter.KeyValueSource;
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
 import org.apache.maven.surefire.booter.ProviderFactory;
+import org.apache.maven.surefire.booter.Shutdown;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.apache.maven.surefire.booter.SurefireExecutionException;
@@ -109,9 +110,11 @@ public class ForkStarter
 {
     private static final long PING_IN_SECONDS = 10;
 
-    private static final ThreadFactory DAEMON_THREAD_FACTORY = newDaemonThreadFactory();
+    private static final ThreadFactory FORKED_JVM_DAEMON_THREAD_FACTORY
+        = newDaemonThreadFactory( "surefire-fork-starter" );
 
-    private static final ThreadFactory SHUTDOWN_HOOK_THREAD_FACTORY = newDaemonThreadFactory();
+    private static final ThreadFactory SHUTDOWN_HOOK_THREAD_FACTORY
+        = newDaemonThreadFactory( "surefire-jvm-killer-shutdownhook" );
 
     private final ScheduledExecutorService pingThreadScheduler = createPingScheduler();
 
@@ -207,7 +210,7 @@ public class ForkStarter
             new ForkClient( forkedReporterFactory, startupReportConfiguration.getTestVmSystemProperties() );
         TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
         TestLessInputStream stream = builder.build();
-        Thread shutdown = createImmediateShutdownHookThread( builder );
+        Thread shutdown = createImmediateShutdownHookThread( builder, providerConfiguration.getShutdown() );
         ScheduledFuture<?> ping = triggerPingTimerForShutdown( builder );
         try
         {
@@ -248,7 +251,7 @@ public class ForkStarter
     {
         ThreadPoolExecutor executorService = new ThreadPoolExecutor( forkCount, forkCount, 60, TimeUnit.SECONDS,
                                                                   new ArrayBlockingQueue<Runnable>( forkCount ) );
-        executorService.setThreadFactory( DAEMON_THREAD_FACTORY );
+        executorService.setThreadFactory( FORKED_JVM_DAEMON_THREAD_FACTORY );
 
         final Queue<String> tests = new ConcurrentLinkedQueue<String>();
 
@@ -265,7 +268,7 @@ public class ForkStarter
         }
 
         ScheduledFuture<?> ping = triggerPingTimerForShutdown( testStreams );
-        Thread shutdown = createShutdownHookThread( testStreams );
+        Thread shutdown = createShutdownHookThread( testStreams, providerConfiguration.getShutdown() );
 
         try
         {
@@ -328,10 +331,10 @@ public class ForkStarter
         ArrayList<Future<RunResult>> results = new ArrayList<Future<RunResult>>( 500 );
         ThreadPoolExecutor executorService =
             new ThreadPoolExecutor( forkCount, forkCount, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() );
-        executorService.setThreadFactory( DAEMON_THREAD_FACTORY );
+        executorService.setThreadFactory( FORKED_JVM_DAEMON_THREAD_FACTORY );
         final TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
         ScheduledFuture<?> ping = triggerPingTimerForShutdown( builder );
-        Thread shutdown = createCachableShutdownHookThread( builder );
+        Thread shutdown = createCachableShutdownHookThread( builder, providerConfiguration.getShutdown() );
         try
         {
             addShutDownHook( shutdown );
@@ -602,29 +605,32 @@ public class ForkStarter
         }
     }
 
-    private static Thread createImmediateShutdownHookThread( final TestLessInputStreamBuilder builder )
+    private static Thread createImmediateShutdownHookThread( final TestLessInputStreamBuilder builder,
+                                                             final Shutdown shutdownType )
     {
         return SHUTDOWN_HOOK_THREAD_FACTORY.newThread( new Runnable()
         {
             public void run()
             {
-                builder.getImmediateCommands().shutdown();
+                builder.getImmediateCommands().shutdown( shutdownType );
             }
         } );
     }
 
-    private static Thread createCachableShutdownHookThread( final TestLessInputStreamBuilder builder )
+    private static Thread createCachableShutdownHookThread( final TestLessInputStreamBuilder builder,
+                                                            final Shutdown shutdownType )
     {
         return SHUTDOWN_HOOK_THREAD_FACTORY.newThread( new Runnable()
         {
             public void run()
             {
-                builder.getCachableCommands().shutdown();
+                builder.getCachableCommands().shutdown( shutdownType );
             }
         } );
     }
 
-    private static Thread createShutdownHookThread( final Iterable<TestProvidingInputStream> streams )
+    private static Thread createShutdownHookThread( final Iterable<TestProvidingInputStream> streams,
+                                                    final Shutdown shutdownType )
     {
         return SHUTDOWN_HOOK_THREAD_FACTORY.newThread( new Runnable()
         {
@@ -632,7 +638,7 @@ public class ForkStarter
             {
                 for ( TestProvidingInputStream stream : streams )
                 {
-                    stream.shutdown();
+                    stream.shutdown( shutdownType );
                 }
             }
         } );
@@ -640,7 +646,7 @@ public class ForkStarter
 
     private static ScheduledExecutorService createPingScheduler()
     {
-        ThreadFactory threadFactory = newDaemonThreadFactory( "ping-thread-" + PING_IN_SECONDS + "sec" );
+        ThreadFactory threadFactory = newDaemonThreadFactory( "ping-timer-" + PING_IN_SECONDS + "sec" );
         return Executors.newScheduledThreadPool( 1, threadFactory );
     }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
index d47645b..5c89173 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
@@ -19,6 +19,8 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  * under the License.
  */
 
+import org.apache.maven.surefire.booter.Shutdown;
+
 /**
  * Forked jvm notifies master process to provide a new test.
  *
@@ -40,7 +42,7 @@ public interface NotifiableTestStream
      */
     void skipSinceNextTest();
 
-    void shutdown();
+    void shutdown( Shutdown shutdownType );
 
     void noop();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
index b15af89..04d39b3 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
@@ -20,6 +20,7 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  */
 
 import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.Shutdown;
 
 import java.io.IOException;
 import java.util.Iterator;
@@ -33,8 +34,8 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import static org.apache.maven.surefire.booter.Command.NOOP;
-import static org.apache.maven.surefire.booter.Command.SHUTDOWN;
 import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.Command.toShutdown;
 
 /**
  * Dispatches commands without tests.
@@ -73,11 +74,11 @@ public final class TestLessInputStream
         }
     }
 
-    public void shutdown()
+    public void shutdown( Shutdown shutdownType )
     {
         if ( canContinue() )
         {
-            immediateCommands.add( SHUTDOWN );
+            immediateCommands.add( toShutdown( shutdownType ) );
             barrier.release();
         }
     }
@@ -336,7 +337,7 @@ public final class TestLessInputStream
                 }
             }
 
-            public void shutdown()
+            public void shutdown( Shutdown shutdownType )
             {
                 Lock lock = rwLock.readLock();
                 lock.lock();
@@ -344,7 +345,7 @@ public final class TestLessInputStream
                 {
                     for ( TestLessInputStream aliveStream : TestLessInputStreamBuilder.this.aliveStreams )
                     {
-                        aliveStream.shutdown();
+                        aliveStream.shutdown( shutdownType );
                     }
                 }
                 finally
@@ -398,13 +399,13 @@ public final class TestLessInputStream
                 }
             }
 
-            public void shutdown()
+            public void shutdown( Shutdown shutdownType )
             {
                 Lock lock = rwLock.readLock();
                 lock.lock();
                 try
                 {
-                    if ( TestLessInputStreamBuilder.this.addTailNodeIfAbsent( SHUTDOWN ) )
+                    if ( TestLessInputStreamBuilder.this.addTailNodeIfAbsent( toShutdown( shutdownType ) ) )
                     {
                         release();
                     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
index dcc2997..766843d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
@@ -20,6 +20,7 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  */
 
 import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.Shutdown;
 
 import java.io.IOException;
 import java.util.Queue;
@@ -27,11 +28,11 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
-import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
-import static org.apache.maven.surefire.booter.Command.SHUTDOWN;
 import static org.apache.maven.surefire.booter.Command.NOOP;
+import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.Command.toRunClass;
+import static org.apache.maven.surefire.booter.Command.toShutdown;
 
 /**
  * An {@link java.io.InputStream} that, when read, provides test class names out of a queue.
@@ -90,11 +91,11 @@ public final class TestProvidingInputStream
         }
     }
 
-    public void shutdown()
+    public void shutdown( Shutdown shutdownType )
     {
         if ( canContinue() )
         {
-            commands.add( SHUTDOWN );
+            commands.add( toShutdown( shutdownType ) );
             barrier.release();
         }
     }
@@ -115,7 +116,7 @@ public final class TestProvidingInputStream
         if ( cmd == null )
         {
             String cmdData = testClassNames.poll();
-            return cmdData == null ? Command.TEST_SET_FINISHED : new Command( RUN_CLASS, cmdData );
+            return cmdData == null ? Command.TEST_SET_FINISHED : toRunClass( cmdData );
         }
         else
         {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
index 9c8cc07..623edf3 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
@@ -237,7 +237,7 @@ public class BooterDeserializerProviderConfigurationTest
         RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null );
         return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration,
                 new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new HashMap<String, String>(), aTestTyped,
-                readTestsFromInStream, cli, 0 );
+                readTestsFromInStream, cli, 0, Shutdown.DEFAULT );
     }
 
     private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
index 6f232fa..007640d 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
@@ -144,7 +144,7 @@ public class BooterDeserializerStartupConfigurationTest
         RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null );
         return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration,
                 new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new HashMap<String, String>(),
-                BooterDeserializerProviderConfigurationTest.aTestTyped, true, cli, 0 );
+                BooterDeserializerProviderConfigurationTest.aTestTyped, true, cli, 0, Shutdown.DEFAULT );
     }
 
     private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
index c1a075f..6dee0a4 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
@@ -30,8 +30,9 @@ import java.util.NoSuchElementException;
 
 import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
 import static org.apache.maven.surefire.booter.Command.NOOP;
-import static org.apache.maven.surefire.booter.Command.SHUTDOWN;
 import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
+import static org.apache.maven.surefire.booter.Shutdown.EXIT;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -103,24 +104,24 @@ public class TestLessInputStreamBuilderTest
         assertThat( is.availablePermits(), is( 1 ) );
         builder.getCachableCommands().skipSinceNextTest();
         assertThat( is.availablePermits(), is( 1 ) );
-        builder.getImmediateCommands().shutdown();
+        builder.getImmediateCommands().shutdown( EXIT );
         assertThat( is.availablePermits(), is( 2 ) );
         is.beforeNextCommand();
-        assertThat( is.nextCommand(), is( SHUTDOWN ) );
+        assertThat( is.nextCommand().getCommandType(), is( SHUTDOWN ) );
         assertThat( is.availablePermits(), is( 1 ) );
         is.beforeNextCommand();
         assertThat( is.nextCommand(), is( SKIP_SINCE_NEXT_TEST ) );
         assertThat( is.availablePermits(), is( 0 ) );
         builder.getImmediateCommands().noop();
         assertThat( is.availablePermits(), is( 1 ) );
-        builder.getCachableCommands().shutdown();
-        builder.getCachableCommands().shutdown();
+        builder.getCachableCommands().shutdown( EXIT );
+        builder.getCachableCommands().shutdown( EXIT );
         assertThat( is.availablePermits(), is( 2 ) );
         is.beforeNextCommand();
         assertThat( is.nextCommand(), is( NOOP ) );
         assertThat( is.availablePermits(), is( 1 ) );
         is.beforeNextCommand();
-        assertThat( is.nextCommand(), is( SHUTDOWN ) );
+        assertThat( is.nextCommand().getCommandType(), is( SHUTDOWN ) );
         assertThat( is.availablePermits(), is( 0 ) );
         e.expect( NoSuchElementException.class );
         is.nextCommand();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index b26b493..88bb107 100644
--- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
+++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
@@ -292,6 +292,19 @@ public class SurefirePlugin
     @Parameter( property = "surefire.skipAfterFailureCount", defaultValue = "0" )
     private int skipAfterFailureCount;
 
+    /**
+     * After the plugin process is shutdown by sending SIGTERM signal (CTRL+C), SHUTDOWN command is received by every
+     * forked JVM. By default (shutdown=testset) forked JVM would not continue with new test which means that
+     * the current test may still continue to run.<br/>
+     * The parameter can be configured with other two values "exit" and "kill".<br/>
+     * Using "exit" forked JVM executes System.exit(1) after the plugin process has received SIGTERM signal.<br/>
+     * Using "kill" the JVM executes Runtime.halt(1) and kills itself.
+     *
+     * @since 2.19
+     */
+    @Parameter( property = "surefire.shutdown", defaultValue = "testset" )
+    private String shutdown;
+
     protected int getRerunFailingTestsCount()
     {
         return rerunFailingTestsCount;
@@ -466,6 +479,11 @@ public class SurefirePlugin
         return skipAfterFailureCount;
     }
 
+    public String getShutdown()
+    {
+        return shutdown;
+    }
+
     public boolean isPrintSummary()
     {
         return printSummary;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm b/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm
new file mode 100644
index 0000000..f50710b
--- /dev/null
+++ b/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm
@@ -0,0 +1,78 @@
+ ------
+ Forked JVM Shutdown
+ ------
+ Tibor Digana <ti...@apache.org>
+ ------
+ September 2015
+ ------
+ 
+ ~~ Licensed to the Apache Software Foundation (ASF) under one
+ ~~ or more contributor license agreements.  See the NOTICE file
+ ~~ distributed with this work for additional information
+ ~~ regarding copyright ownership.  The ASF licenses this file
+ ~~ to you under the Apache License, Version 2.0 (the
+ ~~ "License"); you may not use this file except in compliance
+ ~~ with the License.  You may obtain a copy of the License at
+ ~~
+ ~~   http://www.apache.org/licenses/LICENSE-2.0
+ ~~
+ ~~ Unless required by applicable law or agreed to in writing,
+ ~~ software distributed under the License is distributed on an
+ ~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~~ KIND, either express or implied.  See the License for the
+ ~~ specific language governing permissions and limitations
+ ~~ under the License.
+
+ ~~ NOTE: For help with the syntax of this file, see:
+ ~~ http://maven.apache.org/doxia/references/apt-format.html 
+
+Shutdown of Forked JVM
+
+* Embedded shutdown
+  
+  After the test-set has completed, the process executes
+  <<<java.lang.System.exit(0)>>> which starts shutdown hooks. At this point
+  the process may run next 30 seconds until all non daemon Threads die.
+  After the period of time has elapsed, the process kills itself by
+  <<<java.lang.Runtime.halt(0)>>>.
+
+* Pinging forked JVM
+
+  The master process sends NOOP command to a forked JVM every 10 seconds.
+  Forked JVM is waiting for the command every 20 seconds.
+  If the master process is killed (received SIGKILL signal) or shutdown 
+  (pressed CTRL+C, received SIGTERM signal), forked JVM terminates after
+  timing out waiting period.
+
+* Shutdown of forked JVM by stopping the build
+
+  After the master process of the build is shutdown by sending SIGTERM signal
+  or pressing CTRL+C, the master process sends SHUTDOWN command to every
+  forked JVM. By default (configuration parameter <<<shutdown=testset>>>)
+  forked JVM would not continue with new test which means that the current
+  test may still continue to run.
+  The parameter <<<shutdown>>> can be configured with other two values
+  <<<exit>>> and <<<kill>>>. Using <<<exit>>> forked JVM executes
+  <<<java.lang.System.exit(1)>>> after the master process has received SIGTERM.
+  Using <<<kill>>> the JVM executes <<<java.lang.Runtime.halt(1)>>>, example:
+
++---+
+<project>
+  [...]
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>${project.artifactId}</artifactId>
+        <version>${project.version}</version>
+        <configuration>
+          <shutdown>kill</shutdown>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  [...]
+</project>
++---+
+  
+  
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-plugin/src/site/apt/index.apt.vm
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/apt/index.apt.vm b/maven-surefire-plugin/src/site/apt/index.apt.vm
index c9f8c9e..09ea2c0 100644
--- a/maven-surefire-plugin/src/site/apt/index.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/index.apt.vm
@@ -153,4 +153,6 @@ mvn verify
 
   * {{{./examples/fork-options-and-parallel-execution.html}Fork Options and Parallel Test Execution}}
 
+  * {{{./examples/shutdown.html}Shutdown of Forked JVM}}
+
   []

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/maven-surefire-plugin/src/site/site.xml
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/site.xml b/maven-surefire-plugin/src/site/site.xml
index 833c0a9..b3e9cd2 100644
--- a/maven-surefire-plugin/src/site/site.xml
+++ b/maven-surefire-plugin/src/site/site.xml
@@ -54,6 +54,7 @@
       <item name="New Error Summary" href="newerrorsummary.html"/>
       <item name="Fork Options and Parallel Test Execution" href="examples/fork-options-and-parallel-execution.html"/>
       <item name="Using Console Logs" href="examples/logging.html"/>
+      <item name="Shutdown of Forked JVM" href="examples/shutdown.html"/>
     </menu>
   </body>
 </project>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
index 0433a01..b33b5d0 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
@@ -46,7 +46,7 @@ import java.util.Map;
 public class BaseProviderFactory
     implements DirectoryScannerParametersAware, ReporterConfigurationAware, SurefireClassLoadersAware, TestRequestAware,
     ProviderPropertiesAware, ProviderParameters, TestArtifactInfoAware, RunOrderParametersAware, MainCliOptionsAware,
-    FailFastAware
+    FailFastAware, ShutdownAware
 {
     private static final int ROOT_CHANNEL = 0;
 
@@ -72,6 +72,8 @@ public class BaseProviderFactory
 
     private int skipAfterFailureCount;
 
+    private Shutdown shutdown;
+
     public BaseProviderFactory( ReporterFactory reporterFactory, boolean insideFork )
     {
         this.reporterFactory = reporterFactory;
@@ -206,4 +208,14 @@ public class BaseProviderFactory
     {
         return insideFork;
     }
+
+    public Shutdown getShutdown()
+    {
+        return shutdown;
+    }
+
+    public void setShutdown( Shutdown shutdown )
+    {
+        this.shutdown = shutdown;
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
index 25bb2d1..070980c 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
@@ -20,6 +20,10 @@ package org.apache.maven.surefire.booter;
  */
 
 import static org.apache.maven.surefire.util.internal.StringUtils.requireNonNull;
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
+import static org.apache.maven.surefire.booter.Shutdown.DEFAULT;
 
 /**
  * Encapsulates data and command sent from master to forked process.
@@ -31,7 +35,6 @@ public final class Command
 {
     public static final Command TEST_SET_FINISHED = new Command( MasterProcessCommand.TEST_SET_FINISHED );
     public static final Command SKIP_SINCE_NEXT_TEST = new Command( MasterProcessCommand.SKIP_SINCE_NEXT_TEST );
-    public static final Command SHUTDOWN = new Command( MasterProcessCommand.SHUTDOWN );
     public static final Command NOOP = new Command( MasterProcessCommand.NOOP );
 
     private final MasterProcessCommand command;
@@ -43,6 +46,16 @@ public final class Command
         this.data = data;
     }
 
+    public static Command toShutdown( Shutdown shutdownType )
+    {
+        return new Command( SHUTDOWN, shutdownType.name() );
+    }
+
+    public static Command toRunClass( String runClass )
+    {
+        return new Command( RUN_CLASS, runClass );
+    }
+
     public Command( MasterProcessCommand command )
     {
         this( command, null );
@@ -58,6 +71,24 @@ public final class Command
         return data;
     }
 
+    /**
+     * @return {@link Shutdown} or {@link Shutdown#DEFAULT} if {@link #getData()} is null or blank string
+     * @throws IllegalArgumentException if string data {@link #getData()} is not applicable to enum {@link Shutdown}
+     */
+    public Shutdown toShutdownData()
+    {
+        if ( !isType( SHUTDOWN ) )
+        {
+            throw new IllegalStateException( "expected MasterProcessCommand.SHUTDOWN" );
+        }
+        return isBlank( data ) ? DEFAULT : Shutdown.valueOf( data );
+    }
+
+    public boolean isType( MasterProcessCommand command )
+    {
+        return command == this.command;
+    }
+
     @Override
     public boolean equals( Object o )
     {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
index 2129bf0..8059537 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
@@ -19,10 +19,13 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import org.apache.maven.surefire.util.internal.StringUtils;
+
 import java.io.DataInputStream;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
 
 import static org.apache.maven.surefire.util.internal.StringUtils.FORK_STREAM_CHARSET_NAME;
 import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
@@ -41,10 +44,13 @@ public enum MasterProcessCommand
     RUN_CLASS( 0, String.class ),
     TEST_SET_FINISHED( 1, Void.class ),
     SKIP_SINCE_NEXT_TEST( 2, Void.class ),
-    SHUTDOWN( 3, Void.class ),
+    SHUTDOWN( 3, Shutdown.class ),
+
     /** To tell a forked process that the master process is still alive. Repeated after 10 seconds. */
     NOOP( 4, Void.class );
 
+    private static final Charset ASCII = Charset.forName( "ASCII" );
+
     private final int id;
 
     private final Class<?> dataType;
@@ -157,6 +163,8 @@ public enum MasterProcessCommand
             {
                 case RUN_CLASS:
                     return new String( data, FORK_STREAM_CHARSET_NAME );
+                case SHUTDOWN:
+                    return StringUtils.decode( data, ASCII );
                 default:
                     return null;
             }
@@ -173,6 +181,8 @@ public enum MasterProcessCommand
         {
             case RUN_CLASS:
                 return encodeStringForForkCommunication( data );
+            case SHUTDOWN:
+                return StringUtils.encode( data, ASCII );
             default:
                 return new byte[0];
         }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
index ab45d81..f2f783c 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
@@ -37,14 +37,13 @@ import static java.lang.Thread.State.RUNNABLE;
 import static java.lang.Thread.State.TERMINATED;
 import static java.util.concurrent.locks.LockSupport.park;
 import static java.util.concurrent.locks.LockSupport.unpark;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.decode;
 import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
 import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
 import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
 import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThread;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.Shutdown.DEFAULT;
 
 /**
  * Reader of commands coming from plugin(master) process.
@@ -65,11 +64,13 @@ public final class MasterProcessReader
 
     private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
 
-    private final Node head = new Node();
-
     private final CountDownLatch startMonitor = new CountDownLatch( 1 );
 
-    private volatile Node tail = head;
+    private final Node headTestClassQueue = new Node();
+
+    private volatile Node tailTestClassQueue = headTestClassQueue;
+
+    private volatile Shutdown shutdown;
 
     private static class Node
     {
@@ -87,6 +88,12 @@ public final class MasterProcessReader
         return reader;
     }
 
+    public MasterProcessReader setShutdown( Shutdown shutdown )
+    {
+        this.shutdown = shutdown;
+        return this;
+    }
+
     public boolean awaitStarted()
         throws TestSetFailedException
     {
@@ -136,7 +143,7 @@ public final class MasterProcessReader
 
     Iterable<String> getIterableClasses( PrintStream originalOutStream )
     {
-        return new ClassesIterable( head, originalOutStream );
+        return new ClassesIterable( headTestClassQueue, originalOutStream );
     }
 
     public void stop()
@@ -149,6 +156,11 @@ public final class MasterProcessReader
         }
     }
 
+    private boolean isStopped()
+    {
+        return state.get() == TERMINATED;
+    }
+
     private static boolean isLastNode( Node current )
     {
         return current.successor.get() == current;
@@ -156,17 +168,20 @@ public final class MasterProcessReader
 
     private boolean isQueueFull()
     {
-        return isLastNode( tail );
+        return isLastNode( tailTestClassQueue );
     }
 
-    private boolean addToQueue( String item )
+    /**
+     * thread-safety: Must be called from single thread like here the reader thread only.
+     */
+    private boolean addTestClassToQueue( String item )
     {
-        if ( tail.item == null )
+        if ( tailTestClassQueue.item == null )
         {
-            tail.item = item;
+            tailTestClassQueue.item = item;
             Node newNode = new Node();
-            tail.successor.set( newNode );
-            tail = newNode;
+            tailTestClassQueue.successor.set( newNode );
+            tailTestClassQueue = newNode;
             return true;
         }
         else
@@ -183,18 +198,21 @@ public final class MasterProcessReader
     public void makeQueueFull()
     {
         // order between (#compareAndSet, and #get) matters in multithreading
-        for ( Node tail = this.tail;
+        for ( Node tail = this.tailTestClassQueue;
               !tail.successor.compareAndSet( null, tail ) && tail.successor.get() != tail;
               tail = tail.successor.get() );
     }
 
+    /**
+     * thread-safety: Must be called from single thread like here the reader thread only.
+     */
     private void insertForQueue( Command cmd )
     {
         MasterProcessCommand expectedCommandType = cmd.getCommandType();
         switch ( expectedCommandType )
         {
             case RUN_CLASS:
-                addToQueue( cmd.getData() );
+                addTestClassToQueue( cmd.getData() );
                 break;
             case TEST_SET_FINISHED:
                 makeQueueFull();
@@ -220,6 +238,9 @@ public final class MasterProcessReader
         }
     }
 
+    /**
+     * thread-safety: Must be called from single thread like here the reader thread only.
+     */
     private void insert( Command cmd )
     {
         insertForQueue( cmd );
@@ -292,7 +313,7 @@ public final class MasterProcessReader
 
         private void popUnread()
         {
-            if ( state.get() == TERMINATED )
+            if ( isStopped() )
             {
                 clazz = null;
                 return;
@@ -315,7 +336,13 @@ public final class MasterProcessReader
                             /**
                              * {@link java.util.concurrent.locks.LockSupport#park()}
                              * may spuriously (that is, for no reason) return, therefore the loop here.
+                             * Could be waken up by System.exit or closing the stream.
                              */
+                            if ( isStopped() )
+                            {
+                                clazz = null;
+                                return;
+                            }
                         } while ( current.item == null && !isLastNode( current ) );
                         clazz = current.item;
                         current = current.successor.get();
@@ -329,7 +356,7 @@ public final class MasterProcessReader
                 while ( tryNullWhiteClass() );
             }
 
-            if ( state.get() == TERMINATED )
+            if ( isStopped() )
             {
                 clazz = null;
             }
@@ -355,6 +382,9 @@ public final class MasterProcessReader
         }
     }
 
+    /**
+     * thread-safety: Must be called from single thread like here the reader thread only.
+     */
     private Command read( DataInputStream stdIn )
         throws IOException
     {
@@ -408,15 +438,24 @@ public final class MasterProcessReader
                     }
                     else
                     {
-                        if ( command.getCommandType() == TEST_SET_FINISHED )
-                        {
-                            isTestSetFinished = true;
-                            wakeupWaiters();
-                        }
-                        else if ( command.getCommandType() == RUN_CLASS )
+                        switch ( command.getCommandType() )
                         {
-                            wakeupWaiters();
+                            case TEST_SET_FINISHED:
+                                isTestSetFinished = true;
+                                wakeupWaiters();
+                                break;
+                            case RUN_CLASS:
+                                wakeupWaiters();
+                                break;
+                            case SHUTDOWN:
+                                insertForQueue( Command.TEST_SET_FINISHED );
+                                wakeupWaiters();
+                                break;
+                            default:
+                                // checkstyle do nothing
+                                break;
                         }
+
                         insertForListeners( command );
                     }
                 }
@@ -424,10 +463,16 @@ public final class MasterProcessReader
             catch ( EOFException e )
             {
                 MasterProcessReader.this.state.set( TERMINATED );
+                if ( !isTestSetFinished )
+                {
+                    exitByConfiguration();
+                    // does not go to finally
+                }
             }
             catch ( IOException e )
             {
                 MasterProcessReader.this.state.set( TERMINATED );
+                // If #stop() method is called, reader thread is interrupted and cause is InterruptedException.
                 if ( !( e.getCause() instanceof InterruptedException ) )
                 {
                     System.err.println( "[SUREFIRE] std/in stream corrupted" );
@@ -439,10 +484,33 @@ public final class MasterProcessReader
                 // ensure fail-safe iterator as well as safe to finish in for-each loop using ClassesIterator
                 if ( !isTestSetFinished )
                 {
-                    insert( new Command( TEST_SET_FINISHED ) );
+                    insert( Command.TEST_SET_FINISHED );
                 }
                 wakeupWaiters();
             }
         }
+
+        /**
+         * thread-safety: Must be called from single thread like here the reader thread only.
+         */
+        private void exitByConfiguration()
+        {
+            Shutdown shutdown = MasterProcessReader.this.shutdown; // won't read inconsistent changes through the stack
+            if ( shutdown != null && shutdown != DEFAULT )
+            {
+                insert( Command.TEST_SET_FINISHED ); // lazily
+                wakeupWaiters();
+                switch ( shutdown )
+                {
+                    case EXIT:
+                        System.exit( 1 );
+                    case KILL:
+                        Runtime.getRuntime().halt( 1 );
+                    default:
+                        // should not happen; otherwise you missed enum case
+                        break;
+                }
+            }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java
new file mode 100644
index 0000000..660a40e
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java
@@ -0,0 +1,84 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Specifies the way how the forked jvm should be terminated after
+ * class AbstractCommandStream is closed and CTRL+C.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public enum Shutdown
+{
+    DEFAULT( "testset" ),
+    EXIT( "exit" ),
+    KILL( "kill" );
+
+    private final String param;
+
+    Shutdown( String param )
+    {
+        this.param = param;
+    }
+
+    public String getParam()
+    {
+        return param;
+    }
+
+    public static boolean isKnown( String param )
+    {
+        for ( Shutdown shutdown : values() )
+        {
+            if ( shutdown.param.equals( param ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static String listParameters()
+    {
+        String values = "";
+        for ( Shutdown shutdown : values() )
+        {
+            if ( values.length() != 0 )
+            {
+                values += ", ";
+            }
+            values += shutdown.getParam();
+        }
+        return values;
+    }
+
+    public static Shutdown parameterOf( String parameter )
+    {
+        for ( Shutdown shutdown : values() )
+        {
+            if ( shutdown.param.equals( parameter ) )
+            {
+                return shutdown;
+            }
+        }
+        return DEFAULT;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/booter/ShutdownAware.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ShutdownAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ShutdownAware.java
new file mode 100644
index 0000000..a843b27
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ShutdownAware.java
@@ -0,0 +1,31 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * See the plugin configuration parameter <em>shutdown</em>.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public interface ShutdownAware
+{
+    void setShutdown( Shutdown shutdown );
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
index cf07331..96bc3c0 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
@@ -90,6 +90,10 @@ public class SurefireReflector
 
     private final Class<Enum> commandLineOptionsClass;
 
+    private final Class<?> shutdownAwareClass;
+
+    private final Class<Enum> shutdownClass;
+
 
     @SuppressWarnings( "unchecked" )
     public SurefireReflector( ClassLoader surefireClassLoader )
@@ -114,8 +118,9 @@ public class SurefireReflector
             booterParameters = surefireClassLoader.loadClass( ProviderParameters.class.getName() );
             testListResolver = surefireClassLoader.loadClass( TestListResolver.class.getName() );
             mainCliOptions = surefireClassLoader.loadClass( MainCliOptionsAware.class.getName() );
-            commandLineOptionsClass =
-                (Class<Enum>) surefireClassLoader.loadClass( CommandLineOption.class.getName() );
+            commandLineOptionsClass = (Class<Enum>) surefireClassLoader.loadClass( CommandLineOption.class.getName() );
+            shutdownAwareClass = surefireClassLoader.loadClass( ShutdownAware.class.getName() );
+            shutdownClass = (Class<Enum>) surefireClassLoader.loadClass( Shutdown.class.getName() );
         }
         catch ( ClassNotFoundException e )
         {
@@ -299,6 +304,21 @@ public class SurefireReflector
         ReflectionUtils.invokeSetter( o, "setSkipAfterFailureCount", int.class, skipAfterFailureCount );
     }
 
+    public void setShutdown( Object o, Shutdown shutdown )
+    {
+        if ( shutdownAwareClass.isAssignableFrom( o.getClass() ) )
+        {
+            for ( Enum e : shutdownClass.getEnumConstants() )
+            {
+                if ( shutdown.ordinal() == e.ordinal() )
+                {
+                    ReflectionUtils.invokeSetter( o, "setShutdown", shutdownClass, e );
+                    break;
+                }
+            }
+        }
+    }
+
     public void setDirectoryScannerParameters( Object o, DirectoryScannerParameters dirScannerParams )
     {
         final Object param = createDirectoryScannerParameters( dirScannerParams );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
index 708b436..b6e6467 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
@@ -19,6 +19,7 @@ package org.apache.maven.surefire.providerapi;
  * under the License.
  */
 
+import org.apache.maven.surefire.booter.Shutdown;
 import org.apache.maven.surefire.cli.CommandLineOption;
 import org.apache.maven.surefire.report.ConsoleLogger;
 import org.apache.maven.surefire.report.ReporterConfiguration;
@@ -143,4 +144,6 @@ public interface ProviderParameters
      * in-plugin provider.
      */
     boolean isInsideFork();
+
+    Shutdown getShutdown();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
index 043b87b..06cd55e 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/StringUtils.java
@@ -339,6 +339,32 @@ public class StringUtils
         return ByteBuffer.wrap( out, 0, outPos );
     }
 
+    public static String decode( byte[] toDecode, Charset charset )
+    {
+        try
+        {
+            // @todo use new JDK 1.6 constructor String(byte bytes[], Charset charset)
+            return new String( toDecode, charset.name() );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new RuntimeException( "The JVM must support Charset " + charset, e );
+        }
+    }
+
+    public static byte[] encode( String toEncode, Charset charset )
+    {
+        try
+        {
+            // @todo use new JDK 1.6 method getBytes(Charset charset)
+            return toEncode.getBytes( charset.name() );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new RuntimeException( "The JVM must support Charset " + charset, e );
+        }
+    }
+
     public static byte[] encodeStringForForkCommunication( String string )
     {
         try

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
index 717ab4e..f7f7767 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
@@ -105,11 +105,11 @@ public class MasterProcessCommandTest
                     assertNull( decoded );
                     break;
                 case SHUTDOWN:
-                    assertEquals( Void.class, command.getDataType() );
-                    encoded = command.fromDataType( dummyData );
-                    assertThat( encoded.length, is( 0 ) );
+                    assertEquals( Shutdown.class, command.getDataType() );
+                    encoded = command.fromDataType( Shutdown.EXIT.name() );
+                    assertThat( encoded.length, is( 4 ) );
                     decoded = command.toDataTypeAsString( encoded );
-                    assertNull( decoded );
+                    assertThat( decoded, is( Shutdown.EXIT.name() ) );
                     break;
                 case NOOP:
                     assertEquals( Void.class, command.getDataType() );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
index 749f0e9..d310b9a 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
@@ -54,4 +54,5 @@ public final class BooterConstants
     public static final String RERUN_FAILING_TESTS_COUNT = "rerunFailingTestsCount";
     public static final String MAIN_CLI_OPTIONS = "mainCliOptions";
     public static final String FAIL_FAST_COUNT = "failFastCount";
+    public static final String SHUTDOWN = "shutdown";
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
index b8f2d0b..2540a7f 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
@@ -102,10 +102,12 @@ public class BooterDeserializer
 
         int failFastCount = properties.getIntProperty( FAIL_FAST_COUNT );
 
+        Shutdown shutdown = Shutdown.valueOf( properties.getProperty( SHUTDOWN ) );
+
         return new ProviderConfiguration( dirScannerParams, runOrderParameters,
                                           properties.getBooleanProperty( FAILIFNOTESTS ), reporterConfiguration, testNg,
                                           testSuiteDefinition, properties.getProperties(), typeEncodedTestForFork,
-                                          preferTestsFromInStream, fromStrings( cli ), failFastCount );
+                                          preferTestsFromInStream, fromStrings( cli ), failFastCount, shutdown );
     }
 
     public StartupConfiguration getProviderConfiguration()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
index c7746e9..bf58ca9 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
@@ -40,6 +40,8 @@ import org.apache.maven.surefire.testset.TestSetFailedException;
 
 import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
+import static org.apache.maven.surefire.booter.Shutdown.DEFAULT;
+import static org.apache.maven.surefire.booter.Shutdown.KILL;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_BYE;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_ERROR;
 import static org.apache.maven.surefire.booter.ForkingRunListener.encode;
@@ -138,7 +140,7 @@ public final class ForkedBooter
             encodeAndWriteToOutput( ( (char) BOOTERCODE_BYE ) + ",0,BYE!\n", originalOut );
             originalOut.flush();
             // noinspection CallToSystemExit
-            exit( 0, false );
+            exit( 0, DEFAULT );
         }
         catch ( Throwable t )
         {
@@ -146,7 +148,7 @@ public final class ForkedBooter
             // noinspection UseOfSystemOutOrSystemErr
             t.printStackTrace( System.err );
             // noinspection ProhibitedExceptionThrown,CallToSystemExit
-            exit( 1, false );
+            exit( 1, DEFAULT );
         }
         finally
         {
@@ -180,7 +182,7 @@ public final class ForkedBooter
         {
             public void update( Command command )
             {
-                exit( 1, true );
+                exit( 1, command.toShutdownData() );
             }
         };
     }
@@ -194,7 +196,7 @@ public final class ForkedBooter
                 boolean hasPing = pingDone.getAndSet( false );
                 if ( !hasPing )
                 {
-                    exit( 1, true );
+                    exit( 1, KILL );
                 }
             }
         };
@@ -206,18 +208,19 @@ public final class ForkedBooter
         out.write( encodeBytes, 0, encodeBytes.length );
     }
 
-    private static void exit( int returnCode, boolean immediate )
+    private static void exit( int returnCode, Shutdown shutdownType )
     {
         MasterProcessReader.getReader().stop();
-
-        if ( immediate )
-        {
-            Runtime.getRuntime().halt( returnCode );
-        }
-        else
+        switch ( shutdownType )
         {
-            launchLastDitchDaemonShutdownThread( returnCode );
-            System.exit( returnCode );
+            case KILL:
+                Runtime.getRuntime().halt( returnCode );
+            case EXIT:
+                launchLastDitchDaemonShutdownThread( returnCode );
+                System.exit( returnCode );
+            case DEFAULT:
+            default:
+                break;
         }
     }
 
@@ -303,6 +306,7 @@ public final class ForkedBooter
         bpf.setDirectoryScannerParameters( providerConfiguration.getDirScannerParams() );
         bpf.setMainCliOptions( providerConfiguration.getMainCliOptions() );
         bpf.setSkipAfterFailureCount( providerConfiguration.getSkipAfterFailureCount() );
+        bpf.setShutdown( providerConfiguration.getShutdown() );
         String providerClass = startupConfiguration1.getActualClassName();
         return (SurefireProvider) instantiateOneArg( classLoader, providerClass, ProviderParameters.class, bpf );
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
index 69c7f2c..f4a3b42 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
@@ -68,13 +68,16 @@ public class ProviderConfiguration
 
     private int skipAfterFailureCount;
 
+    private Shutdown shutdown;
+
     @SuppressWarnings( "checkstyle:parameternumber" )
     public ProviderConfiguration( DirectoryScannerParameters directoryScannerParameters,
                                   RunOrderParameters runOrderParameters, boolean failIfNoTests,
                                   ReporterConfiguration reporterConfiguration, TestArtifactInfo testArtifact,
                                   TestRequest testSuiteDefinition, Map<String, String> providerProperties,
                                   TypeEncodedValue typeEncodedTestSet, boolean readTestsFromInStream,
-                                  List<CommandLineOption> mainCliOptions, int skipAfterFailureCount )
+                                  List<CommandLineOption> mainCliOptions, int skipAfterFailureCount,
+                                  Shutdown shutdown )
     {
         this.runOrderParameters = runOrderParameters;
         this.providerProperties = providerProperties;
@@ -87,6 +90,7 @@ public class ProviderConfiguration
         this.readTestsFromInStream = readTestsFromInStream;
         this.mainCliOptions = mainCliOptions;
         this.skipAfterFailureCount = skipAfterFailureCount;
+        this.shutdown = shutdown;
     }
 
     public ReporterConfiguration getReporterConfiguration()
@@ -156,13 +160,13 @@ public class ProviderConfiguration
         return mainCliOptions;
     }
 
-    public void setSkipAfterFailureCount( int skipAfterFailureCount )
+    public int getSkipAfterFailureCount()
     {
-        this.skipAfterFailureCount = skipAfterFailureCount;
+        return skipAfterFailureCount;
     }
 
-    public int getSkipAfterFailureCount()
+    public Shutdown getShutdown()
     {
-        return skipAfterFailureCount;
+        return shutdown;
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
index 5ad587f..85f9c45 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
@@ -100,6 +100,7 @@ public class ProviderFactory
         surefireReflector.setIfDirScannerAware( o, providerConfiguration.getDirScannerParams() );
         surefireReflector.setMainCliOptions( o, providerConfiguration.getMainCliOptions() );
         surefireReflector.setSkipAfterFailureCount( o, providerConfiguration.getSkipAfterFailureCount() );
+        surefireReflector.setShutdown( o, providerConfiguration.getShutdown() );
 
         Object provider = surefireReflector.instantiateProvider( startupConfiguration.getActualClassName(), o );
         currentThread.setContextClassLoader( systemClassLoader );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
index 3321a82..045b194 100644
--- a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
+++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
@@ -101,7 +101,9 @@ public class JUnit4Provider
     public JUnit4Provider( ProviderParameters booterParameters )
     {
         // don't start a thread in MasterProcessReader while we are in in-plugin process
-        commandsReader = booterParameters.isInsideFork() ? MasterProcessReader.getReader() : null;
+        commandsReader = booterParameters.isInsideFork()
+            ? MasterProcessReader.getReader().setShutdown( booterParameters.getShutdown() )
+            : null;
         providerParameters = booterParameters;
         testClassLoader = booterParameters.getTestClassLoader();
         scanResult = booterParameters.getScanResult();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
index 2c7d5c9..799c211 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
@@ -89,7 +89,9 @@ public class JUnitCoreProvider
 
     public JUnitCoreProvider( ProviderParameters providerParameters )
     {
-        commandsReader = providerParameters.isInsideFork() ? MasterProcessReader.getReader() : null;
+        commandsReader = providerParameters.isInsideFork()
+            ? MasterProcessReader.getReader().setShutdown( providerParameters.getShutdown() )
+            : null;
         this.providerParameters = providerParameters;
         testClassLoader = providerParameters.getTestClassLoader();
         scanResult = providerParameters.getScanResult();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/cb97ba70/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
index a317042..c2a1018 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
@@ -73,7 +73,9 @@ public class TestNGProvider
     public TestNGProvider( ProviderParameters booterParameters )
     {
         // don't start a thread in MasterProcessReader while we are in in-plugin process
-        commandsReader = booterParameters.isInsideFork() ? MasterProcessReader.getReader() : null;
+        commandsReader = booterParameters.isInsideFork()
+            ? MasterProcessReader.getReader().setShutdown( booterParameters.getShutdown() )
+            : null;
         providerParameters = booterParameters;
         testClassLoader = booterParameters.getTestClassLoader();
         runOrderCalculator = booterParameters.getRunOrderCalculator();