mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-24 04:54:40 +02:00
4244896: (process) Provide System.getPid(), System.killProcess(String pid)
Reviewed-by: alanb
This commit is contained in:
parent
d558c37a5b
commit
54ddaf5ab7
9 changed files with 598 additions and 26 deletions
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link ProcessBuilder#start()} and
|
* The {@link ProcessBuilder#start()} and
|
||||||
|
@ -91,7 +92,7 @@ public abstract class Process {
|
||||||
* @return the output stream connected to the normal input of the
|
* @return the output stream connected to the normal input of the
|
||||||
* subprocess
|
* subprocess
|
||||||
*/
|
*/
|
||||||
abstract public OutputStream getOutputStream();
|
public abstract OutputStream getOutputStream();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the input stream connected to the normal output of the
|
* 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
|
* @return the input stream connected to the normal output of the
|
||||||
* subprocess
|
* subprocess
|
||||||
*/
|
*/
|
||||||
abstract public InputStream getInputStream();
|
public abstract InputStream getInputStream();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the input stream connected to the error output of the
|
* 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
|
* @return the input stream connected to the error output of
|
||||||
* the subprocess
|
* the subprocess
|
||||||
*/
|
*/
|
||||||
abstract public InputStream getErrorStream();
|
public abstract InputStream getErrorStream();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Causes the current thread to wait, if necessary, until the
|
* 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
|
* thread while it is waiting, then the wait is ended and
|
||||||
* an {@link InterruptedException} is thrown.
|
* 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.
|
* Returns the exit value for the subprocess.
|
||||||
|
@ -167,11 +212,54 @@ public abstract class Process {
|
||||||
* @throws IllegalThreadStateException if the subprocess represented
|
* @throws IllegalThreadStateException if the subprocess represented
|
||||||
* by this {@code Process} object has not yet terminated
|
* 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
|
* Kills the subprocess. The subprocess represented by this
|
||||||
* {@code Process} object is forcibly terminated.
|
* {@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.Executors;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import static java.security.AccessController.doPrivileged;
|
import static java.security.AccessController.doPrivileged;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
@ -212,6 +213,24 @@ final class UNIXProcess extends Process {
|
||||||
return exitcode;
|
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() {
|
public synchronized int exitValue() {
|
||||||
if (!hasExited) {
|
if (!hasExited) {
|
||||||
throw new IllegalThreadStateException("process hasn't exited");
|
throw new IllegalThreadStateException("process hasn't exited");
|
||||||
|
@ -219,8 +238,8 @@ final class UNIXProcess extends Process {
|
||||||
return exitcode;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native void destroyProcess(int pid);
|
private static native void destroyProcess(int pid, boolean force);
|
||||||
public void destroy() {
|
private void destroy(boolean force) {
|
||||||
// There is a risk that pid will be recycled, causing us to
|
// There is a risk that pid will be recycled, causing us to
|
||||||
// kill the wrong process! So we only terminate processes
|
// kill the wrong process! So we only terminate processes
|
||||||
// that appear to still be running. Even with this check,
|
// 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.
|
// soon, so this is quite safe.
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!hasExited)
|
if (!hasExited)
|
||||||
destroyProcess(pid);
|
destroyProcess(pid, force);
|
||||||
}
|
}
|
||||||
try { stdin.close(); } catch (IOException ignored) {}
|
try { stdin.close(); } catch (IOException ignored) {}
|
||||||
try { stdout.close(); } catch (IOException ignored) {}
|
try { stdout.close(); } catch (IOException ignored) {}
|
||||||
try { stderr.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 */
|
/* This routine initializes JNI field offsets for the class */
|
||||||
private static native void initIDs();
|
private static native void initIDs();
|
||||||
|
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.Executors;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import static java.security.AccessController.doPrivileged;
|
import static java.security.AccessController.doPrivileged;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
@ -212,6 +213,24 @@ final class UNIXProcess extends Process {
|
||||||
return exitcode;
|
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() {
|
public synchronized int exitValue() {
|
||||||
if (!hasExited) {
|
if (!hasExited) {
|
||||||
throw new IllegalThreadStateException("process hasn't exited");
|
throw new IllegalThreadStateException("process hasn't exited");
|
||||||
|
@ -219,8 +238,8 @@ final class UNIXProcess extends Process {
|
||||||
return exitcode;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native void destroyProcess(int pid);
|
private static native void destroyProcess(int pid, boolean force);
|
||||||
public void destroy() {
|
private void destroy(boolean force) {
|
||||||
// There is a risk that pid will be recycled, causing us to
|
// There is a risk that pid will be recycled, causing us to
|
||||||
// kill the wrong process! So we only terminate processes
|
// kill the wrong process! So we only terminate processes
|
||||||
// that appear to still be running. Even with this check,
|
// 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.
|
// soon, so this is quite safe.
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!hasExited)
|
if (!hasExited)
|
||||||
destroyProcess(pid);
|
destroyProcess(pid, force);
|
||||||
}
|
}
|
||||||
try { stdin.close(); } catch (IOException ignored) {}
|
try { stdin.close(); } catch (IOException ignored) {}
|
||||||
try { stdout.close(); } catch (IOException ignored) {}
|
try { stdout.close(); } catch (IOException ignored) {}
|
||||||
try { stderr.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 */
|
/* This routine initializes JNI field offsets for the class */
|
||||||
private static native void initIDs();
|
private static native void initIDs();
|
||||||
|
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/* java.lang.Process subclass in the UNIX environment.
|
/* java.lang.Process subclass in the UNIX environment.
|
||||||
*
|
*
|
||||||
|
@ -158,6 +159,24 @@ final class UNIXProcess extends Process {
|
||||||
return exitcode;
|
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() {
|
public synchronized int exitValue() {
|
||||||
if (!hasExited) {
|
if (!hasExited) {
|
||||||
throw new IllegalThreadStateException("process hasn't exited");
|
throw new IllegalThreadStateException("process hasn't exited");
|
||||||
|
@ -165,8 +184,8 @@ final class UNIXProcess extends Process {
|
||||||
return exitcode;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native void destroyProcess(int pid);
|
private static native void destroyProcess(int pid, boolean force);
|
||||||
public synchronized void destroy() {
|
private synchronized void destroy(boolean force) {
|
||||||
// There is a risk that pid will be recycled, causing us to
|
// There is a risk that pid will be recycled, causing us to
|
||||||
// kill the wrong process! So we only terminate processes
|
// kill the wrong process! So we only terminate processes
|
||||||
// that appear to still be running. Even with this check,
|
// 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
|
// is very small, and OSes try hard to not recycle pids too
|
||||||
// soon, so this is quite safe.
|
// soon, so this is quite safe.
|
||||||
if (!hasExited)
|
if (!hasExited)
|
||||||
destroyProcess(pid);
|
destroyProcess(pid, force);
|
||||||
try {
|
try {
|
||||||
stdin_stream.close();
|
stdin_stream.close();
|
||||||
if (stdout_inner_stream != null)
|
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
|
// A FileInputStream that supports the deferment of the actual close
|
||||||
// operation until the last pending I/O operation on the stream has
|
// operation until the last pending I/O operation on the stream has
|
||||||
// finished. This is required on Solaris because we must close the stdin
|
// finished. This is required on Solaris because we must close the stdin
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.lang.ProcessBuilder.Redirect;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/* This class is for the exclusive use of ProcessBuilder.start() to
|
/* This class is for the exclusive use of ProcessBuilder.start() to
|
||||||
* create new processes.
|
* create new processes.
|
||||||
|
@ -254,11 +255,44 @@ final class ProcessImpl extends Process {
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
return exitValue();
|
return exitValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native void waitForInterruptibly(long handle);
|
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); }
|
public void destroy() { terminateProcess(handle); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Process destroyForcibly() {
|
||||||
|
destroy();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private static native void terminateProcess(long handle);
|
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.
|
* Create a process using the win32 function CreateProcess.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -304,12 +304,39 @@ Java_java_lang_ProcessImpl_waitForInterruptibly(JNIEnv *env, jclass ignored, jlo
|
||||||
win32Error(env, "WaitForMultipleObjects");
|
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
|
JNIEXPORT void JNICALL
|
||||||
Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle)
|
Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle)
|
||||||
{
|
{
|
||||||
TerminateProcess((HANDLE) handle, 1);
|
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
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle)
|
Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
|
* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
|
||||||
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
|
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
|
||||||
* 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
|
* 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
|
||||||
* 4947220 7018606 7034570
|
* 4947220 7018606 7034570 4244896
|
||||||
* @summary Basic tests for Process and Environment Variable code
|
* @summary Basic tests for Process and Environment Variable code
|
||||||
* @run main/othervm/timeout=300 Basic
|
* @run main/othervm/timeout=300 Basic
|
||||||
* @author Martin Buchholz
|
* @author Martin Buchholz
|
||||||
|
@ -38,6 +38,7 @@ import static java.lang.ProcessBuilder.Redirect.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
@ -636,6 +637,44 @@ public class Basic {
|
||||||
static boolean is() { return is; }
|
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) {
|
private static boolean matches(String str, String regex) {
|
||||||
return Pattern.compile(regex).matcher(str).find();
|
return Pattern.compile(regex).matcher(str).find();
|
||||||
}
|
}
|
||||||
|
@ -2090,6 +2129,112 @@ public class Basic {
|
||||||
policy.setPermissions(new RuntimePermission("setSecurityManager"));
|
policy.setPermissions(new RuntimePermission("setSecurityManager"));
|
||||||
System.setSecurityManager(null);
|
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) {
|
static void closeStreams(Process p) {
|
||||||
|
|
172
jdk/test/java/lang/ProcessBuilder/DestroyTest.java
Normal file
172
jdk/test/java/lang/ProcessBuilder/DestroyTest.java
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue