Latest ESXX Release


Saturday, June 6, 2009

ThreadPoolExecutor deadlocks

I'm currently trying to get ESXX running on Google's App Engine. One of the problems are that GAE won't let you create background threads or timers, something which most applications, including ESXX, often do.

My initial plan was to switch from a plain ThreadPoolExecutor and Timers to a ScheduledExecutorService. Once done, I would write a GAE-specific, single-threaded version of that class tries to mimic the intended behaviour as good as possible.

The problem is that the ScheduledExecutorService class is a fixed-size thread pool using an unbounded work queue, which just doesn't work in ESXX. Consider this simple example:

import java.util.concurrent.*;

class STPEDeadlock {
public static void main(String[] args) {
final ExecutorService exe =
new ScheduledThreadPoolExecutor(1, new ThreadPoolExecutor.CallerRunsPolicy());

System.out.println(Thread.currentThread() + ": Submitting job 1");
Future j1 = exe.submit(new Runnable() {
@Override public void run() {
System.out.println(Thread.currentThread() + ": Submitting job 2");
Future j2 = exe.submit(new Runnable() {
@Override public void run() {
System.out.println(Thread.currentThread() + ": Running job 2");
}
});

try {
System.out.println(Thread.currentThread() + ": Waiting for job 2");
j2.get();
}
catch (Throwable t) {
t.printStackTrace();
}
}
});

try {
System.out.println(Thread.currentThread() + ": Waiting for job 1");
j1.get();
}
catch (Throwable t) {
t.printStackTrace();
}

System.out.println(Thread.currentThread() + ": All done");
exe.shutdown();
}
}
It creates a fixed-size thread pool, and adds and waits for a job. The job, in turn, adds and waits for another job. The problem is that there are no worker threads left, so the second job is just queued and never executed, resulting in a deadlock.

If you replace the executor with
final ExecutorService exe = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new SynchronousQueue(),
new ThreadPoolExecutor.CallerRunsPolicy());
then all is fine again. This is the fixed-size executor configuration I use in ESXX. Unfortunately, there is no way to configure a ScheduledExecutorService to match this behavior.

What were the Sun developers thinking??

No comments: