Thomas Bright avatar Thomas Bright committed 27fb996

STASHDEV-1915 add unit tests for recursive kills and change windows kill behaviour to reduce recursive kill issues

Comments (0)

Files changed (5)

         <dependency>
             <groupId>org.jvnet.winp</groupId>
             <artifactId>winp</artifactId>
-            <version>1.15-atlassian-1</version>
+            <version>1.17-SNAPSHOT</version>
             <exclusions>
                 <exclusion>
                     <groupId>junit</groupId>

src/main/java/com/atlassian/utils/process/ExternalProcessImpl.java

                 }
             }
         } finally {
-            internalCancel();
+            internalCancel(exitCode);
         }
 
         if (processIncomplete && !interrupted) {
      */
     public void cancel() {
         if (canceled.compareAndSet(false, true)) {
-            internalCancel();
+            internalCancel(-1);
         }
     }
 
-    private void internalCancel() {
+    private void internalCancel(int exitCode) {
         
         if (inputPump != null) {
             // we determine whether we're going to interrupt the inputPump *after* the process has already finished.
         }
 
         if (process != null) {
-            if (isWindows()) {
+            if (exitCode != 0 && isWindows()) {
                 try {
                     new WinProcess(process).killRecursively();
-                } catch (Throwable t) {
-                    LOG.error("Failed to kill Windows process; falling back on Process.destroy()", t);
+                } catch (Throwable t2) {
+                    LOG.error("Failed to kill Windows process", t2);
                     process.destroy();
                 }
+
             } else {
                 process.destroy();
             }

src/test/java/com/atlassian/utils/process/ProcessBuilderEncodingTest.java

     @Test
     public void testNativeWindowsKilling() {
         if (isWindows()) {
-            ExternalProcess process = new ExternalProcessBuilder().command(Arrays.asList("pause")).build();
+            ExternalProcess process = new ExternalProcessBuilder().command(Arrays.asList("pause")).handler(new StringProcessHandler()).build();
             process.start();
             process.cancel();
         }

src/test/java/com/atlassian/utils/process/RecursiveApp.java

+package com.atlassian.utils.process;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Thomas Bright
+ * Date: 17/09/12
+ * Time: 1:51 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class RecursiveApp {
+
+    public static void main(String [] args) throws IOException, InterruptedException {
+        int currentLevel = Integer.parseInt(args[0]);
+        int maxLevel = Integer.parseInt(args[1]);
+        int timeOut = Integer.parseInt(args[2]);
+
+        if (currentLevel < maxLevel)
+        {
+            //recurse
+            System.out.println(currentLevel+" recursing");
+            String[] command = new String[] {"java", "com.atlassian.utils.process.RecursiveApp", Integer.toString(++currentLevel),Integer.toString(maxLevel),Integer.toString(timeOut)};
+            for (String s : command)
+            {
+                System.out.print(s);
+                System.out.print(" ");
+            }
+            System.out.println();
+            ProcessBuilder builder =                    new ProcessBuilder().command(command);
+            builder.environment().put("CLASSPATH", System.getProperty("java.class.path"));
+            Process child = builder.start();
+
+
+            DataInputStream ls_in = new DataInputStream(
+                    child.getInputStream());
+            String ls_str;
+
+
+            while ((ls_str = ls_in.readLine()) != null) {
+                System.out.println(ls_str);
+            }
+
+            child.waitFor();
+        }
+        else
+        {
+            long end = System.currentTimeMillis() + (timeOut * 1000);
+            while ( System.currentTimeMillis()  < end)
+            {
+                try {
+                    Thread.sleep(timeOut);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+
+
+    }
+
+}

src/test/java/com/atlassian/utils/process/RecursiveKillTest.java

+package com.atlassian.utils.process;
+
+import junit.framework.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+import org.jvnet.winp.WinProcess;
+import org.jvnet.winp.WinpException;
+
+import java.util.Arrays;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Thomas Bright
+ * Date: 17/09/12
+ * Time: 2:18 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class RecursiveKillTest {
+
+
+
+    @Test
+    public void testSimpleRecursiveKill() throws InterruptedException {
+
+        //we can only live on windows...
+        Assume.assumeTrue(System.getProperty("os.name").toLowerCase().indexOf("win") != -1);
+
+        ExternalProcessSettings settings = new ExternalProcessSettings();
+        String[] command = new String[]{"java", "com.atlassian.utils.process.RecursiveApp", Integer.toString(0), Integer.toString(5), Integer.toString(100000)};
+        settings.setCommand(Arrays.asList(command));
+        settings.getEnvironment().put("CLASSPATH", System.getProperty("java.class.path"));
+        settings.setExecutionTimeout(3000);
+        settings.setProcessHandler(new StringProcessHandler());
+        ExternalProcess proc = ExternalProcessBuilder.getExternalProcessFactory().create(settings);
+
+        proc.start();
+        Thread.sleep(1000);//give it some time to start up
+        {
+            int count = getRecursiveAppCount();
+            Assert.assertEquals("Initial depth ", 6, count);    //one for command, one for java
+        }
+
+        proc.finish();
+        //sleep and let process die/be killed
+        long end = System.currentTimeMillis() + 3000;
+        while (System.currentTimeMillis() < end) {
+            try {
+                Thread.sleep(1500);
+            } catch (InterruptedException e) {
+            }
+        }
+        {
+            int count = getRecursiveAppCount();
+            Assert.assertEquals("Final depth ", 0, count);
+        }
+
+
+    }
+
+    private int getRecursiveAppCount() {
+        int count = 0;
+        for (WinProcess process : WinProcess.all()) {
+            String commandLine = null;
+            try {
+                commandLine = process.getCommandLine();
+            } catch (WinpException e) {
+                //can't open all the processes
+            }
+
+            if (commandLine != null) {
+                if (!commandLine.contains("cmd") && commandLine.contains("com.atlassian.utils.process.RecursiveApp")) {
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.