1

I'm trying to implement a TimeoutTask which will terminate after a given timeout. Here is what I have:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class TimeoutTask {

    private Long timeout;
    private TimeUnit unit;

    public TimeoutTask(Long timeout, TimeUnit unit) {
        this.timeout = timeout;
        this.unit = unit;
    }

    public <T> T execute(Callable<T> callable) {
        Objects.requireNonNull(timeout, "Timeout");
        Objects.requireNonNull(unit, "Time Unit");
        Objects.requireNonNull(callable, "Callable");

        ExecutorService service = 
                Executors.newFixedThreadPool(1);
        FutureTask<T> task = new FutureTask<T>(callable);
        service.execute(task);

        try {
            return task.get(timeout, unit);
        } catch (InterruptedException | ExecutionException | TimeoutException cause) {
            if(cause instanceof TimeoutException) {
                System.out.println("\nTimeout occured.");
                task.cancel(true);
            }
        } finally {
            System.out.println("Finally called.");
            service.shutdownNow();
        }

        return null;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        Callable<String> callable = () -> {         
            System.out.print("Enter something: ");
            BufferedReader consoleReader = 
                    new BufferedReader(new InputStreamReader(System.in));
            String str = consoleReader.readLine();
            return str;
        };

        TimeoutTask timeoutTask = new TimeoutTask(Long.valueOf(10), TimeUnit.SECONDS);
        timeoutTask.execute(callable);  
    }
}

It is working and I can see the message on timeout if there is no input given. However the process doesn't get terminated. Following is the screenshot from Eclipse's console, see the highlighted portion:

enter image description here

When I am giving any input the process terminated immediately. I am calling the FutureTask#cancel from the catch block also from finally block I am asking to shutdown the service by making a call to ExecutorService#shoutDownNow and I can see it is printing that the finally get called.

  • How can I stop the process being executed any further in case of timeout?
  • Is there any better approach to achieve this?

I am using Java 8.

Reference:

Update

In the answer of Marko Topolnik said that the cause is of non-interrupted Java IO; so I change the Callable as:

Callable<Long> callable = () -> {           
    long i = 0;
    for(; i <Long.MAX_VALUE;i++){

    }
    return i;
};

For this case the same thing happened.

Note: The Callable is just a sample.

Community
  • 1
  • 1
Tapas Bose
  • 28,796
  • 74
  • 215
  • 331

2 Answers2

1

You call

String str = consoleReader.readLine();

which means you are using the classic, blocking, non-interruptible Java IO. The method keeps blocking even after the timeout and shutdownNow() can't touch it, either, because it also just tries to interrupt the thread.

BTW I have run your code and the output shows "Finally called".

Update

Your updated Callable is just as non-interruptible as the original one. You must either call a method which declares to throw InterruptedException (such as Thread.sleep()) or check Thread.interrupted() yourself in the loop.

Community
  • 1
  • 1
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • Thank you sir for your response, I have thought the same thing, so I changed the `Callable` as shown in my update. In that case also the same thing happened. – Tapas Bose Sep 16 '14 at 08:13
  • Yes `Thread.interrupted()` worked in the loop. So there is no generic way to implement `TimeoutTask` for which the caller of this class don't need to think about stopping the `Thread`! – Tapas Bose Sep 16 '14 at 08:42
  • Exactly, and that's intentional: only a *cooperative* interruption mechanism provides you with a chance to implement safe cleanup. – Marko Topolnik Sep 16 '14 at 08:52
0

The issue you need to solve is that System.in.read() does not response to interrupts. See how to bypass this in Heinz M. Kabutz newsletter

alonana
  • 171
  • 2
  • 12