Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/java/lang/Runtime/exec/SleepyCat.java
47216 views
/*1* Copyright (c) 2003, 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/* @test24@bug 4843136 476338425@summary Various race conditions caused exec'ed processes to have26extra unused file descriptors, which caused hard-to-reproduce hangs.27@author Martin Buchholz28*/2930import java.util.Timer;31import java.util.TimerTask;32import java.io.IOException;3334public class SleepyCat {3536private static void destroy (Process[] deathRow) {37for (int i = 0; i < deathRow.length; ++i)38if (deathRow[i] != null)39deathRow[i].destroy();40}4142static class TimeoutTask extends TimerTask {43private Process[] deathRow;44private boolean timedOut;4546TimeoutTask (Process[] deathRow) {47this.deathRow = deathRow;48this.timedOut = false;49}5051public void run() {52timedOut = true;53destroy(deathRow);54}5556public boolean timedOut() {57return timedOut;58}59}6061private static boolean hang1() throws IOException, InterruptedException {62// Time out was reproducible on Solaris 50% of the time;63// on Linux 80% of the time.64//65// Scenario: After fork(), parent executes and closes write end of child's stdin.66// This causes child to retain a write end of the same pipe.67// Thus the child will never see an EOF on its stdin, and will hang.68Runtime rt = Runtime.getRuntime();69// Increasing the iteration count makes the bug more70// reproducible not only for the obvious reason, but also for71// the subtle reason that it makes reading /proc/getppid()/fd72// slower, making the child more likely to win the race!73int iterations = 20;74int timeout = 30;75String[] catArgs = new String[] {"/bin/cat"};76String[] sleepArgs = new String[] {"/bin/sleep",77String.valueOf(timeout+1)};78Process[] cats = new Process[iterations];79Process[] sleeps = new Process[iterations];80Timer timer = new Timer(true);81TimeoutTask catExecutioner = new TimeoutTask(cats);82timer.schedule(catExecutioner, timeout * 1000);8384for (int i = 0; i < cats.length; ++i) {85cats[i] = rt.exec(catArgs);86java.io.OutputStream s = cats[i].getOutputStream();87Process sleep = rt.exec(sleepArgs);88s.close(); // race condition here89sleeps[i] = sleep;90}9192for (int i = 0; i < cats.length; ++i)93cats[i].waitFor(); // hangs?9495timer.cancel();9697destroy(sleeps);9899if (catExecutioner.timedOut())100System.out.println("Child process has a hidden writable pipe fd for its stdin.");101return catExecutioner.timedOut();102}103104private static boolean hang2() throws Exception {105// Inspired by the imaginative test case for106// 4850368 (process) getInputStream() attaches to forked background processes (Linux)107108// Time out was reproducible on Linux 80% of the time;109// never on Solaris because of explicit close in Solaris-specific code.110111// Scenario: After fork(), the parent naturally closes the112// child's stdout write end. The child dup2's the write end113// of its stdout onto fd 1. On Linux, it fails to explicitly114// close the original fd, and because of the parent's close()115// of the fd, the child retains it. The child thus ends up116// with two copies of its stdout. Thus closing one of those117// write fds does not have the desired effect of causing an118// EOF on the parent's read end of that pipe.119Runtime rt = Runtime.getRuntime();120int iterations = 10;121Timer timer = new Timer(true);122int timeout = 30;123Process[] backgroundSleepers = new Process[iterations];124TimeoutTask sleeperExecutioner = new TimeoutTask(backgroundSleepers);125timer.schedule(sleeperExecutioner, timeout * 1000);126byte[] buffer = new byte[10];127String[] args =128new String[] {"/bin/sh", "-c",129"exec sleep " + (timeout+1) + " >/dev/null"};130131for (int i = 0;132i < backgroundSleepers.length && !sleeperExecutioner.timedOut();133++i) {134backgroundSleepers[i] = rt.exec(args); // race condition here135try {136// should get immediate EOF, but might hang137if (backgroundSleepers[i].getInputStream().read() != -1)138throw new Exception("Expected EOF, got a byte");139} catch (IOException e) {140// Stream closed by sleeperExecutioner141break;142}143}144145timer.cancel();146147destroy(backgroundSleepers);148149if (sleeperExecutioner.timedOut())150System.out.println("Child process has two (should be one) writable pipe fds for its stdout.");151return sleeperExecutioner.timedOut();152}153154public static void main (String[] args) throws Exception {155try {156if (hang1() | hang2())157throw new Exception("Read from closed pipe hangs");158} catch (IOException e) {159// We will get here on non-Posix systems,160// which don't have cat and sleep and sh.161}162}163}164165166