How to put an execution time limit on a background task

I’m trying to call some code which is longer running that will update the screen using push, however if the process is taking too long I want to be able to cancel it.

With that in mind I’m trying to use Guava’s SimpleTimeLimiter class but no matter what I do I can’t seem to stop the UI.getCurrent().access() process from stopping. I’ve tried both to put the SimpleTimeLimiter inside UI.getCurrent().access() method and outside of it but they both just continue to execute the process even if SimpleTimeLimiter throws a TimeoutException.

public static void limitExecutionTime(Consumer<UI> lambda)
{
    UI currentUI = UI.getCurrent();

    UI.getCurrent().access(() ->
    {
        try
        {
            SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).callWithTimeout(new Callable<Void>()
            {
                @Override
                public Void call()
                {
                    try
                    {
                        // This is needed to deal how Vaadin 8 handles UI's
                        UI.setCurrent(currentUI);
                        lambda.accept();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        return null;
                    }
                }
        }, 1, TimeUnit.SECONDS);
        
        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            NotificationUtils.showError("Execution took beyond the maximum allowed time.");
            currentUI.push();
        }
    });
}

In the above code if the method takes more than 1 second it will throw a TimeoutException and put up the notification window. However it will continue to execute the lambda.

As a result I’ve tried to do the opposite and put UI.getCurrent().access() in the public Void call() method but this had the exact same result…

Hi,
I didn’t understand if you want to update the ui (via Consumer) only at the end of computation or even while background task is running.

If you want to invoke Consumer only at the end of computation I think you can do something like this (did’t tried it howerver)

public static void limitExecutionTime(Consumer<UI> lambda) {
    UI currentUI = UI.getCurrent();

    CompletableFuture.runAsync(
      SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).runWithTimeout( () ->  {
        // do background computation
      })
    ).handle( (unused, throwable) ->
      currentUI.access( () -> {
        if (throwable != null) {
          NotificationUtils.showError("Execution took beyond the maximum allowed time.") );
        } else {
          lambda.accept(currentUI);
        }
      }
    );
}

You can implement it also using only Future and CompletableFuture, if you want to avoid using an external library like guava

Yes, and it is good to note the correct use of UI in the code example of Marco, i.e. not using UI.getCurrent() or UI.setCurrent() in background thread.

First thank you for the code. That being said unfortunately it doesn’t work. The first issue is that CompletableFuture.runAsync() requires a Runnable whereas SimpleTimeLimiter.create() returns a SimpleTimeLimiter. As far as I know there’s no way to get a Runnable from SimpleTimeLimiter…

The other issue is that I’m actually trying to update the UI from the long running task. Specifically I want to upgrade a ProgressBar.

The reason I need to end a task early is because users can generate reports using a scripting language and should someone do something nefarious, or by accident, we need to be able to shut down a rogue report process. In other words we need to limit the time each report generation is allowed to run to prevent DOS attacks (or accidents).

Hi,
sorry, you’re right, the code was wrong. To run SimpleTimeLimiter as runnable simply wrap it into a lamba expression.

    CompletableFuture.runAsync(() -> 
      SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).runWithTimeout( () ->  {
        // do background computation
      })
    ).handle( (unused, throwable) -> { ... })

To update the UI during the processing you can use the same technique (note that I assume you have PUSH mode set to automatic)

public static void limitExecutionTime(Consumer<UI> lambda) {
    UI currentUI = UI.getCurrent();

    CompletableFuture.runAsync(() -> 
      SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).runWithTimeout( () ->  {
        // do background computation
        currentUI.access(() -> lambda.accept(currentUI); );
        // do background computation
        currentUI.access(() -> lambda.accept(currentUI) );
        // ...
        
      })
    ).handle( (unused, throwable) ->
      currentUI.access( () -> {
        if (throwable != null) {
          NotificationUtils.showError("Execution took beyond the maximum allowed time.") );
        } else {
          // So someting with UI when computation has ended
          updateUIOnSuccess();
        }
      }
    );
}

HTH
Marco

Hi Stephan,

Here’s a very simple example of running a background thread which continues to execute and updates a UI according to condition. If the condition check fails, then the thread dies.

private void startProgressBarUpdaterThread() {
  final UI currentUI = UI.getCurrent();
  new Thread(() -> {

    while (isUploading()) {

      try {
        currentUI.access(() -> {
          updateProgress();
        });
      } catch (final UIDetachedException e) {
        // stop processing
        return;
      }

      try {
        Thread.sleep(ONE_SECOND);
      } catch (final InterruptedException e) {
        // ignore, nothing to do
      }
    }
  }).start();

}

This method is called by the main request thread, which is generating the data. updateProgress method updates the progress bar UI component based on the generated data.

Hope it helps! Regards.

Hi Stephan,

