Repository: maven-surefire
Updated Branches:
  refs/heads/SUREFIRE-1342 9b489a1be -> 40bf72f10 (forced update)


[SUREFIRE-1342] Acknowledge normal exit of JVM and drain shared memory between 
processes


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

Branch: refs/heads/SUREFIRE-1342
Commit: 40bf72f10cc4078832cee2f7b4c2bbff923164f1
Parents: d468680
Author: Tibor17 <tibo...@lycos.com>
Authored: Mon Mar 13 03:10:08 2017 +0100
Committer: Tibor17 <tibo...@lycos.com>
Committed: Sun Mar 19 04:09:22 2017 +0100

----------------------------------------------------------------------
 .../surefire/booterclient/ForkStarter.java      |   1 +
 .../lazytestprovider/AbstractCommandStream.java |   8 +-
 .../lazytestprovider/NotifiableTestStream.java  |   2 +
 .../lazytestprovider/TestLessInputStream.java   |  49 +++++++-
 .../TestProvidingInputStream.java               |  18 +--
 .../booterclient/output/ForkClient.java         |   1 +
 .../output/ThreadedStreamConsumer.java          |  40 +++---
 .../booterclient/MockNotifiableTestStream.java  |   5 +
 .../TestProvidingInputStreamTest.java           |   1 +
 .../apache/maven/surefire/booter/Command.java   |   1 +
 .../maven/surefire/booter/CommandReader.java    |  22 ++--
 .../surefire/booter/MasterProcessCommand.java   |   3 +-
 .../apache/maven/surefire/booter/Shutdown.java  |  15 +++
 .../booter/MasterProcessCommandTest.java        |   7 ++
 .../maven/surefire/booter/ForkedBooter.java     | 124 +++++++++++++------
 .../jiras/Surefire141PluggableProvidersIT.java  |  33 +++--
 16 files changed, 237 insertions(+), 93 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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 54d304a..b69d010 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
