From 4a120b9453a31d172c56e65dd95b364bdd8c39b1 Mon Sep 17 00:00:00 2001 From: martin Date: Fri, 17 Sep 2010 14:35:00 -0700 Subject: [PATCH] 6981138: (process) Process.waitFor() may hang if subprocess has live descendants (lnx) Summary: Do exit status handling before trying to close streams Reviewed-by: alanb, dholmes --- .../classes/java/lang/UNIXProcess.java.linux | 12 ++-- test/java/lang/ProcessBuilder/Basic.java | 58 +++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/solaris/classes/java/lang/UNIXProcess.java.linux b/src/solaris/classes/java/lang/UNIXProcess.java.linux index 29477f93b..b277c2184 100644 --- a/src/solaris/classes/java/lang/UNIXProcess.java.linux +++ b/src/solaris/classes/java/lang/UNIXProcess.java.linux @@ -176,7 +176,13 @@ final class UNIXProcess extends Process { }}); } - synchronized void processExited(int exitcode) { + void processExited(int exitcode) { + synchronized (this) { + this.exitcode = exitcode; + hasExited = true; + notifyAll(); + } + if (stdout instanceof ProcessPipeInputStream) ((ProcessPipeInputStream) stdout).processExited(); @@ -185,10 +191,6 @@ final class UNIXProcess extends Process { if (stdin instanceof ProcessPipeOutputStream) ((ProcessPipeOutputStream) stdin).processExited(); - - this.exitcode = exitcode; - hasExited = true; - notifyAll(); } public OutputStream getOutputStream() { diff --git a/test/java/lang/ProcessBuilder/Basic.java b/test/java/lang/ProcessBuilder/Basic.java index f4e3c26d9..d0bbc1b4a 100644 --- a/test/java/lang/ProcessBuilder/Basic.java +++ b/test/java/lang/ProcessBuilder/Basic.java @@ -1824,6 +1824,64 @@ public class Basic { } } catch (Throwable t) { unexpected(t); } + //---------------------------------------------------------------- + // Check that subprocesses which create subprocesses of their + // own do not cause parent to hang waiting for file + // descriptors to be closed. + //---------------------------------------------------------------- + try { + if (Unix.is() + && new File("/bin/bash").exists() + && new File("/bin/sleep").exists()) { + final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 6666)" }; + final ProcessBuilder pb = new ProcessBuilder(cmd); + final Process p = pb.start(); + final InputStream stdout = p.getInputStream(); + final InputStream stderr = p.getErrorStream(); + final OutputStream stdin = p.getOutputStream(); + final Thread reader = new Thread() { + public void run() { + try { stdout.read(); } + catch (IOException e) { + // e.printStackTrace(); + if (EnglishUnix.is() && + ! (e.getMessage().matches(".*Bad file descriptor.*"))) + unexpected(e); + } + catch (Throwable t) { unexpected(t); }}}; + reader.setDaemon(true); + reader.start(); + Thread.sleep(100); + p.destroy(); + // Subprocess is now dead, but file descriptors remain open. + check(p.waitFor() != 0); + check(p.exitValue() != 0); + stdout.close(); + stderr.close(); + stdin.close(); + //---------------------------------------------------------- + // There remain unsolved issues with asynchronous close. + // Here's a highly non-portable experiment to demonstrate: + //---------------------------------------------------------- + if (Boolean.getBoolean("wakeupJeff!")) { + System.out.println("wakeupJeff!"); + // Initialize signal handler for INTERRUPT_SIGNAL. + new FileInputStream("/bin/sleep").getChannel().close(); + // Send INTERRUPT_SIGNAL to every thread in this java. + String[] wakeupJeff = { + "/bin/bash", "-c", + "/bin/ps --noheaders -Lfp $PPID | " + + "/usr/bin/perl -nale 'print $F[3]' | " + + // INTERRUPT_SIGNAL == 62 on my machine du jour. + "/usr/bin/xargs kill -62" + }; + new ProcessBuilder(wakeupJeff).start().waitFor(); + // If wakeupJeff worked, reader probably got EBADF. + reader.join(); + } + } + } catch (Throwable t) { unexpected(t); } + //---------------------------------------------------------------- // Attempt to start process with insufficient permissions fails. //---------------------------------------------------------------- -- GitLab