1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 6921885
  27  * @run main/othervm SiblingIOEHandle
  28  * @summary inherit IOE handles and MS CreateProcess limitations (kb315939)
  29  */
  30 
  31 import java.io.BufferedReader;
  32 import java.io.File;
  33 import java.io.IOException;
  34 import java.io.InputStreamReader;
  35 import java.util.concurrent.BrokenBarrierException;
  36 import java.util.concurrent.CyclicBarrier;
  37 import java.util.logging.Level;
  38 import java.util.logging.Logger;
  39 
  40 public class SiblingIOEHandle {
  41     private static enum APP {
  42         B, C;
  43     }
  44     private static long LONG_SLEEP = 10000;
  45     private static String SIGNAL = "B child reported.";
  46     private static String JAVA_EXE = System.getProperty("java.home")
  47         + File.separator + "bin"
  48         + File.separator + "java";
  49 
  50     private static String[] getCommandArray(String processName) {
  51         String[] cmdArray = {
  52             JAVA_EXE,
  53             "-cp",
  54             System.getProperty("java.class.path"),
  55             SiblingIOEHandle.class.getName(),
  56             processName
  57         };
  58         return cmdArray;
  59     }
  60 
  61     public static void main(String[] args) {
  62         if (!System.getProperty("os.name").startsWith("Windows")) {
  63             return;
  64         }
  65 
  66         if (args.length > 0) {
  67             APP app = APP.valueOf(args[0]);
  68             switch (app) {
  69             case B:
  70                 performB();
  71                 break;
  72             case C:
  73                 performC();
  74                 break;
  75             }
  76             return;
  77         }
  78         performA(true);
  79         performA(false);
  80     }
  81 
  82     static boolean procClaunched = false;
  83 
  84     private static void waitAbit() {
  85         try {
  86             Thread.sleep(0);
  87         } catch (InterruptedException ex) {
  88             // that was long enough
  89         }
  90     }
  91     private static boolean waitBarrier(CyclicBarrier barrier) {
  92         while (true) try {
  93             barrier.await();
  94             return true;
  95         } catch (InterruptedException ex) {
  96             continue;
  97         } catch (BrokenBarrierException ex) {
  98             ex.printStackTrace();
  99             return false;
 100         }
 101     }
 102 
 103     private static void performA(boolean fileOut) {
 104         try {
 105             ProcessBuilder builderB = new ProcessBuilder(
 106                     getCommandArray(APP.B.name()));
 107 
 108             File outB = null;
 109             if (fileOut) {
 110                 outB = new File("outB.txt");
 111                 builderB.redirectOutput(outB);
 112             }
 113             builderB.redirectErrorStream(true);
 114 
 115             final CyclicBarrier barrier = new CyclicBarrier(2);
 116             Thread procCRunner = new Thread(new Runnable() {
 117                 @Override public void run() {
 118                     try {
 119                         if (waitBarrier(barrier)) {
 120                             waitAbit();
 121                             // Run process C next to B ASAP to make an attempt
 122                             // to capture the B-process IOE handles in C process.
 123                             Runtime.getRuntime().exec(getCommandArray(APP.C.name()));
 124                             procClaunched = true;
 125                         }
 126                     } catch (IOException ex) {
 127                         ex.printStackTrace();
 128                     }
 129                 }
 130             });
 131             procCRunner.start();
 132 
 133 
 134             if (!waitBarrier(barrier)) {
 135                 throw new Error("Catastrophe in process A! Synchronization failed.");
 136             }
 137             // Run process B first.
 138             Process processB = builderB.start();
 139 
 140             while (true) try {
 141                 procCRunner.join();
 142                 break;
 143             } catch (InterruptedException ex) {
 144                 continue;
 145             }
 146 
 147             if (!procClaunched) {
 148                 throw new Error("Catastrophe in process A! C was not launched.");
 149             }
 150 
 151             processB.getOutputStream().close();
 152             processB.getErrorStream().close();
 153 
 154             long startTime = System.currentTimeMillis();
 155 
 156             if (fileOut) {
 157                 try {
 158                     processB.waitFor();
 159                 } catch (InterruptedException ex) {
 160                     throw new Error("Catastrophe in process B! B hung up.");
 161                 }
 162                 System.err.println("Trying to delete [outB.txt].");
 163                 if (!outB.delete()) {
 164                     throw new Error("Greedy brother C deadlock! File share.");
 165                 }
 166                 System.err.println("Succeeded in delete [outB.txt].");
 167             } else {
 168                 System.err.println("Read stream start.");
 169                 try (BufferedReader in = new BufferedReader( new InputStreamReader(
 170                              processB.getInputStream(), "utf-8")))
 171                 {
 172                     String result;
 173                     while ((result = in.readLine()) != null) {
 174                         if (!SIGNAL.equals(result)) {
 175                             throw new Error("Catastrophe in process B! Bad output.");
 176                         }
 177                     }
 178                 }
 179                 System.err.println("Read stream finished.");
 180             }
 181 
 182             // If JDK-6921885 is not fixed that point is unreachable
 183             // in at least 10 seconds.
 184             long testTime = System.currentTimeMillis() - startTime;
 185             if (testTime >= LONG_SLEEP) {
 186                 throw new Error("Greedy brother C deadlock! Time: " + testTime + "ms is too long.");
 187             }
 188         } catch (IOException ex) {
 189             throw new Error("Catastrophe in process A!", ex);
 190         }
 191     }
 192 
 193     private static void performB() {
 194         System.out.println(SIGNAL);
 195     }
 196 
 197     private static void performC() {
 198         while (true) {
 199             try {
 200                 Thread.sleep(LONG_SLEEP);
 201                 break;
 202             } catch (InterruptedException ex) {
 203                 // that is ok. Longer sleep - better effect.
 204             }
 205         }
 206     }
 207 }