diff --git a/src/windows/classes/java/lang/ProcessImpl.java b/src/windows/classes/java/lang/ProcessImpl.java index 0cc9680d08931e1404fcfccbb7cd80b65c062944..4a38579a215a5e6cfffbcd53c3d144ba7ab16c13 100644 --- a/src/windows/classes/java/lang/ProcessImpl.java +++ b/src/windows/classes/java/lang/ProcessImpl.java @@ -491,8 +491,10 @@ final class ProcessImpl extends Process { /** * 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 @@ final class ProcessImpl extends Process { * @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, diff --git a/src/windows/native/java/lang/ProcessImpl_md.c b/src/windows/native/java/lang/ProcessImpl_md.c index c372013456cd4691ac19e7c9ab5cd29b1f851c0c..1806fb8193c8e553ec39c21688813f9c669c5156 100644 --- a/src/windows/native/java/lang/ProcessImpl_md.c +++ b/src/windows/native/java/lang/ProcessImpl_md.c @@ -113,146 +113,270 @@ closeSafely(HANDLE handle) CloseHandle(handle); } -JNIEXPORT jlong JNICALL -Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, - jstring cmd, - jstring envBlock, - jstring dir, - jlongArray stdHandles, - jboolean redirectErrorStream) +static BOOL hasInheritFlag(HANDLE handle) { - 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; + DWORD mask; + if (GetHandleInformation(handle, &mask)) { + return mask & HANDLE_FLAG_INHERIT; } - 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; + return FALSE; +} - if (handles[0] != (jlong) -1) { - si.hStdInput = (HANDLE) handles[0]; - handles[0] = (jlong) -1; +#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 { - if (! CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE)) { + /* 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"); - goto Catch; + 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; } - si.hStdInput = inRead; - SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, 0); - handles[0] = (jlong) inWrite; } - SetHandleInformation(si.hStdInput, - HANDLE_FLAG_INHERIT, - HANDLE_FLAG_INHERIT); + /* 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; +} - 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; +/* 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); } - 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; +} + +/* 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); } - si.hStdError = errWrite; - SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, 0); - handles[2] = (jlong) errRead; +} + +/* 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]); } - 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; + + 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); - 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 jlong JNICALL +Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, + jstring cmd, + jstring envBlock, + jstring dir, + jlongArray stdHandles, + jboolean redirectErrorStream) +{ + jlong ret = 0; + 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); + } + } + return ret; } JNIEXPORT jint JNICALL diff --git a/test/java/lang/ProcessBuilder/InheritIOEHandle.java b/test/java/lang/ProcessBuilder/InheritIOEHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..87fbf1a848241fe9bc8904af4e650e180549fd92 --- /dev/null +++ b/test/java/lang/ProcessBuilder/InheritIOEHandle.java @@ -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. + } + } + } +} diff --git a/test/java/lang/ProcessBuilder/SiblingIOEHandle.java b/test/java/lang/ProcessBuilder/SiblingIOEHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..a18e60692601a3bdc7b6705adc1b889d39800a1e --- /dev/null +++ b/test/java/lang/ProcessBuilder/SiblingIOEHandle.java @@ -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. + } + } + } +}