diff --git a/jdk/src/share/classes/java/lang/Process.java b/jdk/src/share/classes/java/lang/Process.java index ec0a661d7ce..4e945382465 100644 --- a/jdk/src/share/classes/java/lang/Process.java +++ b/jdk/src/share/classes/java/lang/Process.java @@ -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 @@ public abstract class Process { * @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 @@ public abstract class Process { * @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 @@ public abstract class Process { * @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 @@ public abstract class Process { * 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. + * + *
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}. + * + *
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 @@ public abstract class Process { * @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. + * + *
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. + * + *
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;
+ }
+ }
}
diff --git a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.bsd b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.bsd
index 409390820cf..d70c120267a 100644
--- a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.bsd
+++ b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.bsd
@@ -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.Arrays;
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 @@ final class UNIXProcess extends Process {
}
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
// 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();
diff --git a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.linux b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.linux
index 61676e084cf..760ffa7b394 100644
--- a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.linux
+++ b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.linux
@@ -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.Arrays;
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
// 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();
diff --git a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.solaris b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.solaris
index 550ce7c2b05..ecdc280a08f 100644
--- a/jdk/src/solaris/classes/java/lang/UNIXProcess.java.solaris
+++ b/jdk/src/solaris/classes/java/lang/UNIXProcess.java.solaris
@@ -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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
// 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 @@ final class UNIXProcess extends Process {
}
}
+ 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
diff --git a/jdk/src/solaris/native/java/lang/UNIXProcess_md.c b/jdk/src/solaris/native/java/lang/UNIXProcess_md.c
index 43077ce4509..5aff82fb0c4 100644
--- a/jdk/src/solaris/native/java/lang/UNIXProcess_md.c
+++ b/jdk/src/solaris/native/java/lang/UNIXProcess_md.c
@@ -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 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
}
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);
}
diff --git a/jdk/src/windows/classes/java/lang/ProcessImpl.java b/jdk/src/windows/classes/java/lang/ProcessImpl.java
index 88486e5f472..d4cdc6c7eec 100644
--- a/jdk/src/windows/classes/java/lang/ProcessImpl.java
+++ b/jdk/src/windows/classes/java/lang/ProcessImpl.java
@@ -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.io.BufferedOutputStream;
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 @@ final class ProcessImpl extends Process {
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.
*
diff --git a/jdk/src/windows/native/java/lang/ProcessImpl_md.c b/jdk/src/windows/native/java/lang/ProcessImpl_md.c
index 89dc8252a3a..ed14eeba1be 100644
--- a/jdk/src/windows/native/java/lang/ProcessImpl_md.c
+++ b/jdk/src/windows/native/java/lang/ProcessImpl_md.c
@@ -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,17 +299,44 @@ Java_java_lang_ProcessImpl_waitForInterruptibly(JNIEnv *env, jclass ignored, jlo
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)
{
diff --git a/jdk/test/java/lang/ProcessBuilder/Basic.java b/jdk/test/java/lang/ProcessBuilder/Basic.java
index b18a0f66ca2..fe8ff4e2255 100644
--- a/jdk/test/java/lang/ProcessBuilder/Basic.java
+++ b/jdk/test/java/lang/ProcessBuilder/Basic.java
@@ -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 static java.lang.ProcessBuilder.Redirect.*;
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 @@ public class Basic {
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 @@ public class Basic {
policy.setPermissions(new RuntimePermission("setSecurityManager"));
System.setSecurityManager(null);
+ //----------------------------------------------------------------
+ // Check that Process.isAlive() &
+ // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.
+ //----------------------------------------------------------------
+ try {
+ List