Lazy Runner Pattern

The guideline to execute a long running operation in a separate thread is well known, especially for GUI triggered stuff. However I did not come across any best practice to deal with lengthy operations creeping. For example scrolling faster then the content refresh rate will cause a series of content updates going on and on long after the user has finished scrolling. This post is to provide a reference solution to handle such a case.

The LazyRunner, I think I can call it a pattern, guarantees that only the latest Runnable submitted to the execute() method will actually run (unless external disruption such as thread interruption or system error happens). All the Runnables submitted while some other Runnable is being executed are ignored.

In result the maximum time to finish processing after last execution request is equal to
2 * <single runnable time> instead of
<number of requests> * <single runnable time>.

public class LazyRunner  {
	private volatile Runnable running;
	private volatile Runnable latest;
	
	public final void execute(final Runnable r) {
		latest = r;
		if (running == null) {
			running = latest;
			new Thread("lazy runner") {
				@Override
				public void run() {
					do {
						running = latest;
						running.run();
					}
					while (running != latest);
					running = null;
				};
			}.start();
		}
	}
}

An example of usage:

static class LongOperation implements Runnable {
	String s;
	public LongOperation(String s) { this.s = s; }

	@Override
	public void run() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {}
		System.out.println(s);
	}
}

public static void main(String[] args) throws InterruptedException {
	LazyRunner runner = new LazyRunner();
	
	runner.execute(new LongOperation("a"));
	runner.execute(new LongOperation("b"));
	Thread.sleep(100);
	runner.execute(new LongOperation("c"));
	Thread.sleep(100);
	runner.execute(new LongOperation("d"));
	Thread.sleep(100);
	runner.execute(new LongOperation("e"));
}

The output should print “b” and “e”.

A special case is slow SWT operations, for instance drawing text in GTK2 on Linux, which causes a noticeable rendering delay for StyledText with a few dozen of lines. A LazyRunnable in such scenario has to mingle with display.asyncExec() method to execute the Runnable in the GUI thread. It can be implemented as follows:

public class SwtLazyRunner  {
	private final Display display;
	private volatile Runnable running;
	private volatile Runnable latest;
	
	public SwtLazyRunner(Display display) {
		this.display = display;
	}

	public final void execute(final Runnable r) {
		latest = r;
		if (running == null) {
			running = latest;
			if (!display.isDisposed()) {
				display.asyncExec(new Runnable() {
					@Override
					public void run() {
						do {
							running = latest;
							running.run();
						}
						while (running != latest);
						running = null;
					}
				});
			}
		}
	}
}

I hope you find it useful.

Leave a comment