test/java/lang/ProcessBuilder/CloseRace.java

Print this page




  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 8024521
  27  * @summary Closing ProcessPipeInputStream at the time the process exits is racy
  28  *          and leads to data corruption. Run this test manually (as
  29  *          an ordinary java program) with  -Xmx8M  to repro bug 8024521.
  30  * @run main/othervm -Xmx8M -Dtest.duration=2 CloseRace
  31  */
  32 
  33 import java.io.*;
  34 import java.util.ArrayList;
  35 import java.util.List;


  36 
  37 public class CloseRace {
  38     private static final String BIG_FILE = "bigfile";
  39 
  40     private static final int[] procFDs = new int[6];
  41 
  42     /** default value sufficient to repro bug 8024521. */
  43     private static final int testDurationSeconds
  44         = Integer.getInteger("test.duration", 600);
  45 



  46     static boolean fdInUse(int i) {
  47         return new File("/proc/self/fd/" + i).exists();
  48     }
  49 
  50     static boolean[] procFDsInUse() {
  51         boolean[] inUse = new boolean[procFDs.length];
  52         for (int i = 0; i < procFDs.length; i++)
  53             inUse[i] = fdInUse(procFDs[i]);
  54         return inUse;
  55     }
  56 
  57     static int count(boolean[] bits) {
  58         int count = 0;
  59         for (int i = 0; i < bits.length; i++)
  60             count += bits[i] ? 1 : 0;
  61         return count;
  62     }
  63 












  64     public static void main(String args[]) throws Exception {
  65         if (!(new File("/proc/self/fd").isDirectory()))
  66             return;
  67 
  68         // Catch Errors from process reaper
  69         Thread.setDefaultUncaughtExceptionHandler
  70             ((t, e) -> { e.printStackTrace(); System.exit(1); });
  71 
  72         try (RandomAccessFile f = new RandomAccessFile(BIG_FILE, "rw")) {
  73             f.setLength(Runtime.getRuntime().maxMemory()); // provoke OOME
  74         }
  75 
  76         for (int i = 0, j = 0; j < procFDs.length; i++)
  77             if (!fdInUse(i))
  78                 procFDs[j++] = i;
  79 
  80         Thread[] threads = {
  81             new Thread(new OpenLoop()),
  82             new Thread(new ExecLoop()),
  83         };
  84         for (Thread thread : threads)
  85             thread.start();
  86 

  87         Thread.sleep(testDurationSeconds * 1000);
  88 
  89         for (Thread thread : threads)
  90             thread.interrupt();
  91         for (Thread thread : threads)
  92             thread.join();







  93     }
  94 
  95     static class OpenLoop implements Runnable {
  96         public void run() {

  97             while (!Thread.interrupted()) {
  98                 try {
  99                     // wait for ExecLoop to finish creating process
 100                     do {} while (count(procFDsInUse()) != 3);



 101                     List<InputStream> iss = new ArrayList<>(4);
 102 
 103                     // eat up three "holes" (closed ends of pipe fd pairs)
 104                     for (int i = 0; i < 3; i++)
 105                         iss.add(new FileInputStream(BIG_FILE));
 106                     do {} while (count(procFDsInUse()) == procFDs.length);



 107                     // hopefully this will racily occupy empty fd slot
 108                     iss.add(new FileInputStream(BIG_FILE));
 109                     Thread.sleep(1); // Widen race window
 110                     for (InputStream is : iss)
 111                         is.close();
 112                 } catch (InterruptedException e) {
 113                     break;
 114                 } catch (Exception e) {
 115                     throw new Error(e);
 116                 }
 117             }
 118         }
 119     }
 120 
 121     static class ExecLoop implements Runnable {
 122         public void run() {

 123             ProcessBuilder builder = new ProcessBuilder("/bin/true");
 124             while (!Thread.interrupted()) {
 125                 try {
 126                     // wait for OpenLoop to finish
 127                     do {} while (count(procFDsInUse()) > 0);



 128                     Process process = builder.start();
 129                     InputStream is = process.getInputStream();
 130                     process.waitFor();
 131                     is.close();
 132                 } catch (InterruptedException e) {
 133                     break;
 134                 } catch (Exception e) {
 135                     throw new Error(e);
 136                 }
 137             }
 138         }
 139     }
 140 }


  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 8024521
  27  * @summary Closing ProcessPipeInputStream at the time the process exits is racy
  28  *          and leads to data corruption. Run this test manually (as
  29  *          an ordinary java program) with  -Xmx8M  to repro bug 8024521.
  30  * @run main/othervm -Xmx8M -Dtest.duration=2 CloseRace
  31  */
  32 
  33 import java.io.*;
  34 import java.util.ArrayList;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.concurrent.CountDownLatch;
  38 
  39 public class CloseRace {
  40     private static final String BIG_FILE = "bigfile";
  41 
  42     private static final int[] procFDs = new int[6];
  43 
  44     /** default value sufficient to repro bug 8024521. */
  45     private static final int testDurationSeconds
  46         = Integer.getInteger("test.duration", 600);
  47 
  48     private static final CountDownLatch threadsStarted
  49         = new CountDownLatch(2);
  50 
  51     static boolean fdInUse(int i) {
  52         return new File("/proc/self/fd/" + i).exists();
  53     }
  54 
  55     static boolean[] procFDsInUse() {
  56         boolean[] inUse = new boolean[procFDs.length];
  57         for (int i = 0; i < procFDs.length; i++)
  58             inUse[i] = fdInUse(procFDs[i]);
  59         return inUse;
  60     }
  61 
  62     static int count(boolean[] bits) {
  63         int count = 0;
  64         for (int i = 0; i < bits.length; i++)
  65             count += bits[i] ? 1 : 0;
  66         return count;
  67     }
  68 
  69     static void dumpAllStacks() {
  70         System.err.println("Start of dump");
  71         final Map<Thread, StackTraceElement[]> allStackTraces
  72                 = Thread.getAllStackTraces();
  73         for (Thread thread : allStackTraces.keySet()) {
  74             System.err.println("Thread " + thread.getName());
  75             for (StackTraceElement element : allStackTraces.get(thread))
  76                 System.err.println("\t" + element);
  77         }
  78         System.err.println("End of dump");
  79     }
  80 
  81     public static void main(String args[]) throws Exception {
  82         if (!(new File("/proc/self/fd").isDirectory()))
  83             return;
  84 
  85         // Catch Errors from process reaper
  86         Thread.setDefaultUncaughtExceptionHandler
  87             ((t, e) -> { e.printStackTrace(); System.exit(1); });
  88 
  89         try (RandomAccessFile f = new RandomAccessFile(BIG_FILE, "rw")) {
  90             f.setLength(Runtime.getRuntime().maxMemory()); // provoke OOME
  91         }
  92 
  93         for (int i = 0, j = 0; j < procFDs.length; i++)
  94             if (!fdInUse(i))
  95                 procFDs[j++] = i;
  96 
  97         Thread[] threads = {
  98             new Thread(new OpenLoop()),
  99             new Thread(new ExecLoop()),
 100         };
 101         for (Thread thread : threads)
 102             thread.start();
 103 
 104         threadsStarted.await();
 105         Thread.sleep(testDurationSeconds * 1000);
 106 
 107         for (Thread thread : threads)
 108             thread.interrupt();
 109         for (Thread thread : threads) {
 110             thread.join(10_000);
 111             if (thread.isAlive()) {
 112                 dumpAllStacks();
 113                 throw new Error("At least one child thread ("
 114                         + thread.getName()
 115                         + ") failed to finish gracefully");
 116             }
 117         }
 118     }
 119 
 120     static class OpenLoop implements Runnable {
 121         public void run() {
 122             threadsStarted.countDown();
 123             while (!Thread.interrupted()) {
 124                 try {
 125                     // wait for ExecLoop to finish creating process
 126                     do {
 127                         if (Thread.interrupted())
 128                             return;
 129                     } while (count(procFDsInUse()) != 3);
 130                     List<InputStream> iss = new ArrayList<>(4);
 131 
 132                     // eat up three "holes" (closed ends of pipe fd pairs)
 133                     for (int i = 0; i < 3; i++)
 134                         iss.add(new FileInputStream(BIG_FILE));
 135                     do {
 136                         if (Thread.interrupted())
 137                             return;
 138                     } while (count(procFDsInUse()) == procFDs.length);
 139                     // hopefully this will racily occupy empty fd slot
 140                     iss.add(new FileInputStream(BIG_FILE));
 141                     Thread.sleep(1); // Widen race window
 142                     for (InputStream is : iss)
 143                         is.close();
 144                 } catch (InterruptedException e) {
 145                     break;
 146                 } catch (Exception e) {
 147                     throw new Error(e);
 148                 }
 149             }
 150         }
 151     }
 152 
 153     static class ExecLoop implements Runnable {
 154         public void run() {
 155             threadsStarted.countDown();
 156             ProcessBuilder builder = new ProcessBuilder("/bin/true");
 157             while (!Thread.interrupted()) {
 158                 try {
 159                     // wait for OpenLoop to finish
 160                     do {
 161                         if (Thread.interrupted())
 162                             return;
 163                     } while (count(procFDsInUse()) > 0);
 164                     Process process = builder.start();
 165                     InputStream is = process.getInputStream();
 166                     process.waitFor();
 167                     is.close();
 168                 } catch (InterruptedException e) {
 169                     break;
 170                 } catch (Exception e) {
 171                     throw new Error(e);
 172                 }
 173             }
 174         }
 175     }
 176 }