Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/java/lang/RuntimeTests/exec/SleepyCat.java
41153 views
1
/*
2
* Copyright (c) 2003, 2019, 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
/* @test
25
@bug 4843136 4763384 8044841
26
@summary Various race conditions caused exec'ed processes to have
27
extra unused file descriptors, which caused hard-to-reproduce hangs.
28
@author Martin Buchholz
29
*/
30
31
import java.util.Timer;
32
import java.util.TimerTask;
33
import java.io.IOException;
34
35
public class SleepyCat {
36
37
private static void destroy (Process[] deathRow) {
38
for (int i = 0; i < deathRow.length; ++i)
39
if (deathRow[i] != null)
40
deathRow[i].destroy();
41
}
42
43
static class TimeoutTask extends TimerTask {
44
private Process[] deathRow;
45
private boolean timedOut;
46
47
TimeoutTask (Process[] deathRow) {
48
this.deathRow = deathRow;
49
this.timedOut = false;
50
}
51
52
public void run() {
53
dumpState(deathRow); // before killing the processes dump all the state
54
55
timedOut = true;
56
destroy(deathRow);
57
}
58
59
public boolean timedOut() {
60
return timedOut;
61
}
62
}
63
64
/**
65
* Temporary debugging code for intermittent failures.
66
* @param pids the processes to dump status for
67
*/
68
static void dumpState(Process... pids) {
69
if (!System.getProperty("os.name").contains("SunOS")) {
70
return;
71
}
72
try {
73
String[] psArgs = {"ps", "-elf"};
74
Process ps = new ProcessBuilder(psArgs).inheritIO().start();
75
ps.waitFor();
76
String[] sfiles = {"pfiles", "self"};
77
Process fds = new ProcessBuilder(sfiles).inheritIO().start();
78
fds.waitFor();
79
80
for (Process p : pids) {
81
if (p == null)
82
continue;
83
String[] pfiles = {"pfiles", Long.toString(p.pid())};
84
fds = new ProcessBuilder(pfiles).inheritIO().start();
85
fds.waitFor();
86
String[] pstack = {"pstack", Long.toString(p.pid())};
87
fds = new ProcessBuilder(pstack).inheritIO().start();
88
fds.waitFor();
89
}
90
} catch (IOException | InterruptedException i) {
91
i.printStackTrace();
92
}
93
}
94
95
private static boolean hang1() throws IOException, InterruptedException {
96
// Time out was reproducible on Solaris 50% of the time;
97
// on Linux 80% of the time.
98
//
99
// Scenario: After fork(), parent executes and closes write end of child's stdin.
100
// This causes child to retain a write end of the same pipe.
101
// Thus the child will never see an EOF on its stdin, and will hang.
102
Runtime rt = Runtime.getRuntime();
103
// Increasing the iteration count makes the bug more
104
// reproducible not only for the obvious reason, but also for
105
// the subtle reason that it makes reading /proc/getppid()/fd
106
// slower, making the child more likely to win the race!
107
int iterations = 20;
108
int timeout = 30;
109
String[] catArgs = new String[] {UnixCommands.cat()};
110
String[] sleepArgs = new String[] {UnixCommands.sleep(),
111
String.valueOf(timeout+1)};
112
Process[] cats = new Process[iterations];
113
Process[] sleeps = new Process[iterations];
114
Timer timer = new Timer(true);
115
TimeoutTask catExecutioner = new TimeoutTask(cats);
116
timer.schedule(catExecutioner, timeout * 1000);
117
118
for (int i = 0; i < cats.length; ++i) {
119
cats[i] = rt.exec(catArgs);
120
java.io.OutputStream s = cats[i].getOutputStream();
121
Process sleep = rt.exec(sleepArgs);
122
s.close(); // race condition here
123
sleeps[i] = sleep;
124
}
125
126
for (int i = 0; i < cats.length; ++i)
127
cats[i].waitFor(); // hangs?
128
129
timer.cancel();
130
131
destroy(sleeps);
132
133
if (catExecutioner.timedOut())
134
System.out.println("Child process has a hidden writable pipe fd for its stdin.");
135
return catExecutioner.timedOut();
136
}
137
138
private static boolean hang2() throws Exception {
139
// Inspired by the imaginative test case for
140
// 4850368 (process) getInputStream() attaches to forked background processes (Linux)
141
142
// Time out was reproducible on Linux 80% of the time;
143
// never on Solaris because of explicit close in Solaris-specific code.
144
145
// Scenario: After fork(), the parent naturally closes the
146
// child's stdout write end. The child dup2's the write end
147
// of its stdout onto fd 1. On Linux, it fails to explicitly
148
// close the original fd, and because of the parent's close()
149
// of the fd, the child retains it. The child thus ends up
150
// with two copies of its stdout. Thus closing one of those
151
// write fds does not have the desired effect of causing an
152
// EOF on the parent's read end of that pipe.
153
Runtime rt = Runtime.getRuntime();
154
int iterations = 10;
155
Timer timer = new Timer(true);
156
int timeout = 30;
157
Process[] backgroundSleepers = new Process[iterations];
158
TimeoutTask sleeperExecutioner = new TimeoutTask(backgroundSleepers);
159
timer.schedule(sleeperExecutioner, timeout * 1000);
160
byte[] buffer = new byte[10];
161
String[] args =
162
new String[] {UnixCommands.sh(), "-c",
163
"exec " + UnixCommands.sleep() + " "
164
+ (timeout+1) + " >/dev/null"};
165
166
for (int i = 0;
167
i < backgroundSleepers.length && !sleeperExecutioner.timedOut();
168
++i) {
169
backgroundSleepers[i] = rt.exec(args); // race condition here
170
try {
171
// should get immediate EOF, but might hang
172
if (backgroundSleepers[i].getInputStream().read() != -1)
173
throw new Exception("Expected EOF, got a byte");
174
} catch (IOException e) {
175
// Stream closed by sleeperExecutioner
176
break;
177
}
178
}
179
180
timer.cancel();
181
182
destroy(backgroundSleepers);
183
184
if (sleeperExecutioner.timedOut())
185
System.out.println("Child process has two (should be one) writable pipe fds for its stdout.");
186
return sleeperExecutioner.timedOut();
187
}
188
189
public static void main (String[] args) throws Exception {
190
if (! UnixCommands.isUnix) {
191
System.out.println("For UNIX only");
192
return;
193
}
194
UnixCommands.ensureCommandsAvailable("sh", "cat", "sleep");
195
196
if (hang1() | hang2())
197
throw new Exception("Read from closed pipe hangs");
198
}
199
}
200
201