Path: blob/master/test/jdk/java/lang/ProcessBuilder/CloseRace.java
41149 views
/*1* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24* @test25* @bug 802452126* @summary Closing ProcessPipeInputStream at the time the process exits is racy27* and leads to data corruption. Run this test manually (as28* an ordinary java program) with -Xmx8M to repro bug 8024521.29* @run main/othervm -Xmx8M -Dtest.duration=2 CloseRace30*/3132import java.io.*;33import java.util.ArrayList;34import java.util.List;35import java.util.Map;36import java.util.concurrent.CountDownLatch;3738public class CloseRace {39private static final String BIG_FILE = "bigfile";4041private static final int[] procFDs = new int[6];4243/** default value sufficient to repro bug 8024521. */44private static final int testDurationSeconds45= Integer.getInteger("test.duration", 600);4647private static final CountDownLatch threadsStarted48= new CountDownLatch(2);4950static boolean fdInUse(int i) {51return new File("/proc/self/fd/" + i).exists();52}5354static boolean[] procFDsInUse() {55boolean[] inUse = new boolean[procFDs.length];56for (int i = 0; i < procFDs.length; i++)57inUse[i] = fdInUse(procFDs[i]);58return inUse;59}6061static int count(boolean[] bits) {62int count = 0;63for (int i = 0; i < bits.length; i++)64count += bits[i] ? 1 : 0;65return count;66}6768static void dumpAllStacks() {69System.err.println("Start of dump");70final Map<Thread, StackTraceElement[]> allStackTraces71= Thread.getAllStackTraces();72for (Thread thread : allStackTraces.keySet()) {73System.err.println("Thread " + thread.getName());74for (StackTraceElement element : allStackTraces.get(thread))75System.err.println("\t" + element);76}77System.err.println("End of dump");78}7980public static void main(String args[]) throws Exception {81if (!(new File("/proc/self/fd").isDirectory()))82return;8384// Catch Errors from process reaper85Thread.setDefaultUncaughtExceptionHandler86((t, e) -> { e.printStackTrace(); System.exit(1); });8788try (RandomAccessFile f = new RandomAccessFile(BIG_FILE, "rw")) {89f.setLength(Runtime.getRuntime().maxMemory()); // provoke OOME90}9192for (int i = 0, j = 0; j < procFDs.length; i++)93if (!fdInUse(i))94procFDs[j++] = i;9596Thread[] threads = {97new Thread(new OpenLoop()),98new Thread(new ExecLoop()),99};100for (Thread thread : threads)101thread.start();102103threadsStarted.await();104Thread.sleep(testDurationSeconds * 1000);105106for (Thread thread : threads)107thread.interrupt();108for (Thread thread : threads) {109thread.join(10_000);110if (thread.isAlive()) {111dumpAllStacks();112throw new Error("At least one child thread ("113+ thread.getName()114+ ") failed to finish gracefully");115}116}117}118119static class OpenLoop implements Runnable {120public void run() {121threadsStarted.countDown();122while (!Thread.interrupted()) {123try {124// wait for ExecLoop to finish creating process125do {126if (Thread.interrupted())127return;128} while (count(procFDsInUse()) != 3);129List<InputStream> iss = new ArrayList<>(4);130131// eat up three "holes" (closed ends of pipe fd pairs)132for (int i = 0; i < 3; i++)133iss.add(new FileInputStream(BIG_FILE));134do {135if (Thread.interrupted())136return;137} while (count(procFDsInUse()) == procFDs.length);138// hopefully this will racily occupy empty fd slot139iss.add(new FileInputStream(BIG_FILE));140Thread.sleep(1); // Widen race window141for (InputStream is : iss)142is.close();143} catch (InterruptedException e) {144break;145} catch (Exception e) {146throw new Error(e);147}148}149}150}151152static class ExecLoop implements Runnable {153public void run() {154threadsStarted.countDown();155ProcessBuilder builder = new ProcessBuilder("/bin/true");156while (!Thread.interrupted()) {157try {158// wait for OpenLoop to finish159do {160if (Thread.interrupted())161return;162} while (count(procFDsInUse()) > 0);163Process process = builder.start();164InputStream is = process.getInputStream();165process.waitFor();166is.close();167} catch (InterruptedException e) {168break;169} catch (Exception e) {170throw new Error(e);171}172}173}174}175}176177178