you can try this way (sorry, again I’ve got no time to test the code).
If it doesn’t work I will look at it tonight

HTH
Marco

public static void limitExecutionTime(Consumer<UI> lambda) {
  UI currentUI = UI.getCurrent();
  CompletableFuture<Void> job = new CompletableFuture<>();
  CompletableFuture.runAsync(() -> {
      try {
          SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).runWithTimeout( () ->  {
            // do background computation
            currentUI.access(() -> lambda.accept(currentUI); );
            // do background computation
            currentUI.access(() -> lambda.accept(currentUI) );
            // ...
          }, 1, TimeUnit.SECONDS);
          // Job ha executed within time limits, complete succesfully
          job.complete(null);
      } catch (Exception ex) {
          // Time out occured or job exception, complete with error
          job.completeExceptionally(ex);
      }
  });

  job.handle( (unused, throwable) ->
      currentUI.access( () -> {
        if (throwable != null) {
          NotificationUtils.showError("Execution took beyond the maximum allowed time.") );
        } else {
          // So someting with UI when computation has ended
          updateUIOnSuccess();
        }
      }
    );
}

Hi Marco,

Unfortunately that code also doesn’t compile, it requires a try/catch around the SimpleTimeLimiter.create() to compile, which basically defeats the handle() on runAsync :frowning:

Hi Marco,

I agree it’s not complex to run a background task, my problem is that I’m calling a report generator to which I do not have the ability to cancel the background task partway through it by checking in a loop if it’s taking too long, etc. Basically I need some way of forcing a background task to terminate after x amount of time and then updating the UI. The key issue is terminating the background task without having access to the code that runs the background task. All I have access to is basically ReportGenerator.longTimeToRender(); Yes I’m generating multiple reports but a single report in my loop could go into an infinite loop, so I can’t even check in the report loop…

Therefore I need some way to terminate that task outside of the task while at the same being able to update the UI. The problem is that any code that terminates the background task also affects the UI thread because I need to update the UI through currentUI.access().

In other words I need to interrupt the thread which also means I’m going to interrupt the UI related thread.

Thank you for the update Marco, it’s appreciated.

That being said I’m a little confused as to how that would be different than my original code (ignoring the UI.setCurrent(currentUI))? I suspect I’m missing something obvious that’s just escaping me…

Aren’t we basically doing the same thing except that my lambda doesn’t need to know it’s in a thread, and hence use currentUI.access() to do any screen updates?

Hi Steaphan,
from my POV the main difference between the code from the first post and my last one is that in the first the main UI thread is blocked at SimpleTimeLimiter.callWithTimeout call whereas the latter is starting the job in a separated thread, so the blocking call is does not affect the main UI thread.

Hi Marco,

Thank you, that helps understand the difference.

That being said the code unfortunately doesn’t seem to want to terminate my thread. The interrupt is called but the thread doesnt’ terminate. I’m wondering if it’s something to do with Vaadin’s access code…

My full code is:

runReportButton.addClickListener(click -> handleRunReportClick());

private void handleRunReportClick()
{
    // limitExecutionTime has been set to 50 milliseconds
    // and uses your exact code
    limitExecutionTime(currentUI -> generateReport(currentUI));
}

private void generateReport(UI currentUI)
{
    System.out.println(">>> Start");
    // waste time to go over the time limit
    for(int x=0; x<100000000; x++)
        new Random().nextInt();
    System.out.println(">>> Should not reach here");
}

Unfortunately no matter what I do I always get “Should not reach here” printed out.

I also avoided using Thread.sleep() or a variation because that would necessitate a catch block where if you remove Thread.sleep() then it will no longer compile.

By the way I even tried just calling the code directly in limitExecutionTime without using a Consumer, etc. but it made no difference.

Any suggestions?

Just to add confusion I’m wondering if it’s not something due to the Vaadin framework? Reason I say this is that if I run the following code by itself it works as expected. But if I hook it up to a button clickListener then it doesn’t get interrupted. Even if all the code is outside of the view, etc. in a self contained class…

