8272600: (test) Use native "sleep" in Basic.java

Reviewed-by: iklam, dholmes
This commit is contained in:
Roger Riggs 2021-09-22 13:55:19 +00:00
parent c6df3c9571
commit 0a361638c5
2 changed files with 139 additions and 63 deletions
test/jdk/java/lang/ProcessBuilder

@ -27,13 +27,13 @@
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
* 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
* 4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464
* 8067796 8224905 8263729 8265173
* 8067796 8224905 8263729 8265173 8272600 8231297
* @key intermittent
* @summary Basic tests for Process and Environment Variable code
* @modules java.base/java.lang:open
* @library /test/lib
* @run main/othervm/timeout=300 -Djava.security.manager=allow Basic
* @run main/othervm/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic
* @run main/othervm/native/timeout=300 -Djava.security.manager=allow Basic
* @run main/othervm/native/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic
* @author Martin Buchholz
*/
@ -50,8 +50,8 @@ import java.lang.ProcessHandle;
import static java.lang.ProcessBuilder.Redirect.*;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
@ -85,7 +85,7 @@ public class Basic {
/**
* Returns the number of milliseconds since time given by
* startNanoTime, which must have been previously returned from a
* call to {@link System.nanoTime()}.
* call to {@link System#nanoTime()}.
*/
private static long millisElapsedSince(long startNanoTime) {
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
@ -2137,34 +2137,8 @@ public class Basic {
final int cases = 4;
for (int i = 0; i < cases; i++) {
final int action = i;
List<String> childArgs = new ArrayList<>(javaChildArgs);
List<String> childArgs = getSleepArgs();
final ProcessBuilder pb = new ProcessBuilder(childArgs);
{
// Redirect any child VM error output away from the stream being tested
// and to the log file. For background see:
// 8231297: java/lang/ProcessBuilder/Basic.java test fails intermittently
// Destroying the process may, depending on the timing, cause some output
// from the child VM.
// This test requires the thread reading from the subprocess be blocked
// in the read from the subprocess; there should be no bytes to read.
// Modify the argument list shared with ProcessBuilder to redirect VM output.
assert (childArgs.get(1).equals("-XX:+DisplayVMOutputToStderr")) : "Expected arg 1 to be \"-XX:+DisplayVMOutputToStderr\"";
switch (action & 0x1) {
case 0:
childArgs.set(1, "-XX:+DisplayVMOutputToStderr");
childArgs.add(2, "-Xlog:all=warning:stderr");
pb.redirectError(INHERIT);
break;
case 1:
childArgs.set(1, "-XX:+DisplayVMOutputToStdout");
childArgs.add(2, "-Xlog:all=warning:stdout");
pb.redirectOutput(INHERIT);
break;
default:
throw new Error();
}
}
childArgs.add("sleep");
final byte[] bytes = new byte[10];
final Process p = pb.start();
final CountDownLatch latch = new CountDownLatch(1);
@ -2237,9 +2211,10 @@ public class Basic {
// our child) but not our grandchild (i.e. '/bin/sleep'). So
// pay attention that the grandchild doesn't run too long to
// avoid polluting the process space with useless processes.
// Running the grandchild for 60s should be more than enough.
final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" };
final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 60\")" };
// Running the grandchild for 59s should be more than enough.
// A unique (59s) time is needed to avoid killing other sleep processes.
final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 59)" };
final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 59\")" };
final ProcessBuilder pb = new ProcessBuilder(cmd);
final Process p = pb.start();
final InputStream stdout = p.getInputStream();
@ -2441,8 +2416,7 @@ public class Basic {
// Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.
//----------------------------------------------------------------
try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("sleep");
List<String> childArgs = getSleepArgs();
final Process p = new ProcessBuilder(childArgs).start();
long start = System.nanoTime();
if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {
@ -2471,17 +2445,19 @@ public class Basic {
// works as expected.
//----------------------------------------------------------------
try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("sleep");
List<String> childArgs = getSleepArgs();
final Process p = new ProcessBuilder(childArgs).start();
long start = System.nanoTime();
p.waitFor(10, TimeUnit.MILLISECONDS);
long end = System.nanoTime();
if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))
fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
if (p.waitFor(10, TimeUnit.MILLISECONDS)) {
var msg = "External sleep process terminated early: exitValue: %d, (%dns)%n"
.formatted(p.exitValue(), (System.nanoTime() - start));
fail(msg);
} else {
long end = System.nanoTime();
if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))
fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
}
p.destroy();
} catch (Throwable t) { unexpected(t); }
@ -2490,8 +2466,7 @@ public class Basic {
// interrupt works as expected, if interrupted while waiting.
//----------------------------------------------------------------
try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("sleep");
List<String> childArgs = getSleepArgs();
final Process p = new ProcessBuilder(childArgs).start();
final long start = System.nanoTime();
final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
@ -2522,8 +2497,7 @@ public class Basic {
// interrupt works as expected, if interrupted while waiting.
//----------------------------------------------------------------
try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("sleep");
List<String> childArgs = getSleepArgs();
final Process p = new ProcessBuilder(childArgs).start();
final long start = System.nanoTime();
final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
@ -2554,8 +2528,7 @@ public class Basic {
// interrupt works as expected, if interrupted before waiting.
//----------------------------------------------------------------
try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("sleep");
List<String> childArgs = getSleepArgs();
final Process p = new ProcessBuilder(childArgs).start();
final long start = System.nanoTime();
final CountDownLatch threadStarted = new CountDownLatch(1);
@ -2586,8 +2559,7 @@ public class Basic {
// Check that Process.waitFor(timeout, null) throws NPE.
//----------------------------------------------------------------
try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("sleep");
List<String> childArgs = getSleepArgs();
final Process p = new ProcessBuilder(childArgs).start();
THROWS(NullPointerException.class,
() -> p.waitFor(10L, null));
@ -2610,8 +2582,7 @@ public class Basic {
// Check that default implementation of Process.waitFor(timeout, null) throws NPE.
//----------------------------------------------------------------
try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("sleep");
List<String> childArgs = getSleepArgs();
final Process proc = new ProcessBuilder(childArgs).start();
final DelegatingProcess p = new DelegatingProcess(proc);
@ -2637,24 +2608,75 @@ public class Basic {
// Process.waitFor(long, TimeUnit)
//----------------------------------------------------------------
try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("sleep");
List<String> childArgs = getSleepArgs();
final Process proc = new ProcessBuilder(childArgs).start();
DelegatingProcess p = new DelegatingProcess(proc);
long start = System.nanoTime();
p.waitFor(1000, TimeUnit.MILLISECONDS);
long end = System.nanoTime();
if ((end - start) < 500000000)
fail("Test failed: waitFor didn't take long enough");
if (p.waitFor(1000, TimeUnit.MILLISECONDS)) {
var msg = "External sleep process terminated early: exitValue: %02x, (%dns)"
.formatted(p.exitValue(), (System.nanoTime() - start));
fail(msg);
} else {
long end = System.nanoTime();
if ((end - start) < 500000000)
fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
}
p.destroy();
p.waitFor(1000, TimeUnit.MILLISECONDS);
} catch (Throwable t) { unexpected(t); }
}
// Path to native executables, if any
private static final String TEST_NATIVEPATH = System.getProperty("test.nativepath");
// Path where "sleep" program may be found" or null
private static final Path SLEEP_PATH = initSleepPath();
/**
* Compute the Path to a sleep executable.
* @return a Path to sleep or BasicSleep(.exe) or null if none
*/
private static Path initSleepPath() {
if (Windows.is() && TEST_NATIVEPATH != null) {
// exeBasicSleep is equivalent to sleep on Unix
Path exePath = Path.of(TEST_NATIVEPATH).resolve("BasicSleep.exe");
if (Files.isExecutable(exePath)) {
return exePath;
}
}
List<String> binPaths = List.of("/bin", "/usr/bin");
for (String dir : binPaths) {
Path exePath = Path.of(dir).resolve("sleep");
if (Files.isExecutable(exePath)) {
return exePath;
}
}
return null;
}
/**
* Return the list of process arguments for a child to sleep 10 minutes (600 seconds).
*
* @return A list of process arguments to sleep 10 minutes.
*/
private static List<String> getSleepArgs() {
List<String> childArgs = null;
if (SLEEP_PATH != null) {
childArgs = List.of(SLEEP_PATH.toString(), "600");
} else {
// Fallback to the JavaChild ; its 'sleep' command is for 10 minutes.
// The fallback the Java$Child is used if the test is run without building
// the BasicSleep native executable (for Windows).
childArgs = new ArrayList<>(javaChildArgs);
childArgs.add("sleep");
System.out.println("Sleep not found, fallback to JavaChild: " + childArgs);
}
return childArgs;
}
static void closeStreams(Process p) {
try {
p.getOutputStream().close();

@ -0,0 +1,54 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
/**
* Command line program to sleep at least given number of seconds.
* The behavior should equivalent to the Unix sleep command.
* Actual time sleeping may vary if interrupted, the remaining time
* returned from sleep has limited accuracy.
*
* Note: the file name prefix "exe" identifies the source should be built into BasicSleep(.exe).
*/
int main(int argc, char** argv) {
int seconds;
if (argc < 2 || (seconds = atoi(argv[1])) < 0) {
fprintf(stderr, "usage: BasicSleep <non-negative seconds>\n");
exit(1);
}
#ifdef _WIN32
Sleep(seconds * 1000);
#else
while ((seconds = sleep(seconds)) > 0) {
// until no more to sleep
}
#endif
}