Progress monitors services are very useful in Eclipse. You use them to display progress of some long running operations to users. As I said very good. But like every other useful thing it can change to beast. This happened in Vortex too. Imagine that you want to report progress of project download to user.
Easy no brain code:
void doDownload(IProgressMonitor monitor)
{
monitor.beginTask("Downloading project...");
try {
// do the actual download here...
}
finally {
monitor.done();
}
}
Easy, nope? Now imagine that you call this function inside monitor.
public void doDownload(IProgressMonitor monitor)
{
synchornized(lock) {
doDownload(monitor);
}
}
Ok, now you want to ask me what's wrong? The deal is that I'm using some other thread to do the actual download processing. This other thread executes some events to the listeners, and some of those listeners are running in UI thread updating some controls. So they use Display.asyncExec(...) function to run code in UI thread. Ok, so where is the problem? The problem is that some Eclipse progress displaying services are acquiring some other locks, and are executing some code with other locks they shouldn't. I explain this later.
The actual lock progress service use is RunnableLock. In comments of this class is that this is used that async and sync runnables do not interfere. They wait in asyncExec (in class Synchronizer) like this:
void asyncExec(Runnable runnable) {
// Some code ...
RunnableLock lock = new RunnableLock(runnable);
synchronized(lock) {
addToQueue(lock);
while(lock.finished() == false)
lock.wait();
}And they are running the queue:
boolean runAsyncMessages(boolean all) {
synchronized(lock) {
lock.runnable.run();
lock.setFinished(true);
}
}
So let's summarize it. The background thread acquires our lock, then progress monitor beast acquires runnable lock, and waits. The UI thread acquires RunnableLock at first, and then it happens. There are apparently some UI event loop executed by these listeners which runs our event listeners that want to acquire our background thread lock. And the deadlock is here. This deadlock happened twice today, and it's hard to reproduce, so I cannot give you any stack trace... [-;
The bug on eclipse for has been filled here. So please feel free to add some comments.
Morale of this story? Be carefull. [-; When dealing with threads, there are not only your locks.
Solution - I use my own version of AccumulatingProgressMonitor in all entries which are called by some progress services. In this class I just have replaced all syncExec()s with asyncExec()s.
| 01 | 02 | 03 | 04 | 05 | 06 | 07 |
| 08 | 09 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |