7147084: (process) appA hangs when read output stream of appB which starts appC that runs forever
authoruta
Thu, 08 Aug 2013 09:16:16 +0400
changeset 19372 e404c834f1cd
parent 19371 bb9611d0308c
child 19373 4bb12c72a46f
7147084: (process) appA hangs when read output stream of appB which starts appC that runs forever Reviewed-by: alanb, robm, martin
jdk/src/windows/classes/java/lang/ProcessImpl.java
jdk/src/windows/native/java/lang/ProcessImpl_md.c
jdk/test/java/lang/ProcessBuilder/InheritIOEHandle.java
jdk/test/java/lang/ProcessBuilder/SiblingIOEHandle.java
--- a/jdk/src/windows/classes/java/lang/ProcessImpl.java	Thu Aug 08 13:51:55 2013 +0900
+++ b/jdk/src/windows/classes/java/lang/ProcessImpl.java	Thu Aug 08 09:16:16 2013 +0400
@@ -491,8 +491,10 @@
 
     /**
      * Create a process using the win32 function CreateProcess.
+     * The method is synchronized due to MS kb315939 problem.
+     * All native handles should restore the inherit flag at the end of call.
      *
-     * @param cmdstr the Windows commandline
+     * @param cmdstr the Windows command line
      * @param envblock NUL-separated, double-NUL-terminated list of
      *        environment strings in VAR=VALUE form
      * @param dir the working directory of the process, or null if
@@ -508,7 +510,7 @@
      * @param redirectErrorStream redirectErrorStream attribute
      * @return the native subprocess HANDLE returned by CreateProcess
      */
