Repository: maven-surefire
Updated Branches:
  refs/heads/SUREFIRE-1342 d468680be -> 703021371


[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/70302137
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/70302137
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/70302137

Branch: refs/heads/SUREFIRE-1342
Commit: 70302137108d496720354b180debd224f0fcfd5d
Parents: d468680
Author: Tibor17 <tibo...@lycos.com>
Authored: Mon Mar 13 03:10:08 2017 +0100
Committer: Tibor17 <tibo...@lycos.com>
Committed: Mon Mar 13 03:10:08 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          | 48 +++++------
 .../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     | 84 ++++++++++++++------
 15 files changed, 187 insertions(+), 78 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/70302137/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/70302137/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/70302137/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/70302137/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/70302137/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/70302137/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/70302137/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..67154a4 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,29 +133,22 @@ public final class ThreadedStreamConsumer
     public void close()
             throws IOException
     {
-        if ( stop )
-        {
-            return;
-        }
-
-        try
-        {
-            items.put( END_ITEM );
-            thread.join( CLOSE_TIMEOUT_MILLIS );
-        }
-        catch ( InterruptedException e )
+        if ( stop.compareAndSet( false, true ) )
         {
-            currentThread().interrupt();
-            throw new IOException( e );
-        }
-        finally
-        {
-            stop = true;
-        }
+            items.clear();
+            try
+            {
+                items.put( END_ITEM );
+            }
+            catch ( InterruptedException e )
+            {
+                currentThread().interrupt();
+            }
 
-        if ( pumper.hasErrors() )
-        {
-            pumper.throwErrors();
+            if ( pumper.hasErrors() )
+            {
+                pumper.throwErrors();
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/70302137/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/70302137/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/70302137/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/70302137/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/70302137/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/70302137/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/70302137/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/70302137/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..041cd2c 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,6 +71,7 @@ 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 long ONE_SECOND_IN_MILLIS = 1000;
     private static final ScheduledExecutorService JVM_TERMINATOR = 
createJvmTerminator();
 
     private static volatile long systemExitTimeoutInSeconds = 
DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS;
@@ -157,7 +159,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 +168,7 @@ public final class ForkedBooter
             // noinspection UseOfSystemOutOrSystemErr
             t.printStackTrace( err );
             // noinspection ProhibitedExceptionThrown,CallToSystemExit
-            exit( 1, EXIT, reader, false );
+            exit( 1 );
         }
         finally
         {
@@ -181,11 +183,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_TERMINATOR.scheduleAtFixedRate( pingJob, 0, 
PING_TIMEOUT_IN_SECONDS, SECONDS );
     }
 
     private static CommandListener createPingHandler( final AtomicBoolean 
pingDone )
@@ -199,18 +201,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 +230,7 @@ public final class ForkedBooter
                 boolean hasPing = pingDone.getAndSet( false );
                 if ( !hasPing )
                 {
-                    exit( 1, KILL, reader, true );
+                    kill();
                 }
             }
         };
@@ -231,23 +242,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()
+    {
+        Runtime.getRuntime().halt( 1 );
+    }
+
+    private static void exit( int returnCode )
+    {
+        launchLastDitchDaemonShutdownThread( returnCode );
+        System.exit( returnCode );
+    }
+
+    private static void exit( int returnCode, final CommandReader reader )
     {
-        switch ( shutdownType )
+        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;
         }
     }
 

Reply via email to