public class TestTimeout
{
    public static void main(String args) throws Exception
    {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try
        {
            System.out.println("Started..");
            System.out.println(future.get(50, TimeUnit.MILLISECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            future.cancel(true);
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

class Task implements Callable<String>
{
    @Override
    public String call() throws Exception
    {
        System.out.println("Start task");
        // Just to demo a long running task of 4 seconds.
        for(int x=0; x<100000000; x++)
            new Random().nextInt();
        return "Task completed!";
    }
}

It’s not your code bur rather a simplified version, but it works nonetheless. Either way the code works outside of the server but not in the server…

Some progress but now it’s just getting weirder…

If I add Thread.sleep(1000) in the Task code then it stops the execution as expected. But if I have a task that takes 1000 milliseconds or more (same amount of time) then it doesn’t stop the execution. In a self contained program it always works as expected but in Vaadin it looks like it needs Thread.sleep(). In other words it seems to only be able to interupt the Task class if it’s in Thread.sleep(), otherwise it just keeps going…

UPDATE: Basically the interrupt only works correctly if it’s interrupted duing Thread.sleep(). If it’s interrupted anywhere else it just keeps on going. Not sure why it’s behaving differently only for Thread.sleep().

Hi Stephan,
try to add a sleep of 10 seconds before shutdown now and you will see that Task is not interupted.
You have to stop the task yourself by checking if the thread is interrupted.

With the following code you will have an output similar to

Started..
Start task
Terminated!
=== BEFORE sleep
Task Completed
=== AFTER sleep
=== AFTER shutdown
    public static class TestTimeout
    {
        public static void main(String[] args) throws Exception
        {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<String> future = executor.submit(new Task());

            try
            {
                System.out.println("Started..");
                System.out.println(future.get(50, TimeUnit.MILLISECONDS));
                System.out.println("Finished!");
            } catch (TimeoutException e) {
                future.cancel(true);
                System.out.println("Terminated!");
            }

            System.out.println("=== BEFORE sleep");
            Thread.sleep(10000);
            System.out.println("=== AFTER sleep");
            executor.shutdownNow();
            System.out.println("=== AFTER shutdown");
        }
    }

    static class Task implements Callable<String>
    {
        @Override
        public String call() throws Exception
        {
            System.out.println("Start task");
            // Just to demo a long running task of 4 seconds.
            for(int x=0; x<100000000; x++) {
                new Random().nextInt();
                //if (Thread.currentThread().isInterrupted()) {
                    //throw new RuntimeException("Stop me!");
                //}
            }
            System.out.println("Task Completed");
            return "Task completed!";
        }
    }

Stephen,
I’m a bit confused now :smiley:

Can we make a step backword and concetrate on how you will update the UI from the generateReport?
You said that in generateReport you will show up the progress or report generation, right?

Is generateReport composed by multiple steps and you will call ui.access() between them?

Hi Stephen,
I cooked up a little example. Take a look at https://github.com/mcollovati/vaadin-stop-thread-sample
and let me know if this is what yu want

HTH
Marco

Hi Marco,

Firstly thank you for all your help, it’s really appreciated!!

The main issue is that I don’t have the ability to throw an exception in the report generating code. In other words I set up everything and then call a third party library to generate the report based on the settings. The problem is that a report script could have an infinite loop or could DOS the server, therefore I need to limit the execution time on any single report.

The only progress I have is which report I’m on. In other words I need to batch reports. So for example I may need to run 10 reports as a batch. However any one single report can go into an infinite loop and I have no access to that code.

for(Report report: reports)
{
    report.generateReport();
    updateProgressBar();
}

In the above code I do not get any insight into the report class itself. My concern is not that the 10 reports combined could take say x seconds or minutes, but rather that a single report could go into an infinite loop. So for example report 3 could go into an infinite loop while all the other reports run in under 1 second. In that case I need a way to interrupt the report generation and kill the batch report task. In other words I don’t have the luxury of doing Thread.currentThread.isInterrupted() in the report.generateReport() code since that code is not accessible to me and that’s the code that can go into an infinite loop if the report script has for example a while(true) print hello world or something like that.

Which means that in the same code the loop with Random.nextInt() is not accessible, it’s all hidden in a 3rd party library. All I can do is terminate the report.generateReport()'s execution.

Update Please ignore my comments about this being specific to Vaadin, I just realized there was a missing System out in my non-Vaadin test project. Basically I need a ways to sandbox a chunk of code without having access to that code.

Hi Stephan,
now I have clear your needs.
Unfortunately the only way I see to achieve your goal is to run each report.generateReport() within a Thread and use the
deprecated
method Thread.stop() when processing takes too much time.
Absolutely not a good solution

HTH
Marco

    public void doAll(List<Report> reports) {
        for (Report report : reports) {
            runReport(report, 500)
                .thenRun(this::updateProgressBar)
                .exceptionally( ex -> {
                    notifyError();
                    return null;
                });
            ;
        }
    }

    public static CompletableFuture<?> runReport(Report report, long timeoutInMillis) {
        CompletableFuture<?> reportJob = new CompletableFuture<>();
        Thread t = new Thread(() -> {
            report.generateReport();
            reportJob.complete(null);
        });
        t.start();
        try {
            reportJob.get(timeoutInMillis, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            t.stop();
            reportJob.completeExceptionally(e);
        }
        return reportJob;
    }

Hi Marcio,

The problem with interrupt and so on is that it assumes you are checking that an interrupt occured and will stop your long running process. Unfortunately the third party library I’m using doesn’t.