1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.IOException; 25 import java.lang.InterruptedException; 26 import java.time.Duration; 27 import java.time.Instant; 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.concurrent.CompletableFuture; 31 import java.util.concurrent.ConcurrentHashMap; 32 import java.util.concurrent.ExecutionException; 33 34 import jdk.testlibrary.Utils; 35 36 import org.testng.annotations.Test; 37 import org.testng.Assert; 38 import org.testng.TestNG; 39 40 /* 41 * @test 42 * @build jdk.testlibrary.Utils 43 * @summary Functions of Process.onExit and ProcessHandle.onExit 44 * @author Roger Riggs 45 */ 46 47 public class OnExitTest extends ProcessUtil { 48 49 @SuppressWarnings("raw_types") 50 public static void main(String[] args) { 51 Class<?>[] testclass = { OnExitTest.class}; 52 TestNG testng = new TestNG(); 53 testng.setTestClasses(testclass); 54 testng.run(); 55 } 56 57 /** 58 * Basic test of exitValue and onExit. 59 */ 60 @Test 61 public static void test1() { 62 try { 63 int[] exitValues = {0, 1, 10}; 64 for (int value : exitValues) { 65 Process p = JavaChild.spawn("exit", Integer.toString(value)); 66 CompletableFuture<Process> future = p.onExit(); 67 future.thenAccept( (ph) -> { 68 int actualExitValue = ph.exitValue(); 69 printf(" javaChild done: %s, exitStatus: %d%n", 70 ph, actualExitValue); 71 Assert.assertEquals(actualExitValue, value, "actualExitValue incorrect"); 72 Assert.assertEquals(ph, p, "Different Process passed to thenAccept"); 73 }); 74 75 Process h = future.get(); 76 Assert.assertEquals(h, p); 77 Assert.assertEquals(p.exitValue(), value); 78 Assert.assertFalse(p.isAlive(), "Process should not be alive"); 79 p.waitFor(); 80 } 81 } catch (IOException | InterruptedException | ExecutionException ex) { 82 Assert.fail(ex.getMessage(), ex); 83 } finally { 84 destroyProcessTree(ProcessHandle.current()); 85 } 86 } 87 88 /** 89 * Test of Completion handler when parent is killed. 90 * Spawn 1 child to spawn 3 children each with 2 children. 91 */ 92 @Test 93 public static void test2() { 94 ProcessHandle procHandle = null; 95 try { 96 ConcurrentHashMap<ProcessHandle, ProcessHandle> processes = new ConcurrentHashMap<>(); 97 List<ProcessHandle> children = getChildren(ProcessHandle.current()); 98 children.forEach(ProcessUtil::printProcess); 99 Assert.assertEquals(children.size(), 0, 100 "Expected to start with zero children; " + children); 101 102 JavaChild proc = JavaChild.spawnJavaChild("stdin"); 103 procHandle = proc.toHandle(); 104 printf(" spawned: %d%n", proc.getPid()); 105 106 proc.forEachOutputLine((s) -> { 107 String[] split = s.trim().split(" "); 108 if (split.length == 3 && split[1].equals("spawn")) { 109 Long child = Long.valueOf(split[2]); 110 Long parent = Long.valueOf(split[0].split(":")[0]); 111 processes.put(ProcessHandle.of(child).get(), ProcessHandle.of(parent).get()); 112 } 113 }); 114 115 proc.sendAction("spawn", "3", "stdin"); 116 117 proc.sendAction("child", "spawn", "2", "stdin"); 118 119 // Poll until all 9 child processes exist or the timeout is reached 120 int expected = 9; 121 long timeout = Utils.adjustTimeout(10L); 122 Instant endTimeout = Instant.now().plusSeconds(timeout); 123 do { 124 Thread.sleep(200L); 125 printf(" subprocess count: %d, waiting for %d%n", processes.size(), expected); 126 } while (processes.size() < expected && 127 Instant.now().isBefore(endTimeout)); 128 129 children = getAllChildren(procHandle); 130 131 ConcurrentHashMap<ProcessHandle, CompletableFuture<ProcessHandle>> completions = 132 new ConcurrentHashMap<>(); 133 Instant startTime = Instant.now(); 134 // Create a future for each of the 9 children 135 processes.forEach( (p, parent) -> { 136 CompletableFuture<ProcessHandle> cf = p.onExit().whenComplete((ph, ex) -> { 137 Duration elapsed = Duration.between(startTime, Instant.now()); 138 printf("whenComplete: pid: %s, exception: %s, thread: %s, elapsed: %s%n", 139 ph, ex, Thread.currentThread(), elapsed); 140 }); 141 completions.put(p, cf); 142 }); 143 144 // Check that each of the spawned processes is included in the children 145 List<ProcessHandle> remaining = new ArrayList<>(children); 146 processes.forEach((p, parent) -> { 147 Assert.assertTrue(remaining.remove(p), "spawned process should have been in children"); 148 }); 149 150 // Remove Win32 system spawned conhost.exe processes 151 remaining.removeIf(ProcessUtil::isWindowsConsole); 152 153 remaining.forEach(p -> printProcess(p, "unexpected: ")); 154 if (remaining.size() > 0) { 155 // Show full list for debugging 156 ProcessUtil.logTaskList(); 157 } 158 159 proc.destroy(); // kill off the parent 160 proc.waitFor(); 161 162 // Wait for all the processes and corresponding onExit CF to be completed 163 processes.forEach((p, parent) -> { 164 try { 165 p.onExit().get(); 166 completions.get(p).join(); 167 } catch (InterruptedException | ExecutionException ex) { 168 // ignore 169 } 170 }); 171 172 // Verify that all 9 exit handlers were called with the correct ProcessHandle 173 processes.forEach((p, parent) -> { 174 ProcessHandle value = completions.get(p).getNow(null); 175 Assert.assertEquals(p, value, "onExit.get value expected: " + p 176 + ", actual: " + value 177 + ": " + p.info()); 178 }); 179 180 // Show the status of the original children 181 children.forEach(p -> printProcess(p, "after onExit:")); 182 183 Assert.assertEquals(proc.isAlive(), false, "destroyed process is alive:: %s%n" + proc); 184 185 List<ProcessHandle> children2 = getAllChildren(procHandle); 186 printf(" children2: %s%n", children2.toString()); 187 Assert.assertEquals(children2.size(), 0, "After onExit, expected no children"); 188 } catch (IOException | InterruptedException ex) { 189 Assert.fail(ex.getMessage()); 190 } finally { 191 if (procHandle != null) { 192 destroyProcessTree(procHandle); 193 } 194 } 195 } 196 197 }