How to stop a command being executed after 4-5 seconds through process builder?
As I understand it you want to stop a subprocess if it runs longer than four or five seconds. This cannot be done directly with ProcessBuilder
(you can see that no relevant method exists in the class), but you can implement this behavior easily enough once the subprocess has begun.
Calling Process.waitFor()
as you do in your sample code is problematic because it will block your current thread indefinitely - if your process takes longer than five seconds .waitFor()
will not stop it. However .waitFor()
is overloaded and its sibling takes a timeout
argument.
public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException
Causes the current thread to wait, if necessary, until the subprocess represented by this Process object has terminated, or the specified waiting time elapses.
You can use this in tandem with Process.destroy()
to stop the process if it takes too long. For example:
Process process = new ProcessBuilder(command, and, arguments)
.redirectErrorStream(true)
.directory(workingDir)
.start();
process.waitFor(5, TimeUnit.SECONDS);
process.destroy();
process.waitFor(); // wait for the process to terminate
This relies on the fact that Process.destroy()
is a no-op when called on an already-finished subprocess. Before Java 9 this behavior was not documented, but in practice has always been the case. The alternative would be to inspect the return value of .waitFor()
, but this would introduce a TOCTTOU race.
What about Process.destroyForcibly()
? Generally speaking you should not call this method (another thing the JDK could be clearer about), however if a process is truly hung it may become necessary. Ideally you should ensure your subprocesses are well-behaved, but if you must use .destroyForcibly()
this is how I would recommend doing so:
// Option 2
process.waitFor(5, TimeUnit.SECONDS); // let the process run for 5 seconds
process.destroy(); // tell the process to stop
process.waitFor(10, TimeUnit.SECONDS); // give it a chance to stop
process.destroyForcibly(); // tell the OS to kill the process
process.waitFor(); // the process is now dead
This ensures that misbehaving processes will be killed promptly, while still giving properly implemented programs time to exit upon being instructed. The exact behavior of .destroy()
and .destroyForcibly()
is OS-specific, but on Linux we can see that they correspond to SIGTERM
and SIGKILL
:
int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
kill(pid, sig);
You should rarely have a need to call .destroyForcibly()
, and I would suggest only adding it if you discover it is necessary.
Option 2 is conceptually similar to using the timeout
command like so:
$ timeout --kill-after=10 5 your_command
It's easy enough to replicate Process.waitFor(long, TimeUnit)
in Java 7, there's nothing magic about the default Java 8 implementation:
public boolean waitFor(long timeout, TimeUnit unit)
throws InterruptedException
{
long startTime = System.nanoTime();
long rem = unit.toNanos(timeout);
do {
try {
exitValue();
return true;
} catch(IllegalThreadStateException ex) {
if (rem > 0)
Thread.sleep(
Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
}
rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
} while (rem > 0);
return false;
}
Ganesh S
Updated on June 14, 2022Comments
-
Ganesh S almost 2 years
Reference code :
ProcessBuilder ps4; Process pr4 = null; String batchFile3 = new File(path + "/src/example.sh"); ps4 = new ProcessBuilder(batchFile3.getAbsolutePath()); ps4.redirectErrorStream(true); ps4.directory(new File(path + "/src/")); pr4 = ps4.start(); BufferedReade readRun = new BufferedReader(new InputStreamReader(pr4.getInputStream())); if(pr4.waitFor()==0) { } String line,stre; while ((line = readRun.readLine()) != null) { System.out.print("-----" + line); if (line != null) { stre += line; } }
Here I have result in stre string it might be error or output generated by batch file which I am executing.
I want to stop execution of batch file if it is taking more that 4-5 seconds to exectute and kill that batch file execution process.
also in that case I should be able to return back to program to process a block which will execute only if this delay in processing of batch file occurs other wise that block should not be processed.
-
Admin about 8 yearsThis has nothing to do with batch-file. It's a Java question (and a very basic programming question at that).
-
Ganesh S about 8 yearsThank You, I think this will help.
-
Ar5hv1r about 8 yearsThere's rarely a need to call
.destroyForcibly()
; under the covers (on Linux) it sends aSIGKILL
while.destroy()
sends the much saferSIGTERM
. You should only use.destroyForcibly()
if you've previously tried to.destroy()
the process and, after a grace period, the process continues running. -
Ganesh S about 8 yearsThis works for java 8 but what about java 7 ? THere is no method waitFor with timeout parameter.
-
Ar5hv1r about 8 years@GaneshS you'll need to replicate the timeout behavior with a polling loop using
Thread.sleep()
andProcess.isAlive()
. -
Ganesh S about 8 yearsI have came up with better solution to this, I have added "timeout -s SIGKILL -k 20s 10s" as prefix to command which I am executing through batch file. And it is working. It works with both Java 7 as well as Java 8.
-
Ganesh S about 8 yearsThere is no Process.isAlive() for java 7. So can not go with Thread.sleep() and Process.isALive() combination.
-
Ar5hv1r about 8 years@GaneshS apologies, I was commenting from my phone. You can replicate
isAlive()
withexitValue()
(IllegalThreadStateException
means still-alive). -
Ar5hv1r about 8 yearsYou certainly can use the
timeout
command if that works for your use-case, though it tightly-couples you to Linux. There are a number of ways the subprocess could be modified to run for no more than n seconds. Note that, as I mention in my answer, you should avoidSIGKILL
; useSIGTERM
orSIGINT
instead. Particularly since you also specify-k
there's no reason to initially send aSIGKILL
.