I'm trying to find a way to set a time limit for running a block of code (force-terminate it when time is up) without modifying the internals of the block of code. Here's what I tried to do: I first copied the TimeLimitedCodeBlock class from this link: Java-how-to-set-timeout
import java.util.*;
import java.util.concurrent.*;
public class TimeLimitedCodeBlock {
public static void runWithTimeout(final Runnable runnable, long timeout, TimeUnit timeUnit) throws Exception {
runWithTimeout(new Callable<Object>() {
@Override
public Object call() throws Exception {
runnable.run();
return null;
}
}, timeout, timeUnit);
}
public static <T> T runWithTimeout(Callable<T> callable, long timeout, TimeUnit timeUnit) throws Exception {
final ExecutorService executor = Executors.newSingleThreadExecutor();
final Future<T> future = executor.submit(callable);
executor.shutdown(); // This does not cancel the already-scheduled task.
try {
return future.get(timeout, timeUnit);
}
catch (TimeoutException e) {
future.cancel(true);
throw e;
}
catch (ExecutionException e) {
Throwable t = e.getCause();
if (t instanceof Error) {
throw (Error) t;
} else if (t instanceof Exception) {
throw (Exception) t;
} else {
throw new IllegalStateException(t);
}
}
}
}
And here is what I ran using the class defined above:
public static void main(String [] args)
{
try{
TimeLimitedCodeBlock.runWithTimeout(new Runnable()
{
public void run()
{
try{
while(true){}
}catch(Exception e){}
}},1,TimeUnit.SECONDS);
}
catch(Exception e){}
}
And it's not terminating. How should I fix it so that it terminates?
Code snippet that I've used to do something similar:
LOG.info("Time limited task started on monitored thread, with limit (" + limit + ")");
final ZonedDateTime start = nowUTC();
final Thread thread = new Thread(toRun);
thread.setDaemon(true);
final List<Throwable> exceptions = new ArrayList<>();
thread.setUncaughtExceptionHandler((t, e) -> {
exceptions.add(e);
});
thread.start();
// Check and wait for completion.
while (thread.isAlive()) {
if (!isWithinLimit(start, nowUTC())) {
LOG.error("Interrupting thread, did not complete before limit (" + limit + ")");
try {
thread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
throw new TimeLimitExceedException("Execution limit of " + limit
+ " exceeded. (Has been running since " + start + ")");
}
try {
Thread.sleep(POLLING_PERIOD.toMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// If it failed because of an exception, we want to trigger this.
if (!exceptions.isEmpty()) {
final Throwable exception = exceptions.get(0);
if (exception instanceof RuntimeException) {
throw (RuntimeException) exception;
} else {
throw new RuntimeException(exception);
}
}
final Duration runTime = Duration.between(start, nowUTC());
LOG.info("Time limited task has completed in (" + runTime + ") vs limit of (" + limit
+ ").");
TLDR: I just start whatever I'm running as a new thread that is set as a daemon (just in case it is the last thing running), then I get a reference to that and poll it, and call thread.interrupt() if it goes over the time limit.
Other context & bells and whistles