4244896: (process) Provide System.getPid(), System.killProcess(String pid)
Reviewed-by: alanb
--- a/jdk/src/share/classes/java/lang/Process.java Mon Jun 25 14:19:38 2012 +0100
+++ b/jdk/src/share/classes/java/lang/Process.java Tue Jun 26 13:27:26 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
package java.lang;
import java.io.*;
+import java.util.concurrent.TimeUnit;
/**
* The {@link ProcessBuilder#start()} and
@@ -91,7 +92,7 @@
* @return the output stream connected to the normal input of the
* subprocess
*/
- abstract public OutputStream getOutputStream();
+ public abstract OutputStream getOutputStream();
/**
* Returns the input stream connected to the normal output of the
@@ -117,7 +118,7 @@
* @return the input stream connected to the normal output of the
* subprocess
*/
- abstract public InputStream getInputStream();
+ public abstract InputStream getInputStream();
/**
* Returns the input stream connected to the error output of the
@@ -138,7 +139,7 @@
* @return the input stream connected to the error output of
* the subprocess
*/
- abstract public InputStream getErrorStream();
+ public abstract InputStream getErrorStream();
/**
* Causes the current thread to wait, if necessary, until the
@@ -156,7 +157,51 @@
* thread while it is waiting, then the wait is ended and
* an {@link InterruptedException} is thrown.
*/
- abstract public int waitFor() throws InterruptedException;
+ public abstract int waitFor() throws InterruptedException;
+
+ /**
+ * Causes the current thread to wait, if necessary, until the
+ * subprocess represented by this {@code Process} object has
+ * terminated, or the specified waiting time elapses.
+ *
+ * <p>If the subprocess has already terminated then this method returns
+ * immediately with the value {@code true}. If the process has not
+ * terminated and the timeout value is less than, or equal to, zero, then
+ * this method returns immediately with the value {@code false}.
+ *
+ * <p>The default implementation of this methods polls the {@code exitValue}
+ * to check if the process has terminated. Concrete implementations of this
+ * class are strongly encouraged to override this method with a more
+ * efficient implementation.
+ *
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the {@code timeout} argument
+ * @return {@code true} if the subprocess has exited and {@code false} if
+ * the waiting time elapsed before the subprocess has exited.
+ * @throws InterruptedException if the current thread is interrupted
+ * while waiting.
+ * @throws NullPointerException if unit is null
+ * @since 1.8
+ */
+ public boolean waitFor(long timeout, TimeUnit unit)
+ throws InterruptedException
+ {
+ long startTime = System.nanoTime();
+ long rem = unit.toNanos(timeout);
+
+ do {
+ try {
+ exitValue();
+ return true;
+ } catch(IllegalThreadStateException ex) {
+ if (rem > 0)
+ Thread.sleep(
+ Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
+ }
+ rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
+ } while (rem > 0);
+ return false;
+ }
/**
* Returns the exit value for the subprocess.
@@ -167,11 +212,54 @@
* @throws IllegalThreadStateException if the subprocess represented
* by this {@code Process} object has not yet terminated
*/
- abstract public int exitValue();
+ public abstract int exitValue();
+
+ /**
+ * Kills the subprocess. Whether the subprocess represented by this
+ * {@code Process} object is forcibly terminated or not is
+ * implementation dependent.
+ */
+ public abstract void destroy();
/**
* Kills the subprocess. The subprocess represented by this
* {@code Process} object is forcibly terminated.
+ *
+ * <p>The default implementation of this method invokes {@link #destroy}
+ * and so may not forcibly terminate the process. Concrete implementations
+ * of this class are strongly encouraged to override this method with a
+ * compliant implementation. Invoking this method on {@code Process}
+ * objects returned by {@link ProcessBuilder#start} and
+ * {@link Runtime#exec} will forcibly terminate the process.
+ *
+ * <p>Note: The subprocess may not terminate immediately.
+ * i.e. {@code isAlive()} may return true for a brief period
+ * after {@code destroyForcibly()} is called. This method
+ * may be chained to {@code waitFor()} if needed.
+ *
+ * @return the {@code Process} object representing the
+ * subprocess to be forcibly destroyed.
+ * @since 1.8
*/
- abstract public void destroy();
+ public Process destroyForcibly() {
+ destroy();
+ return this;
+ }
+
+ /**
+ * Tests whether the subprocess represented by this {@code Process} is
+ * alive.
+ *
+ * @return {@code true} if the subprocess represented by this
+ * {@code Process} object has not yet terminated.
+ * @since 1.8
+ */
+ public boolean isAlive() {
+ try {
+ exitValue();
+ return false;
+ } catch(IllegalThreadStateException e) {
+ return true;
+ }
+ }
}
--- a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.bsd Mon Jun 25 14:19:38 2012 +0100
+++ b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.bsd Tue Jun 26 13:27:26 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,6 +38,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
import java.security.AccessController;
import static java.security.AccessController.doPrivileged;
import java.security.PrivilegedAction;
@@ -211,6 +212,24 @@
}
return exitcode;
}
+
+ @Override
+ public synchronized boolean waitFor(long timeout, TimeUnit unit)
+ throws InterruptedException
+ {
+ if (hasExited) return true;
+ if (timeout <= 0) return false;
+
+ long timeoutAsNanos = unit.toNanos(timeout);
+ long startTime = System.nanoTime();
+ long rem = timeoutAsNanos;
+
+ while (!hasExited && (rem > 0)) {
+ wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
+ rem = timeoutAsNanos - (System.nanoTime() - startTime);
+ }
+ return hasExited;
+ }
public synchronized int exitValue() {
if (!hasExited) {
@@ -219,8 +238,8 @@
return exitcode;
}
- private static native void destroyProcess(int pid);
- public void destroy() {
+ private static native void destroyProcess(int pid, boolean force);
+ private void destroy(boolean force) {
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
@@ -229,13 +248,28 @@
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
- destroyProcess(pid);
+ destroyProcess(pid, force);
}
try { stdin.close(); } catch (IOException ignored) {}
try { stdout.close(); } catch (IOException ignored) {}
try { stderr.close(); } catch (IOException ignored) {}
}
+ public void destroy() {
+ destroy(false);
+ }
+
+ @Override
+ public Process destroyForcibly() {
+ destroy(true);
+ return this;
+ }
+
+ @Override
+ public synchronized boolean isAlive() {
+ return !hasExited;
+ }
+
/* This routine initializes JNI field offsets for the class */
private static native void initIDs();
--- a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.linux Mon Jun 25 14:19:38 2012 +0100
+++ b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.linux Tue Jun 26 13:27:26 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,6 +38,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
import java.security.AccessController;
import static java.security.AccessController.doPrivileged;
import java.security.PrivilegedAction;
@@ -212,6 +213,24 @@
return exitcode;
}
+ @Override
+ public synchronized boolean waitFor(long timeout, TimeUnit unit)
+ throws InterruptedException
+ {
+ if (hasExited) return true;
+ if (timeout <= 0) return false;
+
+ long timeoutAsNanos = unit.toNanos(timeout);
+ long startTime = System.nanoTime();
+ long rem = timeoutAsNanos;
+
+ while (!hasExited && (rem > 0)) {
+ wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
+ rem = timeoutAsNanos - (System.nanoTime() - startTime);
+ }
+ return hasExited;
+ }
+
public synchronized int exitValue() {
if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited");
@@ -219,8 +238,8 @@
return exitcode;
}
- private static native void destroyProcess(int pid);
- public void destroy() {
+ private static native void destroyProcess(int pid, boolean force);
+ private void destroy(boolean force) {
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
@@ -229,13 +248,28 @@
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
- destroyProcess(pid);
+ destroyProcess(pid, force);
}
try { stdin.close(); } catch (IOException ignored) {}
try { stdout.close(); } catch (IOException ignored) {}
try { stderr.close(); } catch (IOException ignored) {}
}
+ public void destroy() {
+ destroy(false);
+ }
+
+ @Override
+ public Process destroyForcibly() {
+ destroy(true);
+ return this;
+ }
+
+ @Override
+ public synchronized boolean isAlive() {
+ return !hasExited;
+ }
+
/* This routine initializes JNI field offsets for the class */
private static native void initIDs();
--- a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.solaris Mon Jun 25 14:19:38 2012 +0100
+++ b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.solaris Tue Jun 26 13:27:26 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
package java.lang;
import java.io.*;
+import java.util.concurrent.TimeUnit;
/* java.lang.Process subclass in the UNIX environment.
*
@@ -158,6 +159,24 @@
return exitcode;
}
+ @Override
+ public synchronized boolean waitFor(long timeout, TimeUnit unit)
+ throws InterruptedException
+ {
+ if (hasExited) return true;
+ if (timeout <= 0) return false;
+
+ long timeoutAsNanos = unit.toNanos(timeout);
+ long startTime = System.nanoTime();
+ long rem = timeoutAsNanos;
+
+ while (!hasExited && (rem > 0)) {
+ wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
+ rem = timeoutAsNanos - (System.nanoTime() - startTime);
+ }
+ return hasExited;
+ }
+
public synchronized int exitValue() {
if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited");
@@ -165,8 +184,8 @@
return exitcode;
}
- private static native void destroyProcess(int pid);
- public synchronized void destroy() {
+ private static native void destroyProcess(int pid, boolean force);
+ private synchronized void destroy(boolean force) {
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
@@ -174,7 +193,7 @@
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.
if (!hasExited)
- destroyProcess(pid);
+ destroyProcess(pid, force);
try {
stdin_stream.close();
if (stdout_inner_stream != null)
@@ -187,6 +206,21 @@
}
}
+ public void destroy() {
+ destroy(false);
+ }
+
+ @Override
+ public Process destroyForcibly() {
+ destroy(true);
+ return this;
+ }
+
+ @Override
+ public synchronized boolean isAlive() {
+ return !hasExited;
+ }
+
// A FileInputStream that supports the deferment of the actual close
// operation until the last pending I/O operation on the stream has
// finished. This is required on Solaris because we must close the stdin
--- a/jdk/src/solaris/native/java/lang/UNIXProcess_md.c Mon Jun 25 14:19:38 2012 +0100
+++ b/jdk/src/solaris/native/java/lang/UNIXProcess_md.c Tue Jun 26 13:27:26 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -955,7 +955,11 @@
}
JNIEXPORT void JNICALL
-Java_java_lang_UNIXProcess_destroyProcess(JNIEnv *env, jobject junk, jint pid)
+Java_java_lang_UNIXProcess_destroyProcess(JNIEnv *env,
+ jobject junk,
+ jint pid,
+ jboolean force)
{
- kill(pid, SIGTERM);
+ int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
+ kill(pid, sig);
}
--- a/jdk/src/windows/classes/java/lang/ProcessImpl.java Mon Jun 25 14:19:38 2012 +0100
+++ b/jdk/src/windows/classes/java/lang/ProcessImpl.java Tue Jun 26 13:27:26 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,7 @@
import java.lang.ProcessBuilder.Redirect;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.concurrent.TimeUnit;
/* This class is for the exclusive use of ProcessBuilder.start() to
* create new processes.
@@ -254,11 +255,44 @@
throw new InterruptedException();
return exitValue();
}
+
private static native void waitForInterruptibly(long handle);
+ @Override
+ public boolean waitFor(long timeout, TimeUnit unit)
+ throws InterruptedException
+ {
+ if (getExitCodeProcess(handle) != STILL_ACTIVE) return true;
+ if (timeout <= 0) return false;
+
+ long msTimeout = unit.toMillis(timeout);
+
+ waitForTimeoutInterruptibly(handle, msTimeout);
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ return (getExitCodeProcess(handle) != STILL_ACTIVE);
+ }
+
+ private static native void waitForTimeoutInterruptibly(
+ long handle, long timeout);
+
public void destroy() { terminateProcess(handle); }
+
+ @Override
+ public Process destroyForcibly() {
+ destroy();
+ return this;
+ }
+
private static native void terminateProcess(long handle);
+ @Override
+ public boolean isAlive() {
+ return isProcessAlive(handle);
+ }
+
+ private static native boolean isProcessAlive(long handle);
+
/**
* Create a process using the win32 function CreateProcess.
*
--- a/jdk/src/windows/native/java/lang/ProcessImpl_md.c Mon Jun 25 14:19:38 2012 +0100
+++ b/jdk/src/windows/native/java/lang/ProcessImpl_md.c Tue Jun 26 13:27:26 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -299,18 +299,45 @@
if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
FALSE, /* Wait for ANY event */
- INFINITE) /* Wait forever */
+ INFINITE) /* Wait forever */
== WAIT_FAILED)
win32Error(env, "WaitForMultipleObjects");
}
JNIEXPORT void JNICALL
+Java_java_lang_ProcessImpl_waitForTimeoutInterruptibly(JNIEnv *env,
+ jclass ignored,
+ jlong handle,
+ jlong timeout)
+{
+ HANDLE events[2];
+ DWORD dwTimeout = (DWORD)timeout;
+ DWORD result;
+ events[0] = (HANDLE) handle;
+ events[1] = JVM_GetThreadInterruptEvent();
+ result = WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
+ FALSE, /* Wait for ANY event */
+ dwTimeout); /* Wait for dwTimeout */
+
+ if (result == WAIT_FAILED)
+ win32Error(env, "WaitForMultipleObjects");
+}
+
+JNIEXPORT void JNICALL
Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle)
{
TerminateProcess((HANDLE) handle, 1);
}
JNIEXPORT jboolean JNICALL
+Java_java_lang_ProcessImpl_isProcessAlive(JNIEnv *env, jclass ignored, jlong handle)
+{
+ DWORD dwExitStatus;
+ GetExitCodeProcess(handle, &dwExitStatus);
+ return dwExitStatus == STILL_ACTIVE;
+}
+
+JNIEXPORT jboolean JNICALL
Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle)
{
return CloseHandle((HANDLE) handle);
--- a/jdk/test/java/lang/ProcessBuilder/Basic.java Mon Jun 25 14:19:38 2012 +0100
+++ b/jdk/test/java/lang/ProcessBuilder/Basic.java Tue Jun 26 13:27:26 2012 +0100
@@ -26,7 +26,7 @@
* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
* 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
- * 4947220 7018606 7034570
+ * 4947220 7018606 7034570 4244896
* @summary Basic tests for Process and Environment Variable code
* @run main/othervm/timeout=300 Basic
* @author Martin Buchholz
@@ -38,6 +38,7 @@
import java.io.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.security.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
@@ -636,6 +637,44 @@
static boolean is() { return is; }
}
+ static class DelegatingProcess extends Process {
+ final Process p;
+
+ DelegatingProcess(Process p) {
+ this.p = p;
+ }
+
+ @Override
+ public void destroy() {
+ p.destroy();
+ }
+
+ @Override
+ public int exitValue() {
+ return p.exitValue();
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException {
+ return p.waitFor();
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return p.getOutputStream();
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return p.getInputStream();
+ }
+
+ @Override
+ public InputStream getErrorStream() {
+ return p.getErrorStream();
+ }
+ }
+
private static boolean matches(String str, String regex) {
return Pattern.compile(regex).matcher(str).find();
}
@@ -2090,6 +2129,112 @@
policy.setPermissions(new RuntimePermission("setSecurityManager"));
System.setSecurityManager(null);
+ //----------------------------------------------------------------
+ // Check that Process.isAlive() &
+ // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.
+ //----------------------------------------------------------------
+ try {
+ List<String> childArgs = new ArrayList<String>(javaChildArgs);
+ childArgs.add("sleep");
+ final Process p = new ProcessBuilder(childArgs).start();
+ long start = System.nanoTime();
+ if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {
+ fail("Test failed: Process exited prematurely");
+ }
+ long end = System.nanoTime();
+ // give waitFor(timeout) a wide berth (100ms)
+ if ((end - start) > 100000000)
+ fail("Test failed: waitFor took too long");
+
+ p.destroy();
+ p.waitFor();
+
+ if (p.isAlive() ||
+ !p.waitFor(0, TimeUnit.MILLISECONDS))
+ {
+ fail("Test failed: Process still alive - please terminate " +
+ p.toString() + " manually");
+ }
+ } catch (Throwable t) { unexpected(t); }
+
+ //----------------------------------------------------------------
+ // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
+ // works as expected.
+ //----------------------------------------------------------------
+ try {
+ List<String> childArgs = new ArrayList<String>(javaChildArgs);
+ childArgs.add("sleep");
+ final Process p = new ProcessBuilder(childArgs).start();
+ long start = System.nanoTime();
+
+ p.waitFor(1000, TimeUnit.MILLISECONDS);
+
+ long end = System.nanoTime();
+ if ((end - start) < 500000000)
+ fail("Test failed: waitFor didn't take long enough");
+
+ p.destroy();
+
+ start = System.nanoTime();
+ p.waitFor(1000, TimeUnit.MILLISECONDS);
+ end = System.nanoTime();
+ if ((end - start) > 100000000)
+ fail("Test failed: waitFor took too long on a dead process.");
+ } catch (Throwable t) { unexpected(t); }
+
+ //----------------------------------------------------------------
+ // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
+ // interrupt works as expected.
+ //----------------------------------------------------------------
+ try {
+ List<String> childArgs = new ArrayList<String>(javaChildArgs);
+ childArgs.add("sleep");
+ final Process p = new ProcessBuilder(childArgs).start();
+ final long start = System.nanoTime();
+
+ final Thread thread = new Thread() {
+ public void run() {
+ try {
+ try {
+ p.waitFor(10000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ return;
+ }
+ fail("waitFor() wasn't interrupted");
+ } catch (Throwable t) { unexpected(t); }}};
+
+ thread.start();
+ Thread.sleep(1000);
+ thread.interrupt();
+ p.destroy();
+ } catch (Throwable t) { unexpected(t); }
+
+ //----------------------------------------------------------------
+ // Check the default implementation for
+ // Process.waitFor(long, TimeUnit)
+ //----------------------------------------------------------------
+ try {
+ List<String> childArgs = new ArrayList<String>(javaChildArgs);
+ childArgs.add("sleep");
+ final Process proc = new ProcessBuilder(childArgs).start();
+ DelegatingProcess p = new DelegatingProcess(proc);
+ long start = System.nanoTime();
+
+ p.waitFor(1000, TimeUnit.MILLISECONDS);
+
+ long end = System.nanoTime();
+ if ((end - start) < 500000000)
+ fail("Test failed: waitFor didn't take long enough");
+
+ p.destroy();
+
+ start = System.nanoTime();
+ p.waitFor(1000, TimeUnit.MILLISECONDS);
+ end = System.nanoTime();
+ // allow for the less accurate default implementation
+ if ((end - start) > 200000000)
+ fail("Test failed: waitFor took too long on a dead process.");
+ } catch (Throwable t) { unexpected(t); }
}
static void closeStreams(Process p) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessBuilder/DestroyTest.java Tue Jun 26 13:27:26 2012 +0100
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 4244896
+ * @summary Test for the various platform specific implementations of
+ * destroyForcibly.
+ */
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+abstract class ProcessTest implements Runnable {
+ ProcessBuilder bldr;
+ Process p;
+
+ public Process killProc(boolean force) throws Exception {
+ if (force) {
+ p.destroyForcibly();
+ } else {
+ p.destroy();
+ }
+ return p;
+ }
+
+ public boolean isAlive() {
+ return p.isAlive();
+ }
+
+ public void run() {
+ try {
+ String line;
+ BufferedReader is =
+ new BufferedReader(new InputStreamReader(p.getInputStream()));
+ while ((line = is.readLine()) != null)
+ System.err.println("ProcessTrap: " + line);
+ } catch(IOException e) {
+ if (!e.getMessage().matches("[Ss]tream [Cc]losed")) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public abstract void runTest() throws Exception;
+}
+
+class UnixTest extends ProcessTest {
+ public UnixTest(File script) throws IOException {
+ script.deleteOnExit();
+ createScript(script);
+ bldr = new ProcessBuilder(script.getCanonicalPath());
+ bldr.redirectErrorStream(true);
+ bldr.directory(new File("."));
+ p = bldr.start();
+ }
+
+ void createScript(File processTrapScript) throws IOException {
+ processTrapScript.deleteOnExit();
+ FileWriter fstream = new FileWriter(processTrapScript);
+ try (BufferedWriter out = new BufferedWriter(fstream)) {
+ out.write("#!/bin/bash\n" +
+ "echo \\\"ProcessTrap.sh started: trapping SIGTERM/SIGINT\\\"\n" +
+ "trap bashtrap SIGTERM SIGINT\n" +
+ "bashtrap()\n" +
+ "{\n" +
+ " echo \\\"SIGTERM/SIGINT detected!\\\"\n" +
+ "}\n" +
+ "\n" +
+ "while :\n" +
+ "do\n" +
+ " sleep 1;\n" +
+ "done\n");
+ }
+ processTrapScript.setExecutable(true, true);
+ }
+
+ @Override
+ public void runTest() throws Exception {
+ killProc(false);
+ Thread.sleep(1000);
+ if (!p.isAlive())
+ throw new RuntimeException("Process terminated prematurely.");
+ killProc(true).waitFor();
+ if (p.isAlive())
+ throw new RuntimeException("Problem terminating the process.");
+ }
+}
+
+class MacTest extends UnixTest {
+ public MacTest(File script) throws IOException {
+ super(script);
+ }
+
+ @Override
+ public void runTest() throws Exception {
+ // On Mac, it appears that when we close the processes streams
+ // after a destroy() call, the process terminates with a
+ // SIGPIPE even if it was trapping the SIGTERM, so as with
+ // windows, we skip the trap test and go straight to destroyForcibly().
+ killProc(true).waitFor();
+ if (p.isAlive())
+ throw new RuntimeException("Problem terminating the process.");
+ }
+}
+
+class WindowsTest extends ProcessTest {
+ public WindowsTest() throws IOException {
+ bldr = new ProcessBuilder("ftp");
+ bldr.redirectErrorStream(true);
+ bldr.directory(new File("."));
+ p = bldr.start();
+ }
+
+ @Override
+ public void runTest() throws Exception {
+ killProc(true).waitFor();
+ }
+}
+
+public class DestroyTest {
+
+ public static ProcessTest getTest() throws Exception {
+ String osName = System.getProperty("os.name");
+ if (osName.startsWith("Windows")) {
+ return new WindowsTest();
+ } else if (osName.startsWith("Linux") == true) {
+ return new UnixTest(
+ File.createTempFile("ProcessTrap-", ".sh",null));
+ } else if (osName.startsWith("Mac OS")) {
+ return new MacTest(
+ File.createTempFile("ProcessTrap-", ".sh",null));
+ } else if (osName.equals("SunOS")) {
+ return new UnixTest(
+ File.createTempFile("ProcessTrap-", ".sh",null));
+ }
+ return null;
+ }
+
+ public static void main(String args[]) throws Exception {
+ ProcessTest test = getTest();
+ if (test == null) {
+ throw new RuntimeException("Unrecognised OS");
+ } else {
+ new Thread(test).start();
+ Thread.sleep(1000);
+ test.runTest();
+ }
+ }
+}
+