mirror of
https://github.com/corda/corda.git
synced 2025-01-19 11:16:54 +00:00
fix Runtime.exec bugs
The first bug affected POSIX systems: if the app never called Process.waitFor, we'd never call waitpid on the child and thus leak a zombie process. This patch ensures that we always call waitpid by spawning a thread to handle it. The second bug affected Windows systems: we weren't closing the child's ends of the stdin, stdout, and stderr pipes after process creation, which lead to us blocking forever while reading from the child's stdout or stderr.
This commit is contained in:
parent
5ade8d1cf6
commit
3d18f88ad9
@ -192,6 +192,10 @@ Java_java_lang_Runtime_exec(JNIEnv* e, jclass,
|
||||
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
|
||||
0, 0, &si, &pi);
|
||||
|
||||
CloseHandle(in[1]);
|
||||
CloseHandle(out[0]);
|
||||
CloseHandle(err[1]);
|
||||
|
||||
if (not success) {
|
||||
throwNew(e, "java/io/IOException", getErrorStr(GetLastError()));
|
||||
return;
|
||||
@ -202,19 +206,6 @@ Java_java_lang_Runtime_exec(JNIEnv* e, jclass,
|
||||
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_lang_Runtime_exitValue(JNIEnv* e, jclass, jlong pid)
|
||||
{
|
||||
DWORD exitCode;
|
||||
BOOL success = GetExitCodeProcess(reinterpret_cast<HANDLE>(pid), &exitCode);
|
||||
if(not success){
|
||||
throwNew(e, "java/lang/Exception", getErrorStr(GetLastError()));
|
||||
} else if(exitCode == STILL_ACTIVE){
|
||||
throwNew(e, "java/lang/IllegalThreadStateException", "Process is still active");
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_lang_Runtime_waitFor(JNIEnv* e, jclass, jlong pid)
|
||||
{
|
||||
@ -317,20 +308,6 @@ Java_java_lang_Runtime_exec(JNIEnv* e, jclass,
|
||||
fcntl(err[0], F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_lang_Runtime_exitValue(JNIEnv* e, jclass, jlong pid)
|
||||
{
|
||||
int status;
|
||||
pid_t returned = waitpid(pid, &status, WNOHANG);
|
||||
if(returned == 0){
|
||||
throwNewErrno(e, "java/lang/IllegalThreadStateException");
|
||||
} else if(returned == -1){
|
||||
throwNewErrno(e, "java/lang/Exception");
|
||||
}
|
||||
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_lang_Runtime_waitFor(JNIEnv*, jclass, jlong pid)
|
||||
{
|
||||
|
@ -43,27 +43,71 @@ public class Runtime {
|
||||
}
|
||||
}
|
||||
|
||||
public Process exec(String command) throws IOException {
|
||||
long[] process = new long[4];
|
||||
public Process exec(String command) {
|
||||
StringTokenizer t = new StringTokenizer(command);
|
||||
String[] cmd = new String[t.countTokens()];
|
||||
for (int i = 0; i < cmd.length; i++)
|
||||
cmd[i] = t.nextToken();
|
||||
exec(cmd, process);
|
||||
return new MyProcess(process[0], (int) process[1], (int) process[2], (int) process[3]);
|
||||
|
||||
return exec(cmd);
|
||||
}
|
||||
|
||||
public Process exec(String[] command) {
|
||||
long[] process = new long[4];
|
||||
exec(command, process);
|
||||
return new MyProcess(process[0], (int) process[1], (int) process[2], (int) process[3]);
|
||||
public MyProcess exec(final String[] command) {
|
||||
final MyProcess[] process = new MyProcess[1];
|
||||
final Throwable[] exception = new Throwable[1];
|
||||
|
||||
synchronized (process) {
|
||||
Thread t = new Thread() {
|
||||
public void run() {
|
||||
synchronized (process) {
|
||||
try {
|
||||
long[] info = new long[4];
|
||||
exec(command, info);
|
||||
process[0] = new MyProcess
|
||||
(info[0], (int) info[1], (int) info[2], (int) info[3]);
|
||||
|
||||
MyProcess p = process[0];
|
||||
synchronized (p) {
|
||||
try {
|
||||
if (p.pid != 0) {
|
||||
p.exitCode = Runtime.waitFor(p.pid);
|
||||
p.pid = 0;
|
||||
}
|
||||
} finally {
|
||||
p.notifyAll();
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
exception[0] = e;
|
||||
} finally {
|
||||
process.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
|
||||
while (process[0] == null && exception[0] == null) {
|
||||
try {
|
||||
process.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exception[0] != null) {
|
||||
throw new RuntimeException(exception[0]);
|
||||
}
|
||||
|
||||
return process[0];
|
||||
}
|
||||
|
||||
public native void addShutdownHook(Thread t);
|
||||
|
||||
private static native void exec(String[] command, long[] process);
|
||||
|
||||
private static native int exitValue(long pid);
|
||||
private static native void exec(String[] command, long[] process)
|
||||
throws IOException;
|
||||
|
||||
private static native int waitFor(long pid);
|
||||
|
||||
@ -95,13 +139,6 @@ public class Runtime {
|
||||
throw new RuntimeException("not implemented");
|
||||
}
|
||||
|
||||
public int exitValue() {
|
||||
if (pid != 0) {
|
||||
exitCode = Runtime.exitValue(pid);
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return new FileInputStream(new FileDescriptor(in));
|
||||
}
|
||||
@ -114,11 +151,19 @@ public class Runtime {
|
||||
return new FileInputStream(new FileDescriptor(err));
|
||||
}
|
||||
|
||||
public int waitFor() throws InterruptedException {
|
||||
public synchronized int exitValue() {
|
||||
if (pid != 0) {
|
||||
exitCode = Runtime.waitFor(pid);
|
||||
pid = 0;
|
||||
throw new IllegalThreadStateException();
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public synchronized int waitFor() throws InterruptedException {
|
||||
while (pid != 0) {
|
||||
wait();
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user