48

At the moment I execute a native process using the following:

java.lang.Process process = Runtime.getRuntime().exec(command); 
int returnCode = process.waitFor();

Suppose instead of waiting for the program to return I wish to terminate if a certain amount of time has elapsed. How do I do this?

1

6 Answers 6

57

All other responses are correct but it can be made more robust and efficient using FutureTask.

For example,

private static final ExecutorService THREAD_POOL 
    = Executors.newCachedThreadPool();

private static <T> T timedCall(Callable<T> c, long timeout, TimeUnit timeUnit)
    throws InterruptedException, ExecutionException, TimeoutException
{
    FutureTask<T> task = new FutureTask<T>(c);
    THREAD_POOL.execute(task);
    return task.get(timeout, timeUnit);
}

final java.lang.Process[] process = new Process[1];
try {
    int returnCode = timedCall(new Callable<Integer>() {
        public Integer call() throws Exception {
            process[0] = Runtime.getRuntime().exec(command); 
            return process[0].waitFor();
        }
    }, timeout, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    process[0].destroy();
    // Handle timeout here
}

If you do this repeatedly, the thread pool is more efficient because it caches the threads.

Sign up to request clarification or add additional context in comments.

4 Comments

The handle timeout here could be a little more robust for an example. I have a couple of mechanisms I use, but for the simplest case, use something like: catch (TimeoutException e) { System.exit(-1);}
Type argument cannot be of primitive type. Please replace int with Integer.
Although the TimeoutException will be called after the specified timeout, the command itself will continue to execute in the background on the other thread unless we call destroy() when TimeoutException is thrown. (the same is seen even after a shutdownNow() call on the executor)
THREAD_POOL should be shut down when the application is stopped, otherwise it might prevent the JVM from terminating for up to 60 seconds (the maximum idle time for worker threads of newCachedThreadPool()) because non-daemon worker threads are still alive.
25

If you're using Java 8 or later (API 26 or later for Android) you could simply use the waitFor with timeout:

Process p = ...
if(!p.waitFor(1, TimeUnit.MINUTE)) {
    //timeout - kill the process. 
    p.destroy(); // consider using destroyForcibly instead
}

2 Comments

In Android that is only available in API 26 and above though.
@Bogdan, if (and only if) waitFor(...) returned true, you can call p.exitValue() to get the exit code.
19

This is how the Plexus CommandlineUtils does it:

Process p;

p = cl.execute();

...

if ( timeoutInSeconds <= 0 )
{
    returnValue = p.waitFor();
}
else
{
    long now = System.currentTimeMillis();
    long timeoutInMillis = 1000L * timeoutInSeconds;
    long finish = now + timeoutInMillis;
    while ( isAlive( p ) && ( System.currentTimeMillis() < finish ) )
    {
        Thread.sleep( 10 );
    }
    if ( isAlive( p ) )
    {
        throw new InterruptedException( "Process timeout out after " + timeoutInSeconds + " seconds" );
    }
    returnValue = p.exitValue();
}

public static boolean isAlive( Process p ) {
    try
    {
        p.exitValue();
        return false;
    } catch (IllegalThreadStateException e) {
        return true;
    }
}

3 Comments

Ewwww...so every 10 milliseconds it relies on p.exitValue() throwing IllegalThreadStateException to indicate "still running"?
@OgrePsalm33 It's awful, but sadly Java gives no nicer way up to Java 7. Java8 gives a "p.isAlive()"
Why the loop? Why not to run a TimerTask which will check "aliveness" only once upon timeout expiration?
6

What about the Groovy way

public void yourMethod() {
    ...
    Process process = new ProcessBuilder(...).start(); 
    //wait 5 secs or kill the process
    waitForOrKill(process, TimeUnit.SECONDS.toMillis(5));
    ...
}

public static void waitForOrKill(Process self, long numberOfMillis) {
    ProcessRunner runnable = new ProcessRunner(self);
    Thread thread = new Thread(runnable);
    thread.start();
    runnable.waitForOrKill(numberOfMillis);
}

protected static class ProcessRunner implements Runnable {
    Process process;
    private boolean finished;

    public ProcessRunner(Process process) {
        this.process = process;
    }

    public void run() {
        try {
            process.waitFor();
        } catch (InterruptedException e) {
            // Ignore
        }
        synchronized (this) {
            notifyAll();
            finished = true;
        }
    }

    public synchronized void waitForOrKill(long millis) {
        if (!finished) {
            try {
                wait(millis);
            } catch (InterruptedException e) {
                // Ignore
            }
            if (!finished) {
                process.destroy();
            }
        }
    }
}

Comments

4

just modified a bit according to my requirement. time out is 10 seconds here. process is getting destroyed after 10 seconds if it is not exiting.

public static void main(String arg[]) {

    try {
        Process p = Runtime.getRuntime().exec("\"C:/Program Files/VanDyke Software/SecureCRT/SecureCRT.exe\"");
        long now = System.currentTimeMillis(); 
        long timeoutInMillis = 1000L * 10; 
        long finish = now + timeoutInMillis; 
        while ( isAlive( p ) ) { 
            Thread.sleep( 10 ); 
            if ( System.currentTimeMillis() > finish ) {
                p.destroy();
            }
        }
    } catch (Exception err) {
        err.printStackTrace();
    }
}

public static boolean isAlive( Process p ) {  
    try {  
        p.exitValue();  
        return false;  
    } catch (IllegalThreadStateException e) {  
        return true;  
    }  
}  

Comments

2

You'd need a 2. thread that interrupts the thread that calls .waitFor(); Some non trivial synchronization will be needed to make it robust, but the basics are:

TimeoutThread:

 Thread.sleep(timeout);
 processThread.interrupt();

ProcessThread:

  try {
      proc.waitFor(); 
    } catch (InterruptedException e) {
       proc.destroy();
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.