56 57 public boolean timedOut() { 58 return timedOut; 59 } 60 } 61 62 private static boolean hang1() throws IOException, InterruptedException { 63 // Time out was reproducible on Solaris 50% of the time; 64 // on Linux 80% of the time. 65 // 66 // Scenario: After fork(), parent executes and closes write end of child's stdin. 67 // This causes child to retain a write end of the same pipe. 68 // Thus the child will never see an EOF on its stdin, and will hang. 69 Runtime rt = Runtime.getRuntime(); 70 // Increasing the iteration count makes the bug more 71 // reproducible not only for the obvious reason, but also for 72 // the subtle reason that it makes reading /proc/getppid()/fd 73 // slower, making the child more likely to win the race! 74 int iterations = 20; 75 int timeout = 30; 76 String[] catArgs = new String[] {"/bin/cat"}; 77 String[] sleepArgs = new String[] {"/bin/sleep", 78 String.valueOf(timeout+1)}; 79 Process[] cats = new Process[iterations]; 80 Process[] sleeps = new Process[iterations]; 81 Timer timer = new Timer(true); 82 TimeoutTask catExecutioner = new TimeoutTask(cats); 83 timer.schedule(catExecutioner, timeout * 1000); 84 85 for (int i = 0; i < cats.length; ++i) { 86 cats[i] = rt.exec(catArgs); 87 java.io.OutputStream s = cats[i].getOutputStream(); 88 Process sleep = rt.exec(sleepArgs); 89 s.close(); // race condition here 90 sleeps[i] = sleep; 91 } 92 93 for (int i = 0; i < cats.length; ++i) 94 cats[i].waitFor(); // hangs? 95 96 timer.cancel(); 97 109 // Time out was reproducible on Linux 80% of the time; 110 // never on Solaris because of explicit close in Solaris-specific code. 111 112 // Scenario: After fork(), the parent naturally closes the 113 // child's stdout write end. The child dup2's the write end 114 // of its stdout onto fd 1. On Linux, it fails to explicitly 115 // close the original fd, and because of the parent's close() 116 // of the fd, the child retains it. The child thus ends up 117 // with two copies of its stdout. Thus closing one of those 118 // write fds does not have the desired effect of causing an 119 // EOF on the parent's read end of that pipe. 120 Runtime rt = Runtime.getRuntime(); 121 int iterations = 10; 122 Timer timer = new Timer(true); 123 int timeout = 30; 124 Process[] backgroundSleepers = new Process[iterations]; 125 TimeoutTask sleeperExecutioner = new TimeoutTask(backgroundSleepers); 126 timer.schedule(sleeperExecutioner, timeout * 1000); 127 byte[] buffer = new byte[10]; 128 String[] args = 129 new String[] {"/bin/sh", "-c", 130 "exec sleep " + (timeout+1) + " >/dev/null"}; 131 132 for (int i = 0; 133 i < backgroundSleepers.length && !sleeperExecutioner.timedOut(); 134 ++i) { 135 backgroundSleepers[i] = rt.exec(args); // race condition here 136 try { 137 // should get immediate EOF, but might hang 138 if (backgroundSleepers[i].getInputStream().read() != -1) 139 throw new Exception("Expected EOF, got a byte"); 140 } catch (IOException e) { 141 // Stream closed by sleeperExecutioner 142 break; 143 } 144 } 145 146 timer.cancel(); 147 148 destroy(backgroundSleepers); 149 150 if (sleeperExecutioner.timedOut()) | 56 57 public boolean timedOut() { 58 return timedOut; 59 } 60 } 61 62 private static boolean hang1() throws IOException, InterruptedException { 63 // Time out was reproducible on Solaris 50% of the time; 64 // on Linux 80% of the time. 65 // 66 // Scenario: After fork(), parent executes and closes write end of child's stdin. 67 // This causes child to retain a write end of the same pipe. 68 // Thus the child will never see an EOF on its stdin, and will hang. 69 Runtime rt = Runtime.getRuntime(); 70 // Increasing the iteration count makes the bug more 71 // reproducible not only for the obvious reason, but also for 72 // the subtle reason that it makes reading /proc/getppid()/fd 73 // slower, making the child more likely to win the race! 74 int iterations = 20; 75 int timeout = 30; 76 String[] catArgs = new String[] {UnixCommands.cat()}; 77 String[] sleepArgs = new String[] {UnixCommands.sleep(), 78 String.valueOf(timeout+1)}; 79 Process[] cats = new Process[iterations]; 80 Process[] sleeps = new Process[iterations]; 81 Timer timer = new Timer(true); 82 TimeoutTask catExecutioner = new TimeoutTask(cats); 83 timer.schedule(catExecutioner, timeout * 1000); 84 85 for (int i = 0; i < cats.length; ++i) { 86 cats[i] = rt.exec(catArgs); 87 java.io.OutputStream s = cats[i].getOutputStream(); 88 Process sleep = rt.exec(sleepArgs); 89 s.close(); // race condition here 90 sleeps[i] = sleep; 91 } 92 93 for (int i = 0; i < cats.length; ++i) 94 cats[i].waitFor(); // hangs? 95 96 timer.cancel(); 97 109 // Time out was reproducible on Linux 80% of the time; 110 // never on Solaris because of explicit close in Solaris-specific code. 111 112 // Scenario: After fork(), the parent naturally closes the 113 // child's stdout write end. The child dup2's the write end 114 // of its stdout onto fd 1. On Linux, it fails to explicitly 115 // close the original fd, and because of the parent's close() 116 // of the fd, the child retains it. The child thus ends up 117 // with two copies of its stdout. Thus closing one of those 118 // write fds does not have the desired effect of causing an 119 // EOF on the parent's read end of that pipe. 120 Runtime rt = Runtime.getRuntime(); 121 int iterations = 10; 122 Timer timer = new Timer(true); 123 int timeout = 30; 124 Process[] backgroundSleepers = new Process[iterations]; 125 TimeoutTask sleeperExecutioner = new TimeoutTask(backgroundSleepers); 126 timer.schedule(sleeperExecutioner, timeout * 1000); 127 byte[] buffer = new byte[10]; 128 String[] args = 129 new String[] {UnixCommands.sh(), "-c", 130 "exec " + UnixCommands.sleep() + " " 131 + (timeout+1) + " >/dev/null"}; 132 133 for (int i = 0; 134 i < backgroundSleepers.length && !sleeperExecutioner.timedOut(); 135 ++i) { 136 backgroundSleepers[i] = rt.exec(args); // race condition here 137 try { 138 // should get immediate EOF, but might hang 139 if (backgroundSleepers[i].getInputStream().read() != -1) 140 throw new Exception("Expected EOF, got a byte"); 141 } catch (IOException e) { 142 // Stream closed by sleeperExecutioner 143 break; 144 } 145 } 146 147 timer.cancel(); 148 149 destroy(backgroundSleepers); 150 151 if (sleeperExecutioner.timedOut()) |