Path: blob/master/test/jdk/java/lang/ProcessHandle/OnExitTest.java
41149 views
/*1* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.IOException;24import java.time.Duration;25import java.time.Instant;26import java.util.ArrayList;27import java.util.List;28import java.util.concurrent.ArrayBlockingQueue;29import java.util.concurrent.CompletableFuture;30import java.util.concurrent.ConcurrentHashMap;31import java.util.concurrent.ExecutionException;32import java.util.concurrent.TimeUnit;3334import jdk.test.lib.Utils;3536import org.testng.annotations.Test;37import org.testng.Assert;38import org.testng.TestNG;3940/*41* @test42* @library /test/lib43* @modules jdk.management44* @build jdk.test.lib.Utils45* @run testng OnExitTest46* @summary Functions of Process.onExit and ProcessHandle.onExit47* @author Roger Riggs48*/4950public class OnExitTest extends ProcessUtil {5152@SuppressWarnings("raw_types")53public static void main(String[] args) {54Class<?>[] testclass = { OnExitTest.class};55TestNG testng = new TestNG();56testng.setTestClasses(testclass);57testng.run();58}5960/**61* Basic test of exitValue and onExit.62*/63@Test64public static void test1() {65try {66int[] exitValues = {0, 1, 10};67for (int value : exitValues) {68Process p = JavaChild.spawn("exit", Integer.toString(value));69CompletableFuture<Process> future = p.onExit();70future.thenAccept( (ph) -> {71int actualExitValue = ph.exitValue();72printf(" javaChild done: %s, exitStatus: %d%n",73ph, actualExitValue);74Assert.assertEquals(actualExitValue, value, "actualExitValue incorrect");75Assert.assertEquals(ph, p, "Different Process passed to thenAccept");76});7778Process h = future.get();79Assert.assertEquals(h, p);80Assert.assertEquals(p.exitValue(), value);81Assert.assertFalse(p.isAlive(), "Process should not be alive");82p.waitFor();83}84} catch (IOException | InterruptedException | ExecutionException ex) {85Assert.fail(ex.getMessage(), ex);86} finally {87destroyProcessTree(ProcessHandle.current());88}89}9091/**92* Test of Completion handler when parent is killed.93* Spawn 1 child to spawn 3 children each with 2 children.94*/95@Test96public static void test2() {97ProcessHandle procHandle = null;98try {99ConcurrentHashMap<ProcessHandle, ProcessHandle> processes = new ConcurrentHashMap<>();100List<ProcessHandle> children = getChildren(ProcessHandle.current());101children.forEach(ProcessUtil::printProcess);102103JavaChild proc = JavaChild.spawnJavaChild("stdin");104procHandle = proc.toHandle();105printf(" spawned: %d%n", proc.pid());106107proc.forEachOutputLine((s) -> {108String[] split = s.trim().split(" ");109if (split.length == 3 && split[1].equals("spawn")) {110Long child = Long.valueOf(split[2]);111Long parent = Long.valueOf(split[0].split(":")[0]);112processes.put(ProcessHandle.of(child).get(), ProcessHandle.of(parent).get());113}114});115116proc.sendAction("spawn", "3", "stdin");117118proc.sendAction("child", "spawn", "2", "stdin");119120// Poll until all 9 child processes exist or the timeout is reached121int expected = 9;122long timeout = Utils.adjustTimeout(10L);123Instant endTimeout = Instant.now().plusSeconds(timeout);124do {125Thread.sleep(200L);126printf(" subprocess count: %d, waiting for %d%n", processes.size(), expected);127} while (processes.size() < expected &&128Instant.now().isBefore(endTimeout));129130if (processes.size() < expected) {131printf("WARNING: not all children have been started. Can't complete test.%n");132printf(" You can try to increase the timeout or%n");133printf(" you can try to use a faster VM (i.e. not a debug version).%n");134}135children = getDescendants(procHandle);136137ConcurrentHashMap<ProcessHandle, CompletableFuture<ProcessHandle>> completions =138new ConcurrentHashMap<>();139Instant startTime = Instant.now();140// Create a future for each of the 9 children141processes.forEach( (p, parent) -> {142CompletableFuture<ProcessHandle> cf = p.onExit().whenComplete((ph, ex) -> {143Duration elapsed = Duration.between(startTime, Instant.now());144printf("whenComplete: pid: %s, exception: %s, thread: %s, elapsed: %s%n",145ph, ex, Thread.currentThread(), elapsed);146});147completions.put(p, cf);148});149150// Check that each of the spawned processes is included in the children151List<ProcessHandle> remaining = new ArrayList<>(children);152processes.forEach((p, parent) -> {153Assert.assertTrue(remaining.remove(p), "spawned process should have been in children");154});155156// Remove Win32 system spawned conhost.exe processes157remaining.removeIf(ProcessUtil::isWindowsConsole);158159remaining.forEach(p -> printProcess(p, "unexpected: "));160if (remaining.size() > 0) {161// Show full list for debugging162ProcessUtil.logTaskList();163}164165proc.destroy(); // kill off the parent166proc.waitFor();167168// Wait for all the processes and corresponding onExit CF to be completed169processes.forEach((p, parent) -> {170try {171p.onExit().get();172completions.get(p).join();173} catch (InterruptedException | ExecutionException ex) {174// ignore175}176});177178// Verify that all 9 exit handlers were called with the correct ProcessHandle179processes.forEach((p, parent) -> {180ProcessHandle value = completions.get(p).getNow(null);181Assert.assertEquals(p, value, "onExit.get value expected: " + p182+ ", actual: " + value183+ ": " + p.info());184});185186// Show the status of the original children187children.forEach(p -> printProcess(p, "after onExit:"));188189Assert.assertEquals(proc.isAlive(), false, "destroyed process is alive:: %s%n" + proc);190} catch (IOException | InterruptedException ex) {191Assert.fail(ex.getMessage());192} finally {193if (procHandle != null) {194destroyProcessTree(procHandle);195}196}197}198199/**200* Verify that onExit completes for a non-child process only when201* the process has exited.202* Spawn a child (A) waiting to be commanded to exit.203* Spawn a child (B) to wait for that process to exit.204* Command (A) to exit.205* Check that (B) does not complete until (A) has exited.206*/207@Test208public static void peerOnExitTest() {209String line = null;210ArrayBlockingQueue<String> alines = new ArrayBlockingQueue<>(100);211ArrayBlockingQueue<String> blines = new ArrayBlockingQueue<>(100);212JavaChild A = null;213try {214String[] split;215A = JavaChild.spawnJavaChild("stdin");216A.forEachOutputLine(l -> alines.add(l));217218// Verify A is running219A.sendAction("pid");220do {221split = getSplitLine(alines);222} while (!"pid".equals(split[1]));223224JavaChild B = null;225try {226B = JavaChild.spawnJavaChild("stdin");227B.forEachOutputLine(l -> blines.add(l));228229// Verify B is running230B.sendAction("pid");231do {232split = getSplitLine(blines);233} while (!"pid".equals(split[1]));234235// Tell B to wait for A's pid236B.sendAction("waitpid", A.pid());237238// Wait a bit to see if B will prematurely report the termination of A239try {240line = blines.poll(5L, TimeUnit.SECONDS);241} catch (InterruptedException ie) {242Assert.fail("interrupted", ie);243}244Assert.assertNull(line, "waitpid didn't wait");245246A.toHandle().onExit().thenAccept(p -> {247System.out.printf(" A.toHandle().onExit().A info: %s, now: %s%n",248p.info(), Instant.now());249});250251A.onExit().thenAccept(p -> {252System.out.printf(" A.onExit().A info: %s, now: %s%n",253p.info(), Instant.now());254});255256ProcessHandle.Info A_info = A.info();257258A.sendAction("exit", 0L);259260// Look for B to report that A has exited261do {262Instant start = Instant.now();263while (blines.isEmpty() && A.isAlive()) {264A_info = A.info(); // Spin265}266Instant end = Instant.now();267System.out.printf(" a.isAlive: %s, a.info: %s, @%s%n", A.isAlive(), A.info(),268Duration.between(start, end));269270split = getSplitLine(blines);271} while (!"waitpid".equals(split[1]));272273Assert.assertEquals(split[2], "false", "Process A should not be alive");274275B.sendAction("exit", 0L);276} catch (IOException ioe) {277Assert.fail("unable to start JavaChild B", ioe);278} finally {279B.destroyForcibly();280}281} catch (IOException ioe2) {282Assert.fail("unable to start JavaChild A", ioe2);283} finally {284A.destroyForcibly();285}286}287288private static boolean DEBUG = true;289290/**291* Get a line from the queue and split into words on whitespace.292* Log to stdout if requested.293* @param queue a queue of strings294* @return the words split from the line.295*/296private static String[] getSplitLine(ArrayBlockingQueue<String> queue) {297try {298String line = queue.take();299String[] split = line.split("\\s");300if (DEBUG) {301System.out.printf(" Child Output: %s%n", line);302}303return split;304} catch (InterruptedException ie) {305Assert.fail("interrupted", ie);306return null;307}308}309310}311312313