@@ -644,6 +644,7 @@ public class ForkStarter
         }
         finally
         {
+            currentForkClients.remove( forkClient );
             closer.close();
             if ( runResult == null )
             {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
index 4d6331c..3531ccf 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
@@ -41,10 +41,12 @@ public abstract class AbstractCommandStream
     protected abstract boolean isClosed();
 
     /**
-     * Unnecessarily opposite to {@link #isClosed()} however may respect
-     * {@link #getLastCommand() last command} and {@link #isClosed()}.
+     * Opposite to {@link #isClosed()}.
      */
-    protected abstract boolean canContinue();
+    protected boolean canContinue()
+    {
+        return !isClosed();
+    }
 
     /**
      * Possibly waiting for next command (see {@link #nextCommand()}) unless 
the stream is atomically

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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 5c89173..b181de1 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
@@ -45,4 +45,6 @@ public interface NotifiableTestStream
     void shutdown( Shutdown shutdownType );
 
     void noop();
+
+    void acknowledgeByeEventReceived();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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 b6ae42c..d0ae47a 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
@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import static org.apache.maven.surefire.booter.Command.BYE_ACK;
 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.toShutdown;
@@ -93,15 +94,19 @@ public final class TestLessInputStream
     }
 
     @Override
-    protected boolean isClosed()
+    public void acknowledgeByeEventReceived()
     {
-        return closed.get();
+        if ( canContinue() )
+        {
+            immediateCommands.add( BYE_ACK );
+            barrier.release();
+        }
     }
 
     @Override
-    protected boolean canContinue()
+    protected boolean isClosed()
     {
-        return !isClosed();
+        return closed.get();
     }
 
     @Override
@@ -356,6 +361,24 @@ public final class TestLessInputStream
                     lock.unlock();
                 }
             }
+
+            @Override
+            public void acknowledgeByeEventReceived()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    for ( TestLessInputStream aliveStream : 
TestLessInputStreamBuilder.this.aliveStreams )
+                    {
+                        aliveStream.acknowledgeByeEventReceived();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
         }
 
         /**
@@ -419,6 +442,24 @@ public final class TestLessInputStream
                 }
             }
 
+            @Override
+            public void acknowledgeByeEventReceived()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    if ( TestLessInputStreamBuilder.this.addTailNodeIfAbsent( 
BYE_ACK ) )
+                    {
+                        release();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+
             private void release()
             {
                 for ( TestLessInputStream aliveStream : 
TestLessInputStreamBuilder.this.aliveStreams )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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 766843d..69f73a2 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
@@ -28,7 +28,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static 
org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
+import static org.apache.maven.surefire.booter.Command.BYE_ACK;
 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;
@@ -110,6 +110,16 @@ public final class TestProvidingInputStream
     }
 
     @Override
+    public void acknowledgeByeEventReceived()
+    {
+        if ( canContinue() )
+        {
+            commands.add( BYE_ACK );
+            barrier.release();
+        }
+    }
+
+    @Override
     protected Command nextCommand()
     {
         Command cmd = commands.poll();
@@ -137,12 +147,6 @@ public final class TestProvidingInputStream
         return closed.get();
     }
 
-    @Override
-    protected boolean canContinue()
-    {
-        return getLastCommand() != TEST_SET_FINISHED && !isClosed();
-    }
-
     /**
      * Signal that a new test is to be provided.
      */

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
index d6afcb2..faace0f 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
@@ -265,6 +265,7 @@ public class ForkClient
                     break;
                 case BOOTERCODE_BYE:
                     saidGoodBye = true;
+                    notifiableTestStream.acknowledgeByeEventReceived();
                     break;
                 case BOOTERCODE_STOP_ON_NEXT_TEST:
                     stopOnNextTest();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
index c7d39ae..ff9c238 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
@@ -26,6 +26,7 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static java.lang.Thread.currentThread;
 
@@ -41,16 +42,14 @@ public final class ThreadedStreamConsumer
 
     private static final int ITEM_LIMIT_BEFORE_SLEEP = 10 * 1000;
 
-    private static final long CLOSE_TIMEOUT_MILLIS = 5 * 60 * 1000L;
-
     private final BlockingQueue<String> items = new 
ArrayBlockingQueue<String>( ITEM_LIMIT_BEFORE_SLEEP );
 
+    private final AtomicBoolean stop = new AtomicBoolean();
+
     private final Thread thread;
 
     private final Pumper pumper;
 
-    private volatile boolean stop;
-
     final class Pumper
             implements Runnable
     {
@@ -76,14 +75,14 @@ public final class ThreadedStreamConsumer
          */
         public void run()
         {
-            while ( !ThreadedStreamConsumer.this.stop )
+            while ( !ThreadedStreamConsumer.this.stop.get() )
             {
                 try
                 {
                     String item = ThreadedStreamConsumer.this.items.take();
                     if ( shouldStopQueueing( item ) )
                     {
-                        break;
+                        return;
                     }
                     target.consumeLine( item );
                 }
@@ -114,7 +113,7 @@ public final class ThreadedStreamConsumer
 
     public void consumeLine( String s )
     {
-        if ( stop && !thread.isAlive() )
+        if ( stop.get() && !thread.isAlive() )
         {
             items.clear();
             return;
@@ -134,24 +133,17 @@ public final class ThreadedStreamConsumer
     public void close()
             throws IOException
     {
-        if ( stop )
-        {
-            return;
-        }
-
-        try
+        if ( stop.compareAndSet( false, true ) )
         {
-            items.put( END_ITEM );
-            thread.join( CLOSE_TIMEOUT_MILLIS );
-        }
-        catch ( InterruptedException e )
-        {
-            currentThread().interrupt();
-            throw new IOException( e );
-        }
-        finally
-        {
-            stop = true;
+            items.clear();
+            try
+            {
+                items.put( END_ITEM );
+            }
+            catch ( InterruptedException e )
+            {
+                currentThread().interrupt();
+            }
         }
 
         if ( pumper.hasErrors() )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
index 12a5f92..9e94e7f 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
@@ -46,4 +46,9 @@ final class MockNotifiableTestStream
     public void noop()
     {
     }
+
+    @Override
+    public void acknowledgeByeEventReceived()
+    {
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
index 6fc171e..b117a38 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
@@ -95,6 +95,7 @@ public class TestProvidingInputStreamTest
         assertThat( is.read(), is( 0 ) );
         assertThat( is.read(), is( 0 ) );
         assertThat( is.read(), is( 0 ) );
+        is.close();
         assertThat( is.read(), is( -1 ) );
     }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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 49ae52d..5024992 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
@@ -36,6 +36,7 @@ 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 NOOP = new Command( MasterProcessCommand.NOOP 
);
+    public static final Command BYE_ACK = new Command( 
MasterProcessCommand.BYE_ACK );
 
     private final MasterProcessCommand command;
     private final String data;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
index 57f1c2c..fb3ad05 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
@@ -42,6 +42,7 @@ import static java.lang.Thread.State.TERMINATED;
 import static java.lang.StrictMath.max;
 import static org.apache.maven.surefire.booter.Command.toShutdown;
 import static 
org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.BYE_ACK;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
@@ -166,6 +167,11 @@ public final class CommandReader
         addListener( NOOP, listener );
     }
 
+    public void addByeAckListener( CommandListener listener )
+    {
+        addListener( BYE_ACK, listener );
+    }
+
     private void addListener( MasterProcessCommand cmd, CommandListener 
listener )
     {
         listeners.add( new BiProperty<MasterProcessCommand, CommandListener>( 
cmd, listener ) );
@@ -470,17 +476,15 @@ public final class CommandReader
                 CommandReader.this.makeQueueFull();
                 CommandReader.this.wakeupIterator();
                 insertToListeners( toShutdown( shutdown ) );
-                switch ( shutdown )
+                if ( shutdown.isExit() )
                 {
-                    case EXIT:
-                        System.exit( 1 );
-                    case KILL:
-                        Runtime.getRuntime().halt( 1 );
-                    case DEFAULT:
-                    default:
-                        // should not happen; otherwise you missed enum case
-                        break;
+                    System.exit( 1 );
+                }
+                else if ( shutdown.isKill() )
+                {
+                    Runtime.getRuntime().halt( 1 );
                 }
+                // else is default: should not happen; otherwise you missed 
enum case
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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 d5e314a..229c8af 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
@@ -46,7 +46,8 @@ public enum MasterProcessCommand
     SHUTDOWN( 3, String.class ),
 
     /** To tell a forked process that the master process is still alive. 
Repeated after 10 seconds. */
-    NOOP( 4, Void.class );
+    NOOP( 4, Void.class ),
+    BYE_ACK( 5, Void.class );
 
     private static final Charset ASCII = Charset.forName( "ASCII" );
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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
index 262cb29..77a09cf 100644
--- 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
@@ -44,6 +44,21 @@ public enum Shutdown
         return param;
     }
 
+    public boolean isKill()
+    {
+        return this == KILL;
+    }
+
+    public boolean isExit()
+    {
+        return this == EXIT;
+    }
+
+    public boolean isDefaultShutdown()
+    {
+        return this == DEFAULT;
+    }
+
     public static boolean isKnown( String param )
     {
         for ( Shutdown shutdown : values() )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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 8396d8b..6e66a18 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
@@ -117,6 +117,13 @@ public class MasterProcessCommandTest
                     decoded = command.toDataTypeAsString( encoded );
                     assertNull( decoded );
                     break;
+                case  BYE_ACK:
+                    assertEquals( Void.class, command.getDataType() );
+                    encoded = command.fromDataType( dummyData );
+                    assertThat( encoded.length, is( 0 ) );
+                    decoded = command.toDataTypeAsString( encoded );
+                    assertNull( decoded );
+                    break;
                 default:
                     fail();
             }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/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 b76df2f..665e2f8 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
@@ -36,21 +36,22 @@ import java.lang.reflect.InvocationTargetException;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static java.lang.Math.max;
 import static java.lang.System.err;
 import static java.lang.System.out;
 import static java.lang.System.setErr;
 import static java.lang.System.setOut;
 import static java.lang.Thread.currentThread;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.maven.surefire.booter.CommandReader.getReader;
 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;
-import static org.apache.maven.surefire.booter.Shutdown.EXIT;
-import static org.apache.maven.surefire.booter.Shutdown.KILL;
 import static 
org.apache.maven.surefire.booter.SystemPropertyManager.setSystemProperties;
 import static org.apache.maven.surefire.util.ReflectionUtils.instantiateOneArg;
 import static 
org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThreadFactory;
@@ -70,8 +71,10 @@ public final class ForkedBooter
 {
     private static final long DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS = 30;
     private static final long PING_TIMEOUT_IN_SECONDS = 20;
-    private static final ScheduledExecutorService JVM_TERMINATOR = 
createJvmTerminator();
+    private static final long ONE_SECOND_IN_MILLIS = 1000;
+    private static final ScheduledExecutorService JVM_PING = 
createPingScheduler();
 
+    private static volatile ScheduledThreadPoolExecutor jvmTerminator;
     private static volatile long systemExitTimeoutInSeconds = 
DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS;
 
     /**
@@ -157,7 +160,7 @@ public final class ForkedBooter
             encodeAndWriteToOutput( ( (char) BOOTERCODE_BYE ) + ",0,BYE!\n", 
originalOut );
             originalOut.flush();
             // noinspection CallToSystemExit
-            exit( 0, EXIT, reader, false );
+            exit( 0, reader );
         }
         catch ( Throwable t )
         {
@@ -166,7 +169,7 @@ public final class ForkedBooter
             // noinspection UseOfSystemOutOrSystemErr
             t.printStackTrace( err );
             // noinspection ProhibitedExceptionThrown,CallToSystemExit
-            exit( 1, EXIT, reader, false );
+            exit( 1 );
         }
         finally
         {
@@ -181,11 +184,11 @@ public final class ForkedBooter
 
     private static ScheduledFuture<?> listenToShutdownCommands( CommandReader 
reader )
     {
-        reader.addShutdownListener( createExitHandler( reader ) );
+        reader.addShutdownListener( createExitHandler() );
         AtomicBoolean pingDone = new AtomicBoolean( true );
         reader.addNoopListener( createPingHandler( pingDone ) );
-        return JVM_TERMINATOR.scheduleAtFixedRate( createPingJob( pingDone, 
reader ),
-                                                   0, PING_TIMEOUT_IN_SECONDS, 
SECONDS );
+        Runnable pingJob = createPingJob( pingDone );
+        return JVM_PING.scheduleAtFixedRate( pingJob, 0, 
PING_TIMEOUT_IN_SECONDS, SECONDS );
     }
 
     private static CommandListener createPingHandler( final AtomicBoolean 
pingDone )
@@ -199,18 +202,27 @@ public final class ForkedBooter
         };
     }
 
-    private static CommandListener createExitHandler( final CommandReader 
reader )
+    private static CommandListener createExitHandler()
     {
         return new CommandListener()
         {
             public void update( Command command )
             {
-                exit( 1, command.toShutdownData(), reader, true );
+                Shutdown shutdown = command.toShutdownData();
+                if ( shutdown.isKill() )
+                {
+                    kill();
+                }
+                else if ( shutdown.isExit() )
+                {
+                    exit( 1 );
+                }
+                // else refers to shutdown=testset, but not used now, keeping 
reader open
             }
         };
     }
 
-    private static Runnable createPingJob( final AtomicBoolean pingDone, final 
CommandReader reader  )
+    private static Runnable createPingJob( final AtomicBoolean pingDone  )
     {
         return new Runnable()
         {
@@ -219,7 +231,7 @@ public final class ForkedBooter
                 boolean hasPing = pingDone.getAndSet( false );
                 if ( !hasPing )
                 {
-                    exit( 1, KILL, reader, true );
+                    kill();
                 }
             }
         };
@@ -231,23 +243,44 @@ public final class ForkedBooter
         out.write( encodeBytes, 0, encodeBytes.length );
     }
 
-    private static void exit( int returnCode, Shutdown shutdownType, 
CommandReader reader, boolean stopReaderOnExit )
+    private static void kill()
     {
-        switch ( shutdownType )
+        Runtime.getRuntime().halt( 1 );
+    }
+
+    private static void exit( int returnCode )
+    {
+        launchLastDitchDaemonShutdownThread( returnCode );
+        System.exit( returnCode );
+    }
+
+    private static void exit( int returnCode, final CommandReader reader )
+    {
+        final Semaphore barrier = new Semaphore( 0 );
+        reader.addByeAckListener( new CommandListener()
+                                  {
+                                      @Override
+                                      public void update( Command command )
+                                      {
+                                          barrier.release();
+                                      }
+                                  }
+        );
+        launchLastDitchDaemonShutdownThread( returnCode );
+        final long timeoutMillis = max( systemExitTimeoutInSeconds * 
ONE_SECOND_IN_MILLIS, ONE_SECOND_IN_MILLIS );
+        acquireOnePermit( barrier, timeoutMillis );
+        System.exit( returnCode );
+    }
+
+    private static boolean acquireOnePermit( Semaphore barrier, long 
timeoutMillis )
+    {
+        try
         {
-            case KILL:
-                Runtime.getRuntime().halt( returnCode );
-            case EXIT:
-                if ( stopReaderOnExit )
-                {
-                    reader.stop();
-                }
-                launchLastDitchDaemonShutdownThread( returnCode );
-                System.exit( returnCode );
-            case DEFAULT:
-                // refers to shutdown=testset, but not used now, keeping 
reader open
-            default:
-                break;
+            return barrier.tryAcquire( timeoutMillis, MILLISECONDS );
+        }
+        catch ( InterruptedException e )
+        {
+            return true;
         }
     }
 
@@ -269,11 +302,25 @@ public final class ForkedBooter
         return new ForkingReporterFactory( trimStackTrace, originalSystemOut );
     }
 
-    private static ScheduledExecutorService createJvmTerminator()
+    private static synchronized ScheduledThreadPoolExecutor getJvmTerminator()
+    {
+        if ( jvmTerminator == null )
+        {
+            ThreadFactory threadFactory =
+                    newDaemonThreadFactory( 
"last-ditch-daemon-shutdown-thread-" + systemExitTimeoutInSeconds + "s" );
+            jvmTerminator = new ScheduledThreadPoolExecutor( 1, threadFactory 
);
+            jvmTerminator.setMaximumPoolSize( 1 );
+            return jvmTerminator;
+        }
+        else
+        {
+            return jvmTerminator;
+        }
+    }
+
+    private static ScheduledExecutorService createPingScheduler()
     {
-        ThreadFactory threadFactory = newDaemonThreadFactory( 
"last-ditch-daemon-shutdown-thread-"
-                                                            + 
systemExitTimeoutInSeconds
-                                                            + "s" );
+        ThreadFactory threadFactory = newDaemonThreadFactory( "ping-" + 
PING_TIMEOUT_IN_SECONDS + "s" );
         ScheduledThreadPoolExecutor executor = new 
ScheduledThreadPoolExecutor( 1, threadFactory );
         executor.setMaximumPoolSize( 1 );
         executor.prestartCoreThread();
@@ -283,13 +330,14 @@ public final class ForkedBooter
     @SuppressWarnings( "checkstyle:emptyblock" )
     private static void launchLastDitchDaemonShutdownThread( final int 
returnCode )
     {
-        JVM_TERMINATOR.schedule( new Runnable()
-        {
-            public void run()
-            {
-                Runtime.getRuntime().halt( returnCode );
-            }
-        }, systemExitTimeoutInSeconds, SECONDS );
+        getJvmTerminator().schedule( new Runnable()
+                                        {
+                                            public void run()
+                                            {
+                                                Runtime.getRuntime().halt( 
returnCode );
+                                            }
+                                        }, systemExitTimeoutInSeconds, SECONDS
+        );
     }
 
     private static RunResult invokeProviderInSameClassLoader( Object testSet, 
Object factory,

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/40bf72f1/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
----------------------------------------------------------------------
diff --git 
a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
 
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
index 75aa743..b185217 100644
--- 
a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
+++ 
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
@@ -52,6 +52,7 @@ public class Surefire141PluggableProvidersIT
         throws Exception
     {
         unpack( "surefire-141-pluggableproviders" )
+            .setForkJvm()
             .maven()
             .showExceptionMessages()
             .debugLogging()
@@ -68,13 +69,20 @@ public class Surefire141PluggableProvidersIT
         final String errorText = "Let's fail with a runtimeException";
 
         OutputValidator validator = unpack( "surefire-141-pluggableproviders" )
+            .setForkJvm()
             .sysProp( "invokeCrash", "runtimeException" )
             .maven()
             .withFailure()
-            .executeTest()
-            .verifyTextInLog( errorText );
+            .executeTest();
 
         assertErrorMessage( validator, errorText );
+
+        boolean hasErrorInLog = verifiedErrorInLog( validator, "There was an 
error in the forked process" );
+        boolean verifiedInLog = verifiedErrorInLog( validator, errorText );
+        assertThat( hasErrorInLog && verifiedInLog )
+                .describedAs( "'" + errorText + "' could not be verified in 
log.txt nor *.dump file. ("
+                                      + hasErrorInLog + ", " + verifiedInLog + 
")" )
+                .isTrue();
     }
 
     @Test
@@ -84,13 +92,20 @@ public class Surefire141PluggableProvidersIT
         final String errorText = "Let's fail with a reporterexception";
 
         OutputValidator validator = unpack( "surefire-141-pluggableproviders" )
+            .setForkJvm()
             .sysProp( "invokeCrash", "reporterException" )
             .maven()
             .withFailure()
-            .executeTest()
-            .verifyTextInLog( errorText );
+            .executeTest();
 
         assertErrorMessage( validator, errorText );
+
+        boolean hasErrorInLog = verifiedErrorInLog( validator, "There was an 
error in the forked process" );
+        boolean verifiedInLog = verifiedErrorInLog( validator, errorText );
+        assertThat( hasErrorInLog && verifiedInLog )
+                .describedAs( "'" + errorText + "' could not be verified in 
log.txt nor *.dump file. ("
+                                      + hasErrorInLog + ", " + verifiedInLog + 
")" )
+                .isTrue();
     }
 
     @Test
@@ -100,15 +115,19 @@ public class Surefire141PluggableProvidersIT
         final String errorText = "Let's fail with a runtimeException";
 
         OutputValidator validator = unpack( "surefire-141-pluggableproviders" )
+                                            .setForkJvm()
                                             .sysProp( "constructorCrash", 
"runtimeException" )
                                             .maven()
                                             .withFailure()
                                             .executeTest();
 
+        assertErrorMessage( validator, errorText );
+
+        boolean hasErrorInLog = verifiedErrorInLog( validator, "There was an 
error in the forked process" );
         boolean verifiedInLog = verifiedErrorInLog( validator, errorText );
-        boolean verifiedInDump = verifiedErrorInDump( validator, errorText );
-        assertThat( verifiedInLog || verifiedInDump )
-                .describedAs( "'" + errorText + "' could not be verified in 
log.txt nor *.dump file." )
+        assertThat( hasErrorInLog && verifiedInLog )
+                .describedAs( "'" + errorText + "' could not be verified in 
log.txt nor *.dump file. ("
+                                      + hasErrorInLog + ", " + verifiedInLog + 
")" )
                 .isTrue();
     }
 

Reply via email to