Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/java/lang/ProcessHandle/JavaChild.java
41149 views
1
/*
2
* Copyright (c) 2014, 2017, 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
import com.sun.management.OperatingSystemMXBean;
25
import java.io.File;
26
import java.io.InputStream;
27
import java.io.OutputStream;
28
import java.io.InputStreamReader;
29
import java.io.BufferedReader;
30
import java.io.IOException;
31
import java.io.Reader;
32
import java.io.PrintWriter;
33
import java.lang.InterruptedException;
34
import java.lang.Override;
35
import java.lang.management.ManagementFactory;
36
import java.time.Instant;
37
import java.util.ArrayList;
38
import java.util.Arrays;
39
import java.util.Collections;
40
import java.util.concurrent.CompletableFuture;
41
import java.util.concurrent.ExecutionException;
42
import java.util.HashSet;
43
import java.util.List;
44
import java.util.Set;
45
import java.util.Optional;
46
import java.util.function.Consumer;
47
48
49
/**
50
* Command driven subprocess with useful child functions.
51
*/
52
public class JavaChild extends Process {
53
54
private static volatile int commandSeq = 0; // Command sequence number
55
private static final ProcessHandle self = ProcessHandle.current();
56
private static int finalStatus = 0;
57
private static final List<JavaChild> children = new ArrayList<>();
58
private static final Set<JavaChild> completedChildren =
59
Collections.synchronizedSet(new HashSet<>());
60
61
private final Process delegate;
62
private final PrintWriter inputWriter;
63
private final BufferedReader outputReader;
64
65
66
/**
67
* Create a JavaChild control instance that delegates to the spawned process.
68
* {@link #sendAction} is used to send commands via the processes stdin.
69
* {@link #forEachOutputLine} can be used to process output from the child
70
* @param delegate the process to delegate and send commands to and get responses from
71
*/
72
private JavaChild(ProcessBuilder pb) throws IOException {
73
allArgs = pb.command();
74
delegate = pb.start();
75
// Initialize PrintWriter with autoflush (on println)
76
inputWriter = new PrintWriter(delegate.getOutputStream(), true);
77
outputReader = new BufferedReader(new InputStreamReader(delegate.getInputStream()));
78
}
79
80
@Override
81
public void destroy() {
82
delegate.destroy();
83
}
84
85
@Override
86
public int exitValue() {
87
return delegate.exitValue();
88
}
89
90
@Override
91
public int waitFor() throws InterruptedException {
92
return delegate.waitFor();
93
}
94
95
@Override
96
public OutputStream getOutputStream() {
97
return delegate.getOutputStream();
98
}
99
100
@Override
101
public InputStream getInputStream() {
102
return delegate.getInputStream();
103
}
104
105
@Override
106
public InputStream getErrorStream() {
107
return delegate.getErrorStream();
108
}
109
110
@Override
111
public ProcessHandle toHandle() {
112
return delegate.toHandle();
113
}
114
115
@Override
116
public CompletableFuture<Process> onExit() {
117
return delegate.onExit();
118
}
119
@Override
120
public String toString() {
121
return "delegate: " + delegate.toString();
122
}
123
124
public List<String> getArgs() {
125
return allArgs;
126
}
127
128
public CompletableFuture<JavaChild> onJavaChildExit() {
129
return onExit().thenApply(ph -> this);
130
}
131
132
/**
133
* Send an action and arguments to the child via stdin.
134
* @param action the action
135
* @param args additional arguments
136
* @throws IOException if something goes wrong writing to the child
137
*/
138
void sendAction(String action, Object... args) throws IOException {
139
StringBuilder sb = new StringBuilder();
140
sb.append(action);
141
for (Object arg :args) {
142
sb.append(" ");
143
sb.append(arg);
144
}
145
String cmd = sb.toString();
146
synchronized (this) {
147
inputWriter.println(cmd);
148
}
149
}
150
151
public BufferedReader outputReader() {
152
return outputReader;
153
}
154
155
/**
156
* Asynchronously evaluate each line of output received back from the child process.
157
* @param consumer a Consumer of each line read from the child
158
* @return a CompletableFuture that is completed when the child closes System.out.
159
*/
160
CompletableFuture<String> forEachOutputLine(Consumer<String> consumer) {
161
final CompletableFuture<String> future = new CompletableFuture<>();
162
String name = "OutputLineReader-" + pid();
163
Thread t = new Thread(() -> {
164
try (BufferedReader reader = outputReader()) {
165
String line;
166
while ((line = reader.readLine()) != null) {
167
consumer.accept(line);
168
}
169
} catch (IOException | RuntimeException ex) {
170
consumer.accept("IOE (" + pid() + "):" + ex.getMessage());
171
future.completeExceptionally(ex);
172
}
173
future.complete("success");
174
}, name);
175
t.start();
176
return future;
177
}
178
179
/**
180
* Spawn a JavaChild with the provided arguments.
181
* Commands can be send to the child with {@link #sendAction}.
182
* Output lines from the child can be processed with {@link #forEachOutputLine}.
183
* System.err is set to inherit and is the unstructured async logging
184
* output for all subprocesses.
185
* @param args the command line arguments to JavaChild
186
* @return the JavaChild that was started
187
* @throws IOException thrown by ProcessBuilder.start
188
*/
189
static JavaChild spawnJavaChild(Object... args) throws IOException {
190
String[] stringArgs = new String[args.length];
191
for (int i = 0; i < args.length; i++) {
192
stringArgs[i] = args[i].toString();
193
}
194
ProcessBuilder pb = build(stringArgs);
195
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
196
return new JavaChild(pb);
197
}
198
199
/**
200
* Spawn a JavaChild with the provided arguments.
201
* Sets the process to inherit the I/O channels.
202
* @param args the command line arguments to JavaChild
203
* @return the Process that was started
204
* @throws IOException thrown by ProcessBuilder.start
205
*/
206
static Process spawn(String... args) throws IOException {
207
ProcessBuilder pb = build(args);
208
pb.inheritIO();
209
return pb.start();
210
}
211
212
/**
213
* Return a ProcessBuilder with the javaChildArgs and
214
* any additional supplied args.
215
*
216
* @param args the command line arguments to JavaChild
217
* @return the ProcessBuilder
218
*/
219
static ProcessBuilder build(String ... args) {
220
ProcessBuilder pb = new ProcessBuilder();
221
List<String> list = new ArrayList<>(javaChildArgs);
222
for (String arg : args)
223
list.add(arg);
224
pb.command(list);
225
return pb;
226
}
227
228
static final String javaHome = (System.getProperty("test.jdk") != null)
229
? System.getProperty("test.jdk")
230
: System.getProperty("java.home");
231
232
static final String javaExe =
233
javaHome + File.separator + "bin" + File.separator + "java";
234
235
static final String classpath =
236
System.getProperty("java.class.path");
237
238
static final List<String> javaChildArgs =
239
Arrays.asList(javaExe,
240
"-XX:+DisplayVMOutputToStderr",
241
"-Dtest.jdk=" + javaHome,
242
"-classpath", absolutifyPath(classpath),
243
"JavaChild");
244
245
// Will hold the complete list of arguments which was given to Processbuilder.command()
246
private List<String> allArgs;
247
248
private static String absolutifyPath(String path) {
249
StringBuilder sb = new StringBuilder();
250
for (String file : path.split(File.pathSeparator)) {
251
if (sb.length() != 0)
252
sb.append(File.pathSeparator);
253
sb.append(new File(file).getAbsolutePath());
254
}
255
return sb.toString();
256
}
257
258
/**
259
* Main program that interprets commands from the command line args or stdin.
260
* Each command produces output to stdout confirming the command and
261
* providing results.
262
* System.err is used for unstructured information.
263
* @param args an array of strings to be interpreted as commands;
264
* each command uses additional arguments as needed
265
*/
266
public static void main(String[] args) {
267
System.out.printf("args: %s %s%n", ProcessHandle.current(), Arrays.toString(args));
268
interpretCommands(args);
269
System.exit(finalStatus);
270
}
271
272
/**
273
* Interpret an array of strings as a command line.
274
* @param args an array of strings to be interpreted as commands;
275
* each command uses additional arguments as needed
276
*/
277
private static void interpretCommands(String[] args) {
278
try {
279
int nextArg = 0;
280
while (nextArg < args.length) {
281
String action = args[nextArg++];
282
switch (action) {
283
case "help":
284
sendResult(action, "");
285
help();
286
break;
287
case "sleep":
288
int millis = Integer.valueOf(args[nextArg++]);
289
Thread.sleep(millis);
290
sendResult(action, Integer.toString(millis));
291
break;
292
case "cpuloop":
293
long cpuMillis = Long.valueOf(args[nextArg++]);
294
long cpuTarget = getCpuTime() + cpuMillis * 1_000_000L;
295
while (getCpuTime() < cpuTarget) {
296
// burn the cpu until the time is up
297
}
298
sendResult(action, cpuMillis);
299
break;
300
case "cputime":
301
sendResult(action, getCpuTime());
302
break;
303
case "out":
304
case "err":
305
String value = args[nextArg++];
306
sendResult(action, value);
307
if (action.equals("err")) {
308
System.err.println(value);
309
}
310
break;
311
case "stdin":
312
// Read commands from stdin; at eof, close stdin of
313
// children and wait for each to exit
314
sendResult(action, "start");
315
try (Reader reader = new InputStreamReader(System.in);
316
BufferedReader input = new BufferedReader(reader)) {
317
String line;
318
while ((line = input.readLine()) != null) {
319
line = line.trim();
320
if (!line.isEmpty()) {
321
String[] split = line.split("\\s");
322
interpretCommands(split);
323
}
324
}
325
// EOF on stdin, close stdin on all spawned processes
326
for (JavaChild p : children) {
327
try {
328
p.getOutputStream().close();
329
} catch (IOException ie) {
330
sendResult("stdin_closing", p.pid(),
331
"exception", ie.getMessage());
332
}
333
}
334
335
for (JavaChild p : children) {
336
do {
337
try {
338
p.waitFor();
339
break;
340
} catch (InterruptedException e) {
341
// retry
342
}
343
} while (true);
344
}
345
// Wait for all children to be gone
346
Instant timeOut = Instant.now().plusSeconds(10L);
347
while (!completedChildren.containsAll(children)) {
348
if (Instant.now().isBefore(timeOut)) {
349
Thread.sleep(100L);
350
} else {
351
System.err.printf("Timeout waiting for " +
352
"children to terminate%n");
353
children.removeAll(completedChildren);
354
for (JavaChild c : children) {
355
sendResult("stdin_noterm", c.pid());
356
System.err.printf(" Process not terminated: " +
357
"pid: %d%n", c.pid());
358
}
359
System.exit(2);
360
}
361
}
362
}
363
sendResult(action, "done");
364
return; // normal exit from JavaChild Process
365
case "parent":
366
sendResult(action, self.parent().toString());
367
break;
368
case "pid":
369
sendResult(action, self.toString());
370
break;
371
case "exit":
372
int exitValue = (nextArg < args.length)
373
? Integer.valueOf(args[nextArg]) : 0;
374
sendResult(action, exitValue);
375
System.exit(exitValue);
376
break;
377
case "spawn": {
378
if (args.length - nextArg < 2) {
379
throw new RuntimeException("not enough args for respawn: " +
380
(args.length - 2));
381
}
382
// Spawn as many children as requested and
383
// pass on rest of the arguments
384
int ncount = Integer.valueOf(args[nextArg++]);
385
Object[] subargs = new String[args.length - nextArg];
386
System.arraycopy(args, nextArg, subargs, 0, subargs.length);
387
for (int i = 0; i < ncount; i++) {
388
JavaChild p = spawnJavaChild(subargs);
389
sendResult(action, p.pid());
390
p.forEachOutputLine(JavaChild::sendRaw);
391
p.onJavaChildExit().thenAccept((p1) -> {
392
int excode = p1.exitValue();
393
sendResult("child_exit", p1.pid(), excode);
394
completedChildren.add(p1);
395
});
396
children.add(p); // Add child to spawned list
397
}
398
nextArg = args.length;
399
break;
400
}
401
case "child": {
402
// Send the command to all the live children;
403
// ignoring those that are not alive
404
int sentCount = 0;
405
Object[] result =
406
Arrays.copyOfRange(args, nextArg - 1, args.length);
407
Object[] subargs =
408
Arrays.copyOfRange(args, nextArg + 1, args.length);
409
for (JavaChild p : children) {
410
if (p.isAlive()) {
411
sentCount++;
412
// overwrite with current pid
413
result[0] = Long.toString(p.pid());
414
sendResult(action, result);
415
p.sendAction(args[nextArg], subargs);
416
}
417
}
418
if (sentCount == 0) {
419
sendResult(action, "n/a");
420
}
421
nextArg = args.length;
422
break;
423
}
424
case "child_eof" :
425
// Close the InputStream of all the live children;
426
// ignoring those that are not alive
427
for (JavaChild p : children) {
428
if (p.isAlive()) {
429
sendResult(action, p.pid());
430
p.getOutputStream().close();
431
}
432
}
433
break;
434
case "property":
435
String name = args[nextArg++];
436
sendResult(action, name, System.getProperty(name));
437
break;
438
case "threaddump":
439
Thread.dumpStack();
440
break;
441
case "waitpid":
442
long pid = Long.parseLong(args[nextArg++]);
443
Optional<String> s = ProcessHandle.of(pid).map(ph -> waitAlive(ph));
444
sendResult(action, s.orElse("pid not valid: " + pid));
445
break;
446
default:
447
throw new Error("JavaChild action unknown: " + action);
448
}
449
}
450
} catch (Throwable t) {
451
t.printStackTrace(System.err);
452
System.exit(1);
453
}
454
}
455
456
private static String waitAlive(ProcessHandle ph) {
457
String status;
458
try {
459
boolean isAlive = ph.onExit().get().isAlive();
460
status = Boolean.toString(isAlive);
461
} catch (InterruptedException | ExecutionException ex ) {
462
status = "interrupted";
463
}
464
return status;
465
}
466
467
static synchronized void sendRaw(String s) {
468
System.out.println(s);
469
System.out.flush();
470
}
471
static void sendResult(String action, Object... results) {
472
sendRaw(new Event(action, results).toString());
473
}
474
475
static long getCpuTime() {
476
OperatingSystemMXBean osMbean =
477
(OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
478
return osMbean.getProcessCpuTime();
479
}
480
481
/**
482
* Print command usage to stderr.
483
*/
484
private static void help() {
485
System.err.println("Commands:");
486
System.err.println(" help");
487
System.err.println(" pid");
488
System.err.println(" parent");
489
System.err.println(" cpuloop <loopcount>");
490
System.err.println(" cputime");
491
System.err.println(" stdin - read commands from stdin");
492
System.err.println(" sleep <millis>");
493
System.err.println(" spawn <n> command... - spawn n new children and send command");
494
System.err.println(" child command... - send command to all live children");
495
System.err.println(" child_eof - send eof to all live children");
496
System.err.println(" waitpid <pid> - wait for the pid to exit");
497
System.err.println(" exit <exitcode>");
498
System.err.println(" out arg...");
499
System.err.println(" err arg...");
500
}
501
502
static class Event {
503
long pid;
504
long seq;
505
String command;
506
Object[] results;
507
Event(String command, Object... results) {
508
this(self.pid(), ++commandSeq, command, results);
509
}
510
Event(long pid, int seq, String command, Object... results) {
511
this.pid = pid;
512
this.seq = seq;
513
this.command = command;
514
this.results = results;
515
}
516
517
/**
518
* Create a String encoding the pid, seq, command, and results.
519
*
520
* @return a String formatted to send to the stream.
521
*/
522
String format() {
523
StringBuilder sb = new StringBuilder();
524
sb.append(pid);
525
sb.append(":");
526
sb.append(seq);
527
sb.append(" ");
528
sb.append(command);
529
for (int i = 0; i < results.length; i++) {
530
sb.append(" ");
531
sb.append(results[i]);
532
}
533
return sb.toString();
534
}
535
536
Event(String encoded) {
537
String[] split = encoded.split("\\s");
538
String[] pidSeq = split[0].split(":");
539
pid = Long.valueOf(pidSeq[0]);
540
seq = Integer.valueOf(pidSeq[1]);
541
command = split[1];
542
Arrays.copyOfRange(split, 1, split.length);
543
}
544
545
public String toString() {
546
return format();
547
}
548
549
}
550
}
551
552