Hi,

The cleanup of Process state is handled by the implementation.

On Linux it waits for the exitStatus and when the process exits, it captures the status, drains any pending input into local buffers and closes the file descriptors. When the streams and Process implementation are GC'd any remaining cleanup is done. A Cleaner is registered for each stream to close the file descriptor.

On Windows, the cleaner of the Process object is used to close the Windows handle. Each stream has its own Cleaner registration to close its handle when the objects are GC'd. If you've seen cases of leaking file descriptors, zombie processes, or handles, that's a bug (hopefully reproducible).

I'm not sure using T-W-R would make it more robust, there would be additional complexity in mixing synchronous cleanup with the asynchronous cleanup.

Most of awkwardness of the Process API is related to independent states of the streams and the exit status itself and whether the application interprets a non-zero exit status in relation to the end-of-input streams; that interpretation is related to the particular application spawned.

The Process API also doesn't help, except for the timed `waitFor()` with a process that doesn't terminate, an empty standard input but waiting for input, etc.

The doc could be clearer that it is unnecessary to explicitly handle all of the process elements; ignore the exit status from the application spawned unless it means something.  One unfortunate aspect of streams is the lack of a distinction between a premature end-of-stream vs an explicit we're-done-here.  Ignore the error stream if there's no reason to believe it has any useful information, that also depends on what application was spawned.

The simple cases can be easier to code and should be and the more involved cases should take better advantage of more recent language and runtime features.

YMMV, Roger

On 4/15/25 8:30 AM, Ron Pressler wrote:

On 15 Apr 2025, at 10:51, fo...@univ-mlv.fr wrote:

----- Original Message -----
From: "Stuart Marks" <stuart.ma...@oracle.com>
To: "Remi Forax" <fo...@univ-mlv.fr>, "Ron Pressler" <ron.press...@oracle.com>, 
"David Alayachew"
<davidalayac...@gmail.com>
Cc: "cay horstmann" <cay.horstm...@gmail.com>, "core-libs-dev" 
<core-libs-dev@openjdk.org>
Sent: Tuesday, April 15, 2025 12:10:54 AM
Subject: Re: My experience using Java to do CLI Scripting

A few points here...

It might be possible to get away without any Process-related cleanup. For one,
there's no "close" method on Process or ProcessBuilder. The destroy() method
will
certainly clean up, but you don't want that; and waitFor() merely waits for
termination but doesn't actually clean anything up.

At least in the Unix ProcessImpl class, there's a bunch of infrastructure to
handle
exit of the underlying process, drain input from its stdout/stderr, and close
the
pipes. (I didn't look on Windows.) So setting aside termination, IOException,
and
what to do with stderr, it seems possible to just get the inputReader() and do
something with the characters it emits.
You mean by making the Process Closeable ?
It could make sense to make Process AutoCloseable similar to how 
ExecutorService now works, i.e. by having the close method call waitFor.

Getting a stream of lines with lines() seems like a reasonable thing to do if
you
want to process the output line by line.
with the caveat that you have to close() the returned stream (like 
Files.lines()), something people will often forget.

The three Process streams are created (by default) whether you ever use them or 
not, so if you did have to close them, you’d need to do it every time you use 
Process. The reason is that the IO scaffolding (including ensuring cleanup) 
needs to be set up as soon as the process is started, before we know if you’re 
interested in reading the output. I think that if you want the streams to *not* 
be opened, you need to ask ProcessBuilder explicitly with 
ProcessBuilder.Redirect.DISCARD.

It seems to me that on Unix, the pipes are all closed as soon as the process 
terminates. On Windows it’s more complicated and there may be Cleaners involved 
(and the buffering done by the OS rather than Java?), but on either platform it 
seems that it doesn’t matter whether you actually use the streams.

Perhaps Roger can clarify, or correct me if I’m wrong.

There are other possibilities, taking a nod from Files, which has methods
readAllLines() and readString(). Putting similar methods on some class in the
Reader
family might help considerably here. Or you could call Reader.transferTo(Writer)
that will send the characters to any Writer that might have a useful
destination.
+1 for readAllInputLines() and readInputString() on Process.
Wouldn’t it make more sense to add these to BufferedReader?

— Ron



Reply via email to