| /******************************************************************************* |
| * Copyright (c) 2003, 2010 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| * Jeremiah Lott (jeremiah.lott@timesys.com) - fix for deadlock bug 76378 |
| * |
| *******************************************************************************/ |
| package org.eclipse.ui.internal; |
| |
| import org.eclipse.core.runtime.jobs.LockListener; |
| import org.eclipse.swt.widgets.Display; |
| |
| /** |
| * The UI lock listener is used to prevent the UI thread from deadlocking on |
| * a lock when the thread owning the lock is attempting to syncExec. |
| */ |
| public class UILockListener extends LockListener { |
| |
| /** |
| * The Queue is the construct that keeps track of Semaphores. |
| */ |
| public class Queue { |
| private static final int BASE_SIZE = 8; |
| |
| protected Semaphore[] elements = new Semaphore[BASE_SIZE]; |
| |
| protected int head = 0; |
| |
| protected int tail = 0; |
| |
| /** |
| * Add the semaphore to the queue. |
| * @param element |
| */ |
| public synchronized void add(Semaphore element) { |
| int newTail = increment(tail); |
| if (newTail == head) { |
| grow(); |
| newTail = tail + 1; |
| } |
| elements[tail] = element; |
| tail = newTail; |
| } |
| |
| private void grow() { |
| int newSize = elements.length * 2; |
| Semaphore[] newElements = new Semaphore[newSize]; |
| if (tail >= head) { |
| System.arraycopy(elements, head, newElements, head, size()); |
| } else { |
| int newHead = newSize - (elements.length - head); |
| System.arraycopy(elements, 0, newElements, 0, tail + 1); |
| System.arraycopy(elements, head, newElements, newHead, |
| (newSize - newHead)); |
| head = newHead; |
| } |
| elements = newElements; |
| } |
| |
| private int increment(int index) { |
| return (index == (elements.length - 1)) ? 0 : index + 1; |
| } |
| |
| /** |
| * Remove the next semaphore to be woken up. |
| * @return |
| */ |
| public synchronized Semaphore remove() { |
| if (tail == head) { |
| return null; |
| } |
| Semaphore result = elements[head]; |
| elements[head] = null; |
| head = increment(head); |
| //reset the queue if it is empty and it has grown |
| if (tail == head && elements.length > BASE_SIZE) { |
| elements = new Semaphore[BASE_SIZE]; |
| tail = head = 0; |
| } |
| return result; |
| } |
| |
| private int size() { |
| return tail > head ? (tail - head) |
| : ((elements.length - head) + tail); |
| } |
| } |
| |
| protected Display display; |
| |
| protected final Queue pendingWork = new Queue(); |
| |
| protected Semaphore currentWork = null; |
| |
| protected Thread ui; |
| |
| /** |
| * Create a new instance of the receiver. |
| * @param display |
| */ |
| public UILockListener(Display display) { |
| this.display = display; |
| } |
| |
| public void aboutToRelease() { |
| if (isUI()) { |
| ui = null; |
| } |
| } |
| |
| public boolean aboutToWait(Thread lockOwner) { |
| if (isUI()) { |
| // If a syncExec was executed from the current operation, it |
| // has already acquired the lock. So, just return true. |
| if (currentWork != null |
| && currentWork.getOperationThread() == lockOwner) { |
| return true; |
| } |
| ui = Thread.currentThread(); |
| try { |
| doPendingWork(); |
| } finally { |
| //UI field may be nulled if there is a nested wait during execution |
| //of pending work, so make sure it is assigned before we start waiting |
| ui = Thread.currentThread(); |
| } |
| } |
| return false; |
| } |
| |
| void addPendingWork(Semaphore work) { |
| pendingWork.add(work); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.core.runtime.jobs.LockListener#canBlock() |
| */ |
| public boolean canBlock() { |
| return !isUI(); |
| } |
| |
| /** |
| * Should always be called from the UI thread. |
| */ |
| void doPendingWork() { |
| //clear the interrupt flag that we may have set in interruptUI() |
| Thread.interrupted(); |
| Semaphore work; |
| while ((work = pendingWork.remove()) != null) { |
| //remember the old current work before replacing, to handle |
| //the nested waiting case (bug 76378) |
| Semaphore oldWork = currentWork; |
| try { |
| currentWork = work; |
| Runnable runnable = work.getRunnable(); |
| if (runnable != null) { |
| runnable.run(); |
| } |
| |
| } finally { |
| currentWork = oldWork; |
| work.release(); |
| } |
| } |
| } |
| |
| void interruptUI() { |
| display.getThread().interrupt(); |
| } |
| |
| boolean isLockOwner() { |
| return isLockOwnerThread(); |
| } |
| |
| boolean isUI() { |
| return (!display.isDisposed()) |
| && (display.getThread() == Thread.currentThread()); |
| } |
| |
| boolean isUIWaiting() { |
| return (ui != null) && (Thread.currentThread() != ui); |
| } |
| } |