8138566: (Process) java.lang.Process.allChildren specification clarification
8140213: Process/ProcessHandle.onExit() spec need to be improved 8140250: (process) Process.info description is inaccurate Rename to descendants() and clarify Reviewed-by: psandoz
This commit is contained in:
parent
7afea22f63
commit
fe2786af5f
@ -83,7 +83,7 @@ import java.util.stream.Stream;
|
||||
* {@link #getPid() process id},
|
||||
* {@link #info() information about the process},
|
||||
* {@link #children() direct children}, and
|
||||
* {@link #allChildren() direct and indirect children} of the process.
|
||||
* {@link #descendants() direct children plus descendants of those children} of the process.
|
||||
* Delegating to the underlying Process or ProcessHandle is typically
|
||||
* easiest and most efficient.
|
||||
*
|
||||
@ -351,7 +351,7 @@ public abstract class Process {
|
||||
* The {@link java.util.concurrent.CompletableFuture} provides the ability
|
||||
* to trigger dependent functions or actions that may be run synchronously
|
||||
* or asynchronously upon process termination.
|
||||
* When the process terminates the CompletableFuture is
|
||||
* When the process has terminated the CompletableFuture is
|
||||
* {@link java.util.concurrent.CompletableFuture#complete completed} regardless
|
||||
* of the exit status of the process.
|
||||
* <p>
|
||||
@ -362,9 +362,6 @@ public abstract class Process {
|
||||
* {@link java.util.concurrent.CompletableFuture#cancel(boolean) Cancelling}
|
||||
* the CompletableFuture does not affect the Process.
|
||||
* <p>
|
||||
* If the process is {@link #isAlive not alive} the {@link CompletableFuture}
|
||||
* returned has been {@link java.util.concurrent.CompletableFuture#complete completed}.
|
||||
* <p>
|
||||
* Processes returned from {@link ProcessBuilder#start} override the
|
||||
* default implementation to provide an efficient mechanism to wait
|
||||
* for process exit.
|
||||
@ -406,6 +403,9 @@ public abstract class Process {
|
||||
* return delegate.onExit().thenApply(p -> this);
|
||||
* }
|
||||
* }</pre>
|
||||
* @apiNote
|
||||
* The process may be observed to have terminated with {@link #isAlive}
|
||||
* before the ComputableFuture is completed and dependent actions are invoked.
|
||||
*
|
||||
* @return a new {@code CompletableFuture<Process>} for the Process
|
||||
*
|
||||
@ -464,7 +464,7 @@ public abstract class Process {
|
||||
* {@link java.lang.UnsupportedOperationException} and performs no other action.
|
||||
* Subclasses should override this method to provide a ProcessHandle for the
|
||||
* process. The methods {@link #getPid}, {@link #info}, {@link #children},
|
||||
* and {@link #allChildren}, unless overridden, operate on the ProcessHandle.
|
||||
* and {@link #descendants}, unless overridden, operate on the ProcessHandle.
|
||||
*
|
||||
* @return Returns a ProcessHandle for the Process
|
||||
* @throws UnsupportedOperationException if the Process implementation
|
||||
@ -481,9 +481,8 @@ public abstract class Process {
|
||||
/**
|
||||
* Returns a snapshot of information about the process.
|
||||
*
|
||||
* <p> An {@link ProcessHandle.Info} instance has various accessor methods
|
||||
* that return information about the process, if the process is alive and
|
||||
* the information is available, otherwise {@code null} is returned.
|
||||
* <p> A {@link ProcessHandle.Info} instance has accessor methods
|
||||
* that return information about the process if it is available.
|
||||
*
|
||||
* @implSpec
|
||||
* This implementation returns information about the process as:
|
||||
@ -524,9 +523,9 @@ public abstract class Process {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the direct and indirect children of the process.
|
||||
* An indirect child is one whose parent is either a direct child or
|
||||
* another indirect child.
|
||||
* Returns a snapshot of the descendants of the process.
|
||||
* The descendants of a process are the children of the process
|
||||
* plus the descendants of those children, recursively.
|
||||
* Typically, a process that is {@link #isAlive not alive} has no children.
|
||||
* <p>
|
||||
* <em>Note that processes are created and terminate asynchronously.
|
||||
@ -535,18 +534,18 @@ public abstract class Process {
|
||||
*
|
||||
* @implSpec
|
||||
* This implementation returns all children as:
|
||||
* {@link #toHandle toHandle().allChildren()}.
|
||||
* {@link #toHandle toHandle().descendants()}.
|
||||
*
|
||||
* @return a sequential Stream of ProcessHandles for processes that are
|
||||
* direct and indirect children of the process
|
||||
* @return a sequential Stream of ProcessHandles for processes that
|
||||
* are descendants of the process
|
||||
* @throws UnsupportedOperationException if the Process implementation
|
||||
* does not support this operation
|
||||
* @throws SecurityException if a security manager has been installed and
|
||||
* it denies RuntimePermission("manageProcess")
|
||||
* @since 1.9
|
||||
*/
|
||||
public Stream<ProcessHandle> allChildren() {
|
||||
return toHandle().allChildren();
|
||||
public Stream<ProcessHandle> descendants() {
|
||||
return toHandle().descendants();
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ import java.util.stream.Stream;
|
||||
* Each ProcessHandle identifies and allows control of a process in the native
|
||||
* system. ProcessHandles are returned from the factory methods {@link #current()},
|
||||
* {@link #of(long)},
|
||||
* {@link #children}, {@link #allChildren}, {@link #parent()} and
|
||||
* {@link #children}, {@link #descendants}, {@link #parent()} and
|
||||
* {@link #allProcesses()}.
|
||||
* <p>
|
||||
* The {@link Process} instances created by {@link ProcessBuilder} can be queried
|
||||
@ -164,21 +164,21 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
|
||||
Stream<ProcessHandle> children();
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the current direct and indirect children of the process.
|
||||
* An indirect child is one whose parent is either a direct child or
|
||||
* another indirect child.
|
||||
* Returns a snapshot of the descendants of the process.
|
||||
* The descendants of a process are the children of the process
|
||||
* plus the descendants of those children, recursively.
|
||||
* Typically, a process that is {@link #isAlive not alive} has no children.
|
||||
* <p>
|
||||
* <em>Note that processes are created and terminate asynchronously.
|
||||
* There is no guarantee that a process is {@link #isAlive alive}.
|
||||
* </em>
|
||||
*
|
||||
* @return a sequential Stream of ProcessHandles for processes that are
|
||||
* direct and indirect children of the process
|
||||
* @return a sequential Stream of ProcessHandles for processes that
|
||||
* are descendants of the process
|
||||
* @throws SecurityException if a security manager has been installed and
|
||||
* it denies RuntimePermission("manageProcess")
|
||||
*/
|
||||
Stream<ProcessHandle> allChildren();
|
||||
Stream<ProcessHandle> descendants();
|
||||
|
||||
/**
|
||||
* Returns a snapshot of all processes visible to the current process.
|
||||
@ -201,9 +201,8 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
|
||||
/**
|
||||
* Returns a snapshot of information about the process.
|
||||
*
|
||||
* <p> An {@code Info} instance has various accessor methods that return
|
||||
* information about the process, if the process is alive and the
|
||||
* information is available.
|
||||
* <p> A {@link ProcessHandle.Info} instance has accessor methods that return
|
||||
* information about the process if it is available.
|
||||
*
|
||||
* @return a snapshot of information about the process, always non-null
|
||||
*/
|
||||
@ -288,7 +287,7 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
|
||||
* The {@link java.util.concurrent.CompletableFuture} provides the ability
|
||||
* to trigger dependent functions or actions that may be run synchronously
|
||||
* or asynchronously upon process termination.
|
||||
* When the process terminates the CompletableFuture is
|
||||
* When the process has terminated the CompletableFuture is
|
||||
* {@link java.util.concurrent.CompletableFuture#complete completed} regardless
|
||||
* of the exit status of the process.
|
||||
* The {@code onExit} method can be called multiple times to invoke
|
||||
@ -300,9 +299,9 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
|
||||
* {@link java.util.concurrent.Future#get() wait} for it to terminate.
|
||||
* {@link java.util.concurrent.Future#cancel(boolean) Cancelling}
|
||||
* the CompleteableFuture does not affect the Process.
|
||||
* <p>
|
||||
* If the process is {@link #isAlive not alive} the {@link CompletableFuture}
|
||||
* returned has been {@link java.util.concurrent.CompletableFuture#complete completed}.
|
||||
* @apiNote
|
||||
* The process may be observed to have terminated with {@link #isAlive}
|
||||
* before the ComputableFuture is completed and dependent actions are invoked.
|
||||
*
|
||||
* @return a new {@code CompletableFuture<ProcessHandle>} for the ProcessHandle
|
||||
*
|
||||
|
@ -389,7 +389,7 @@ final class ProcessHandleImpl implements ProcessHandle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<ProcessHandle> allChildren() {
|
||||
public Stream<ProcessHandle> descendants() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new RuntimePermission("manageProcess"));
|
||||
|
@ -1248,7 +1248,7 @@ public class Basic {
|
||||
() -> p.toHandle(),
|
||||
() -> p.supportsNormalTermination(),
|
||||
() -> p.children(),
|
||||
() -> p.allChildren());
|
||||
() -> p.descendants());
|
||||
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ public class OnExitTest extends ProcessUtil {
|
||||
printf(" You can try to increase the timeout or%n");
|
||||
printf(" you can try to use a faster VM (i.e. not a debug version).%n");
|
||||
}
|
||||
children = getAllChildren(procHandle);
|
||||
children = getDescendants(procHandle);
|
||||
|
||||
ConcurrentHashMap<ProcessHandle, CompletableFuture<ProcessHandle>> completions =
|
||||
new ConcurrentHashMap<>();
|
||||
|
@ -62,9 +62,9 @@ public class PermissionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allChildrenWithPermission() {
|
||||
public void descendantsWithPermission() {
|
||||
Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
|
||||
currentHndl.allChildren();
|
||||
currentHndl.descendants();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -122,7 +122,7 @@ public class PermissionTest {
|
||||
|
||||
@Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
|
||||
public void noPermissionAllChildren() {
|
||||
currentHndl.allChildren();
|
||||
currentHndl.descendants();
|
||||
}
|
||||
|
||||
@Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
|
||||
|
@ -63,8 +63,8 @@ public abstract class ProcessUtil {
|
||||
* @param ph the Process to get children of
|
||||
* @return a list of child ProcessHandles
|
||||
*/
|
||||
public static List<ProcessHandle> getAllChildren(ProcessHandle ph) {
|
||||
return ph.allChildren()
|
||||
public static List<ProcessHandle> getDescendants(ProcessHandle ph) {
|
||||
return ph.descendants()
|
||||
.filter(ProcessUtil::isNotWindowsConsole)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@ -117,7 +117,7 @@ public abstract class ProcessUtil {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
subprocesses = getAllChildren(ph);
|
||||
subprocesses = getDescendants(ph);
|
||||
count = subprocesses.size();
|
||||
System.out.printf(" waiting for subprocesses of %s to start," +
|
||||
" expected: %d, current: %d%n", ph, nchildren, count);
|
||||
@ -133,7 +133,7 @@ public abstract class ProcessUtil {
|
||||
* @return the ProcessHandle
|
||||
*/
|
||||
public static ProcessHandle destroyProcessTree(ProcessHandle p) {
|
||||
Stream<ProcessHandle> children = p.allChildren().filter(ProcessUtil::isNotWindowsConsole);
|
||||
Stream<ProcessHandle> children = p.descendants().filter(ProcessUtil::isNotWindowsConsole);
|
||||
children.forEach(ph -> {
|
||||
System.out.printf("destroyProcessTree destroyForcibly%n");
|
||||
printProcess(ph);
|
||||
|
@ -193,21 +193,21 @@ public class TreeTest extends ProcessUtil {
|
||||
}
|
||||
|
||||
// show the complete list of children (for debug)
|
||||
List<ProcessHandle> allChildren = getAllChildren(p1Handle);
|
||||
printf(" allChildren: %s%n",
|
||||
allChildren.stream().map(p -> p.getPid())
|
||||
.collect(Collectors.toList()));
|
||||
List<ProcessHandle> descendants = getDescendants(p1Handle);
|
||||
printf(" descendants: %s%n",
|
||||
descendants.stream().map(p -> p.getPid())
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
// Verify that all spawned children show up in the allChildren List
|
||||
// Verify that all spawned children show up in the descendants List
|
||||
processes.forEach((p, parent) -> {
|
||||
Assert.assertEquals(p.isAlive(), true, "Child should be alive: " + p);
|
||||
Assert.assertTrue(allChildren.contains(p), "Spawned child should be listed in allChildren: " + p);
|
||||
Assert.assertTrue(descendants.contains(p), "Spawned child should be listed in descendants: " + p);
|
||||
});
|
||||
|
||||
// Closing JavaChild's InputStream will cause all children to exit
|
||||
p1.getOutputStream().close();
|
||||
|
||||
for (ProcessHandle p : allChildren) {
|
||||
for (ProcessHandle p : descendants) {
|
||||
try {
|
||||
p.onExit().get(); // wait for the child to exit
|
||||
} catch (ExecutionException e) {
|
||||
@ -228,9 +228,9 @@ public class TreeTest extends ProcessUtil {
|
||||
/**
|
||||
* Test destroy of processes.
|
||||
* A JavaChild is started and it starts three children.
|
||||
* Each one is then checked to be alive and listed by allChildren
|
||||
* Each one is then checked to be alive and listed by descendants
|
||||
* and forcibly destroyed.
|
||||
* After they exit they should no longer be listed by allChildren.
|
||||
* After they exit they should no longer be listed by descendants.
|
||||
*/
|
||||
@Test
|
||||
public static void test3() {
|
||||
@ -263,24 +263,24 @@ public class TreeTest extends ProcessUtil {
|
||||
Assert.assertTrue(spawnCount.await(Utils.adjustTimeout(30L), TimeUnit.SECONDS),
|
||||
"Timeout waiting for processes to start");
|
||||
|
||||
// Debugging; list allChildren that are not expected in processes
|
||||
List<ProcessHandle> allChildren = ProcessUtil.getAllChildren(p1Handle);
|
||||
long count = allChildren.stream()
|
||||
// Debugging; list descendants that are not expected in processes
|
||||
List<ProcessHandle> descendants = ProcessUtil.getDescendants(p1Handle);
|
||||
long count = descendants.stream()
|
||||
.filter(ph -> !processes.containsKey(ph))
|
||||
.count();
|
||||
if (count > 0) {
|
||||
allChildren.stream()
|
||||
descendants.stream()
|
||||
.filter(ph -> !processes.containsKey(ph))
|
||||
.forEach(ph1 -> ProcessUtil.printProcess(ph1, "Extra process: "));
|
||||
ProcessUtil.logTaskList();
|
||||
Assert.assertEquals(0, count, "Extra processes in allChildren");
|
||||
Assert.assertEquals(0, count, "Extra processes in descendants");
|
||||
}
|
||||
|
||||
// Verify that all spawned children are alive, show up in the allChildren list
|
||||
// Verify that all spawned children are alive, show up in the descendants list
|
||||
// then destroy them
|
||||
processes.forEach((p, parent) -> {
|
||||
Assert.assertEquals(p.isAlive(), true, "Child should be alive: " + p);
|
||||
Assert.assertTrue(allChildren.contains(p), "Spawned child should be listed in allChildren: " + p);
|
||||
Assert.assertTrue(descendants.contains(p), "Spawned child should be listed in descendants: " + p);
|
||||
p.destroyForcibly();
|
||||
});
|
||||
Assert.assertEquals(processes.size(), newChildren, "Wrong number of children");
|
||||
@ -305,8 +305,8 @@ public class TreeTest extends ProcessUtil {
|
||||
p1.destroyForcibly();
|
||||
p1.waitFor();
|
||||
|
||||
// Verify that none of the spawned children are still listed by allChildren
|
||||
List<ProcessHandle> remaining = getAllChildren(self);
|
||||
// Verify that none of the spawned children are still listed by descendants
|
||||
List<ProcessHandle> remaining = getDescendants(self);
|
||||
Assert.assertFalse(remaining.remove(p1Handle), "Child p1 should have exited");
|
||||
remaining = remaining.stream().filter(processes::containsKey).collect(Collectors.toList());
|
||||
Assert.assertEquals(remaining.size(), 0, "Subprocess(es) should have exited: " + remaining);
|
||||
@ -415,28 +415,28 @@ public class TreeTest extends ProcessUtil {
|
||||
Assert.assertTrue(spawnCount.await(Utils.adjustTimeout(30L), TimeUnit.SECONDS),
|
||||
"Timeout waiting for processes to start");
|
||||
|
||||
// Debugging; list allChildren that are not expected in processes
|
||||
List<ProcessHandle> allChildren = ProcessUtil.getAllChildren(p1Handle);
|
||||
long count = allChildren.stream()
|
||||
// Debugging; list descendants that are not expected in processes
|
||||
List<ProcessHandle> descendants = ProcessUtil.getDescendants(p1Handle);
|
||||
long count = descendants.stream()
|
||||
.filter(ph -> !processes.containsKey(ph))
|
||||
.count();
|
||||
if (count > 0) {
|
||||
allChildren.stream()
|
||||
descendants.stream()
|
||||
.filter(ph -> !processes.containsKey(ph))
|
||||
.forEach(ph1 -> ProcessUtil.printProcess(ph1, "Extra process: "));
|
||||
ProcessUtil.logTaskList();
|
||||
Assert.assertEquals(0, count, "Extra processes in allChildren");
|
||||
Assert.assertEquals(0, count, "Extra processes in descendants");
|
||||
}
|
||||
|
||||
Assert.assertEquals(getChildren(p1Handle).size(),
|
||||
factor, "expected direct children");
|
||||
count = getAllChildren(p1Handle).size();
|
||||
count = getDescendants(p1Handle).size();
|
||||
long totalChildren = factor * factor * factor + factor * factor + factor;
|
||||
Assert.assertTrue(count >= totalChildren,
|
||||
"expected at least " + totalChildren + ", actual: " + count);
|
||||
|
||||
List<ProcessHandle> subprocesses = getAllChildren(p1Handle);
|
||||
printf(" allChildren: %s%n",
|
||||
List<ProcessHandle> subprocesses = getDescendants(p1Handle);
|
||||
printf(" descendants: %s%n",
|
||||
subprocesses.stream().map(p -> p.getPid())
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user