-    private static native long create(String cmdstr,
+    private static synchronized native long create(String cmdstr,
                                       String envblock,
                                       String dir,
                                       long[] stdHandles,
--- a/jdk/src/windows/native/java/lang/ProcessImpl_md.c	Thu Aug 08 13:51:55 2013 +0900
+++ b/jdk/src/windows/native/java/lang/ProcessImpl_md.c	Thu Aug 08 09:16:16 2013 +0400
@@ -113,6 +113,233 @@
         CloseHandle(handle);
 }
 
+static BOOL hasInheritFlag(HANDLE handle)
+{
+    DWORD mask;
+    if (GetHandleInformation(handle, &mask)) {
+        return mask & HANDLE_FLAG_INHERIT;
+    }
+    return FALSE;
+}
+
+#define HANDLE_STORAGE_SIZE 6
+#define OFFSET_READ  0
+#define OFFSET_WRITE 1
+//long signed version of INVALID_HANDLE_VALUE
+#define JAVA_INVALID_HANDLE_VALUE ((jlong) -1)
+#define OPPOSITE_END(offset) (offset==OFFSET_READ ? OFFSET_WRITE : OFFSET_READ)
+
+/* Pipe holder structure */
+typedef struct _STDHOLDER {
+    HANDLE  pipe[2];
+    int     offset;
+} STDHOLDER;
+
+/* Responsible for correct initialization of the [pHolder] structure
+   (that is used for handles recycling) if needs,
+   and appropriate setup of IOE handle [phStd] for child process based
+   on created pipe or Java handle. */
+static BOOL initHolder(
+    JNIEnv *env,
+    jlong *pjhandles,   /* IN OUT - the handle form Java,
+                                    that can be a file, console or undefined */
+    STDHOLDER *pHolder, /* OUT    - initialized structure that holds pipe
+                                    handles */
+    HANDLE *phStd       /* OUT    - initialized handle for child process */
+) {
+    /* Here we test the value from Java against invalid
+       handle value. We are not using INVALID_HANDLE_VALUE macro
+       due to double signed/unsigned and 32/64bit ambiguity.
+       Otherwise it will be easy to get the wrong
+       value   0x00000000FFFFFFFF
+       instead 0xFFFFFFFFFFFFFFFF. */
+    if (*pjhandles != JAVA_INVALID_HANDLE_VALUE) {
+        /* Java file or console redirection */
+        *phStd = (HANDLE) *pjhandles;
+        /* Here we set the related Java stream (Process.getXXXXStream())
+           to [ProcessBuilder.NullXXXXStream.INSTANCE] value.
+           The initial Java handle [*pjhandles] will be closed in
+           ANY case. It is not a handle leak. */
+        *pjhandles = JAVA_INVALID_HANDLE_VALUE;
+    } else {
+        /* Creation of parent-child pipe */
+        if (!CreatePipe(
+            &pHolder->pipe[OFFSET_READ],
+            &pHolder->pipe[OFFSET_WRITE],
+            NULL, /* we would like to inherit
+                     default process access,
+                     instead of 'Everybody' access */
+            PIPE_SIZE))
+        {
+            win32Error(env, L"CreatePipe");
+            return FALSE;
+        } else {
+            /* [thisProcessEnd] has no the inherit flag because
+               the [lpPipeAttributes] param of [CreatePipe]
+               had the NULL value. */
+            HANDLE thisProcessEnd = pHolder->pipe[OPPOSITE_END(pHolder->offset)];
+            *phStd = pHolder->pipe[pHolder->offset];
+            *pjhandles = (jlong) thisProcessEnd;
+        }
+    }
+    /* Pipe handle will be closed in the [releaseHolder] call,
+       file handle will be closed in Java.
+       The long-live handle need to restore the inherit flag,
+       we do it later in the [prepareIOEHandleState] call. */
+    SetHandleInformation(
+        *phStd,
+        HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+    return TRUE;
+}
+
+/* Smart recycling of pipe handles in [pHolder]. For the failed
+   create process attempts, both ends of pipe need to be released.
+   The [complete] has the [TRUE] value in the failed attempt. */
+static void releaseHolder(BOOL complete, STDHOLDER *pHolder) {
+    closeSafely(pHolder->pipe[pHolder->offset]);
+    if (complete) {
+        /* Error occur, close this process pipe end */
+        closeSafely(pHolder->pipe[OPPOSITE_END(pHolder->offset)]);
+    }
+}
+
+/* Stores and drops the inherit flag of handles that should not
+   be shared with the child process by default, but can hold the
+   inherit flag due to MS process birth specific. */
+static void prepareIOEHandleState(
+    HANDLE *stdIOE,
+    BOOL *inherit)
+{
+    int i;
+    for (i = 0; i < HANDLE_STORAGE_SIZE; ++i) {
+        HANDLE hstd = stdIOE[i];
+        if (INVALID_HANDLE_VALUE != hstd && hasInheritFlag(hstd)) {
+            /* FALSE by default */
+            inherit[i] = TRUE;
+            /* Java does not need implicit inheritance for IOE handles,
+               so we drop inherit flag that probably was installed by
+               previous CreateProcess call that launched current process.
+               We will return the handle state back after CreateProcess call.
+               By clearing inherit flag we prevent "greedy grandchild" birth.
+               The explicit inheritance for child process IOE handles is
+               implemented in the [initHolder] call. */
+            SetHandleInformation(hstd, HANDLE_FLAG_INHERIT, 0);
+        }
+    }
+}
+
+/* Restores the inheritance flag of handles from stored values. */
+static void restoreIOEHandleState(
+    const HANDLE *stdIOE,
+    const BOOL *inherit)
+{
+    /* The set of current process standard IOE handles and
+       the set of child process IOE handles can intersect.
+       To restore the inherit flag right, we use backward
+       array iteration. */
+    int i;
+    for (i = HANDLE_STORAGE_SIZE - 1; i >= 0; --i)
+        if (INVALID_HANDLE_VALUE != stdIOE[i]) {
+           /* Restore inherit flag for any case.
+              The handle can be changed by explicit inheritance.*/
+            SetHandleInformation(stdIOE[i],
+                HANDLE_FLAG_INHERIT,
+                inherit[i] ? HANDLE_FLAG_INHERIT : 0);
+        }
+}
+
+/* Please, read about the MS inheritance problem
+   http://support.microsoft.com/kb/315939
+   and critical section/synchronized block solution. */
+static jlong processCreate(
+    JNIEnv *env,
+    const jchar *pcmd,
+    const jchar *penvBlock,
+    const jchar *pdir,
+    jlong *handles,
+    jboolean redirectErrorStream)
+{
+    jlong ret = 0L;
+    STARTUPINFOW si = {sizeof(si)};
+
+    /* Handles for which the inheritance flag must be restored. */
+    HANDLE stdIOE[HANDLE_STORAGE_SIZE] = {
+        /* Current process standard IOE handles: JDK-7147084 */
+        INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
+        /* Child process IOE handles: JDK-6921885 */
+        (HANDLE)handles[0], (HANDLE)handles[1], (HANDLE)handles[2]};
+    BOOL inherit[HANDLE_STORAGE_SIZE] = {
+        FALSE, FALSE, FALSE,
+        FALSE, FALSE, FALSE};
+
+    {
+        /* Extraction of current process standard IOE handles */
+        DWORD idsIOE[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
+        int i;
+        for (i = 0; i < 3; ++i)
+            /* Should not be closed by CloseHandle! */
+            stdIOE[i] = GetStdHandle(idsIOE[i]);
+    }
+
+    prepareIOEHandleState(stdIOE, inherit);
+    {
+        /* Input */
+        STDHOLDER holderIn = {{INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, OFFSET_READ};
+        if (initHolder(env, &handles[0], &holderIn, &si.hStdInput)) {
+
+            /* Output */
+            STDHOLDER holderOut = {{INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, OFFSET_WRITE};
+            if (initHolder(env, &handles[1], &holderOut, &si.hStdOutput)) {
+
+                /* Error */
+                STDHOLDER holderErr = {{INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, OFFSET_WRITE};
+                BOOL success;
+                if (redirectErrorStream) {
+                    si.hStdError = si.hStdOutput;
+                    /* Here we set the error stream to [ProcessBuilder.NullInputStream.INSTANCE]
+                       value. That is in accordance with Java Doc for the redirection case.
+                       The Java file for the [ handles[2] ] will be closed in ANY case. It is not
+                       a handle leak. */
+                    handles[2] = JAVA_INVALID_HANDLE_VALUE;
+                    success = TRUE;
+                } else {
+                    success = initHolder(env, &handles[2], &holderErr, &si.hStdError);
+                }
+
+                if (success) {
+                    PROCESS_INFORMATION pi;
+                    DWORD processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
+
+                    si.dwFlags = STARTF_USESTDHANDLES;
+                    if (!CreateProcessW(
+                        NULL,             /* executable name */
+                        (LPWSTR)pcmd,     /* command line */
+                        NULL,             /* process security attribute */
+                        NULL,             /* thread security attribute */
+                        TRUE,             /* inherits system handles */
+                        processFlag,      /* selected based on exe type */
+                        (LPVOID)penvBlock,/* environment block */
+                        (LPCWSTR)pdir,    /* change to the new current directory */
+                        &si,              /* (in)  startup information */
+                        &pi))             /* (out) process information */
+                    {
+                        win32Error(env, L"CreateProcess");
+                    } else {
+                        closeSafely(pi.hThread);
+                        ret = (jlong)pi.hProcess;
+                    }
+                }
+                releaseHolder(ret == 0, &holderErr);
+                releaseHolder(ret == 0, &holderOut);
+            }
+            releaseHolder(ret == 0, &holderIn);
+        }
+    }
+    restoreIOEHandleState(stdIOE, inherit);
+
+    return ret;
+}
+
 JNIEXPORT jlong JNICALL
 Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
                                   jstring cmd,
@@ -121,138 +348,35 @@
                                   jlongArray stdHandles,
                                   jboolean redirectErrorStream)
 {
-    HANDLE inRead   = INVALID_HANDLE_VALUE;
-    HANDLE inWrite  = INVALID_HANDLE_VALUE;
-    HANDLE outRead  = INVALID_HANDLE_VALUE;
-    HANDLE outWrite = INVALID_HANDLE_VALUE;
-    HANDLE errRead  = INVALID_HANDLE_VALUE;
-    HANDLE errWrite = INVALID_HANDLE_VALUE;
-    SECURITY_ATTRIBUTES sa;
-    PROCESS_INFORMATION pi;
-    STARTUPINFOW si;
-    const jchar*  pcmd = NULL;
-    const jchar*  pdir = NULL;
-    const jchar*  penvBlock = NULL;
-    jlong  *handles = NULL;
     jlong ret = 0;
-    DWORD processFlag;
-
-    assert(cmd != NULL);
-    pcmd = (*env)->GetStringChars(env, cmd, NULL);
-    if (pcmd == NULL) goto Catch;
-
-    if (dir != 0) {
-        pdir = (*env)->GetStringChars(env, dir, NULL);
-        if (pdir == NULL) goto Catch;
-    }
-    if (envBlock != NULL) {
-        penvBlock = ((*env)->GetStringChars(env, envBlock, NULL));
-        if (penvBlock == NULL) goto Catch;
-    }
-    assert(stdHandles != NULL);
-    handles = (*env)->GetLongArrayElements(env, stdHandles, NULL);
-    if (handles == NULL) goto Catch;
-
-    memset(&si, 0, sizeof(si));
-    si.cb = sizeof(si);
-    si.dwFlags = STARTF_USESTDHANDLES;
-
-    sa.nLength = sizeof(sa);
-    sa.lpSecurityDescriptor = 0;
-    sa.bInheritHandle = TRUE;
-
-    if (handles[0] != (jlong) -1) {
-        si.hStdInput = (HANDLE) handles[0];
-        handles[0] = (jlong) -1;
-    } else {
-        if (! CreatePipe(&inRead,  &inWrite,  &sa, PIPE_SIZE)) {
-            win32Error(env, L"CreatePipe");
-            goto Catch;
-        }
-        si.hStdInput = inRead;
-        SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, 0);
-        handles[0] = (jlong) inWrite;
-    }
-    SetHandleInformation(si.hStdInput,
-        HANDLE_FLAG_INHERIT,
-        HANDLE_FLAG_INHERIT);
-
-    if (handles[1] != (jlong) -1) {
-        si.hStdOutput = (HANDLE) handles[1];
-        handles[1] = (jlong) -1;
-    } else {
-        if (! CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE)) {
-            win32Error(env, L"CreatePipe");
-            goto Catch;
+    if (cmd != NULL && stdHandles != NULL) {
+        const jchar *pcmd = (*env)->GetStringChars(env, cmd, NULL);
+        if (pcmd != NULL) {
+            const jchar *penvBlock = (envBlock != NULL)
+                ? (*env)->GetStringChars(env, envBlock, NULL)
+                : NULL;
+            const jchar *pdir = (dir != NULL)
+                ? (*env)->GetStringChars(env, dir, NULL)
+                : NULL;
+            jlong *handles = (*env)->GetLongArrayElements(env, stdHandles, NULL);
+            if (handles != NULL) {
+                ret = processCreate(
+                    env,
+                    pcmd,
+                    penvBlock,
+                    pdir,
+                    handles,
+                    redirectErrorStream);
+                (*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);
+            }
+            if (pdir != NULL)
+                (*env)->ReleaseStringChars(env, dir, pdir);
+            if (penvBlock != NULL)
+                (*env)->ReleaseStringChars(env, envBlock, penvBlock);
+            (*env)->ReleaseStringChars(env, cmd, pcmd);
         }
-        si.hStdOutput = outWrite;
-        SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, 0);
-        handles[1] = (jlong) outRead;
     }
-    SetHandleInformation(si.hStdOutput,
-        HANDLE_FLAG_INHERIT,
-        HANDLE_FLAG_INHERIT);
-
-    if (redirectErrorStream) {
-        si.hStdError = si.hStdOutput;
-        handles[2] = (jlong) -1;
-    } else if (handles[2] != (jlong) -1) {
-        si.hStdError = (HANDLE) handles[2];
-        handles[2] = (jlong) -1;
-    } else {
-        if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) {
-            win32Error(env, L"CreatePipe");
-            goto Catch;
-        }
-        si.hStdError = errWrite;
-        SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, 0);
-        handles[2] = (jlong) errRead;
-    }
-    SetHandleInformation(si.hStdError,
-        HANDLE_FLAG_INHERIT,
-        HANDLE_FLAG_INHERIT);
-
-    processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
-    ret = CreateProcessW(0,                /* executable name */
-                         (LPWSTR)pcmd,     /* command line */
-                         0,                /* process security attribute */
-                         0,                /* thread security attribute */
-                         TRUE,             /* inherits system handles */
-                         processFlag,      /* selected based on exe type */
-                         (LPVOID)penvBlock,/* environment block */
-                         (LPCWSTR)pdir,    /* change to the new current directory */
-                         &si,              /* (in)  startup information */
-                         &pi);             /* (out) process information */
-    if (!ret) {
-        win32Error(env, L"CreateProcess");
-        goto Catch;
-    }
-
-    CloseHandle(pi.hThread);
-    ret = (jlong)pi.hProcess;
-
- Finally:
-    /* Always clean up the child's side of the pipes */
-    closeSafely(inRead);
-    closeSafely(outWrite);
-    closeSafely(errWrite);
-
-    if (pcmd != NULL)
-        (*env)->ReleaseStringChars(env, cmd, pcmd);
-    if (pdir != NULL)
-        (*env)->ReleaseStringChars(env, dir, pdir);
-    if (penvBlock != NULL)
-        (*env)->ReleaseStringChars(env, envBlock, penvBlock);
-    if (handles != NULL)
-        (*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);
     return ret;
-
- Catch:
-    /* Clean up the parent's side of the pipes in case of failure only */
-    closeSafely(inWrite);
-    closeSafely(outRead);
-    closeSafely(errRead);
-    goto Finally;
 }
 
 JNIEXPORT jint JNICALL
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessBuilder/InheritIOEHandle.java	Thu Aug 08 09:16:16 2013 +0400
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2013, 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 7147084
+ * @run main/othervm InheritIOEHandle
+ * @summary inherit IOE handles and MS CreateProcess limitations (kb315939)
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class InheritIOEHandle {
+    private static enum APP {
+        B, C;
+    }
+    private static File stopC = new File(".\\StopC.txt");
+    private static String SIGNAL = "After call child process";
+    private static String JAVA_EXE = System.getProperty("java.home")
+        + File.separator + "bin"
+        + File.separator + "java";
+
+    private static String[] getCommandArray(String processName) {
+        String[] cmdArray = {
+            JAVA_EXE,
+            "-cp",
+            System.getProperty("java.class.path"),
+            InheritIOEHandle.class.getName(),
+            processName
+        };
+        return cmdArray;
+    }
+
+    public static void main(String[] args) throws Exception {
+        if (!System.getProperty("os.name").startsWith("Windows")) {
+            return;
+        }
+
+        if (args.length > 0) {
+            APP app = APP.valueOf(args[0]);
+            switch (app) {
+            case B:
+                performB();
+                break;
+            case C:
+                performC();
+                break;
+            }
+            return;
+        }
+        performA();
+    }
+
+    private static void performA() {
+        try {
+            stopC.delete();
+
+            ProcessBuilder builder = new ProcessBuilder(
+                    getCommandArray(APP.B.name()));
+            builder.redirectErrorStream(true);
+
+            Process process = builder.start();
+
+            process.getOutputStream().close();
+            process.getErrorStream().close();
+
+            try (BufferedReader in = new BufferedReader( new InputStreamReader(
+                         process.getInputStream(), "utf-8")))
+            {
+                String result;
+                while ((result = in.readLine()) != null) {
+                    if (!SIGNAL.equals(result)) {
+                        throw new Error("Catastrophe in process B! Bad output.");
+                    }
+                }
+            }
+
+            // If JDK-7147084 is not fixed that point is unreachable.
+
+            // write signal file
+            stopC.createNewFile();
+
+            System.err.println("Read stream finished.");
+        } catch (IOException ex) {
+            throw new Error("Catastrophe in process A!", ex);
+        }
+    }
+
+    private static void performB() {
+        try {
+            ProcessBuilder builder = new ProcessBuilder(
+                    getCommandArray(APP.C.name()));
+
+            Process process = builder.start();
+
+            process.getInputStream().close();
+            process.getOutputStream().close();
+            process.getErrorStream().close();
+
+            System.out.println(SIGNAL);
+
+            // JDK-7147084 subject:
+            // Process C inherits the [System.out] handle and
+            // handle close in B does not finalize the streaming for A.
+            // (handle reference count > 1).
+        } catch (IOException ex) {
+            throw new Error("Catastrophe in process B!", ex);
+        }
+    }
+
+    private static void performC() {
+        // If JDK-7147084 is not fixed the loop is 5min long.
+        for (int i = 0; i < 5*60; ++i) {
+            try {
+                Thread.sleep(1000);
+                // check for sucess
+                if (stopC.exists())
+                    break;
+            } catch (InterruptedException ex) {
+                // that is ok. Longer sleep - better effect.
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessBuilder/SiblingIOEHandle.java	Thu Aug 08 09:16:16 2013 +0400
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2013, 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 6921885
+ * @run main/othervm SiblingIOEHandle
+ * @summary inherit IOE handles and MS CreateProcess limitations (kb315939)
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+public class SiblingIOEHandle {
+    private static enum APP {
+        B, C;
+    }
+    private static File stopC = new File(".\\StopCs.txt");
+    private static String SIGNAL = "B child reported.";
+    private static String JAVA_EXE = System.getProperty("java.home")
+        + File.separator + "bin"
+        + File.separator + "java";
+
+    private static String[] getCommandArray(String processName) {
+        String[] cmdArray = {
+            JAVA_EXE,
+            "-cp",
+            System.getProperty("java.class.path"),
+            SiblingIOEHandle.class.getName(),
+            processName
+        };
+        return cmdArray;
+    }
+
+    public static void main(String[] args) {
+        if (!System.getProperty("os.name").startsWith("Windows")) {
+            return;
+        }
+
+        if (args.length > 0) {
+            APP app = APP.valueOf(args[0]);
+            switch (app) {
+            case B:
+                performB();
+                break;
+            case C:
+                performC();
+                break;
+            }
+            return;
+        }
+        performA(true);
+        performA(false);
+    }
+
+    static boolean procClaunched = false;
+
+    private static void waitAbit() {
+        try {
+            Thread.sleep(0);
+        } catch (InterruptedException ex) {
+            // that was long enough
+        }
+    }
+    private static boolean waitBarrier(CyclicBarrier barrier) {
+        while (true) try {
+            barrier.await();
+            return true;
+        } catch (InterruptedException ex) {
+            continue;
+        } catch (BrokenBarrierException ex) {
+            ex.printStackTrace();
+            return false;
+        }
+    }
+
+    private static void performA(boolean fileOut) {
+        try {
+            stopC.delete();
+            ProcessBuilder builderB = new ProcessBuilder(
+                    getCommandArray(APP.B.name()));
+
+            File outB = null;
+            if (fileOut) {
+                outB = new File("outB.txt");
+                builderB.redirectOutput(outB);
+            }
+            builderB.redirectErrorStream(true);
+
+            final CyclicBarrier barrier = new CyclicBarrier(2);
+            Thread procCRunner = new Thread(new Runnable() {
+                @Override public void run() {
+                    try {
+                        if (waitBarrier(barrier)) {
+                            waitAbit();
+                            // Run process C next to B ASAP to make an attempt
+                            // to capture the B-process IOE handles in C process.
+                            Runtime.getRuntime().exec(getCommandArray(APP.C.name()));
+                            procClaunched = true;
+                        }
+                    } catch (IOException ex) {
+                        ex.printStackTrace();
+                    }
+                }
+            });
+            procCRunner.start();
+
+
+            if (!waitBarrier(barrier)) {
+                throw new Error("Catastrophe in process A! Synchronization failed.");
+            }
+            // Run process B first.
+            Process processB = builderB.start();
+
+            while (true) try {
+                procCRunner.join();
+                break;
+            } catch (InterruptedException ex) {
+                continue;
+            }
+
+            if (!procClaunched) {
+                throw new Error("Catastrophe in process A! C was not launched.");
+            }
+
+            processB.getOutputStream().close();
+            processB.getErrorStream().close();
+
+            if (fileOut) {
+                try {
+                    processB.waitFor();
+                } catch (InterruptedException ex) {
+                    throw new Error("Catastrophe in process B! B hung up.");
+                }
+                System.err.println("Trying to delete [outB.txt].");
+                if (!outB.delete()) {
+                    throw new Error("Greedy brother C deadlock! File share.");
+                }
+                System.err.println("Succeeded in delete [outB.txt].");
+            } else {
+                System.err.println("Read stream start.");
+                try (BufferedReader in = new BufferedReader( new InputStreamReader(
+                             processB.getInputStream(), "utf-8")))
+                {
+                    String result;
+                    while ((result = in.readLine()) != null) {
+                        if (!SIGNAL.equals(result)) {
+                            throw new Error("Catastrophe in process B! Bad output.");
+                        }
+                    }
+                }
+                System.err.println("Read stream finished.");
+            }
+            // If JDK-6921885 is not fixed that point is unreachable.
+            // Test timeout exception.
+
+            // write signal file to stop C process.
+            stopC.createNewFile();
+        } catch (IOException ex) {
+            throw new Error("Catastrophe in process A!", ex);
+        }
+    }
+
+    private static void performB() {
+        System.out.println(SIGNAL);
+    }
+
+    private static void performC() {
+        // If JDK-7147084 is not fixed the loop is 5min long.
+        for (int i = 0; i < 5*60; ++i) {
+            try {
+                Thread.sleep(1000);
+                // check for sucess
+                if (stopC.exists())
+                    break;
+            } catch (InterruptedException ex) {
+                // that is ok. Longer sleep - better effect.
+            }
+        }
+    }
+}