Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/stringsubstitution/SelectedResourceManager.java131
1 files changed, 101 insertions, 30 deletions
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/stringsubstitution/SelectedResourceManager.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/stringsubstitution/SelectedResourceManager.java
index 352a8d6cf..2df6c515a 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/stringsubstitution/SelectedResourceManager.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/stringsubstitution/SelectedResourceManager.java
@@ -13,9 +13,20 @@
*******************************************************************************/
package org.eclipse.debug.internal.ui.stringsubstitution;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
import java.util.Iterator;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.jface.text.ITextSelection;
@@ -23,6 +34,7 @@ import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbenchPage;
@@ -37,9 +49,16 @@ import org.eclipse.ui.IWorkbenchWindow;
*/
public class SelectedResourceManager {
+ // Limit in seconds to wait on UI for accessing data
+ private static final int MAX_UI_WAIT_TIME = 10;
+
// singleton
private static SelectedResourceManager fgDefault;
+ // Used to avoid deadlocks while accessing UI thread from non UI code
+ private static ExecutorService executor = Executors.newSingleThreadExecutor();
+
+
/**
* Returns the singleton resource selection manager
*
@@ -69,14 +88,11 @@ public class SelectedResourceManager {
* @since 3.3
*/
public IStructuredSelection getCurrentSelection() {
- if(DebugUIPlugin.getStandardDisplay().getThread().equals(Thread.currentThread())) {
- return getCurrentSelection0();
- }
- else {
- final IStructuredSelection[] selection = new IStructuredSelection[1];
- DebugUIPlugin.getStandardDisplay().syncExec(() -> selection[0] = getCurrentSelection0());
- return selection[0];
+ IStructuredSelection selection = getFromUI(this::getCurrentSelection0);
+ if (selection == null) {
+ selection = StructuredSelection.EMPTY;
}
+ return selection;
}
/**
@@ -85,7 +101,7 @@ public class SelectedResourceManager {
*
* @since 3.4
*/
- private IStructuredSelection getCurrentSelection0() {
+ IStructuredSelection getCurrentSelection0() {
IWorkbenchWindow window = DebugUIPlugin.getActiveWorkbenchWindow();
if(window != null) {
IWorkbenchPage page = window.getActivePage();
@@ -119,14 +135,8 @@ public class SelectedResourceManager {
* @return selected resource or <code>null</code>
*/
public IResource getSelectedResource() {
- if(DebugUIPlugin.getStandardDisplay().getThread().equals(Thread.currentThread())) {
- return getSelectedResource0();
- }
- else {
- final IResource[] resource = new IResource[1];
- DebugUIPlugin.getStandardDisplay().syncExec(() -> resource[0] = getSelectedResource0());
- return resource[0];
- }
+ IResource resource = getFromUI(this::getSelectedResource0);
+ return resource;
}
/**
@@ -177,14 +187,8 @@ public class SelectedResourceManager {
* @return the current text selection as a <code>String</code> or <code>null</code>
*/
public String getSelectedText() {
- if(DebugUIPlugin.getStandardDisplay().getThread().equals(Thread.currentThread())) {
- return getSelectedText0();
- }
- else {
- final String[] text = new String[1];
- DebugUIPlugin.getStandardDisplay().syncExec(() -> text[0] = getSelectedText0());
- return text[0];
- }
+ String text = getFromUI(this::getSelectedText0);
+ return text;
}
/**
@@ -224,14 +228,81 @@ public class SelectedResourceManager {
* @since 3.2
*/
public IWorkbenchWindow getActiveWindow() {
- if(DebugUIPlugin.getStandardDisplay().getThread().equals(Thread.currentThread())) {
- return DebugUIPlugin.getActiveWorkbenchWindow();
+ IWorkbenchWindow window = getFromUI(DebugUIPlugin::getActiveWorkbenchWindow);
+ return window;
+ }
+
+ private <T> T getFromUI(Callable<T> call) {
+ try {
+ if (Display.getCurrent() != null) {
+ return call.call();
+ } else {
+ return runInUIThreadWithTimeout(call, MAX_UI_WAIT_TIME, TimeUnit.SECONDS);
+ }
+ } catch (TimeoutException e) {
+ reportTimeout();
+ return null;
+ } catch (Exception e) {
+ DebugUIPlugin.log(e);
+ return null;
}
- else {
- final IWorkbenchWindow[] window = new IWorkbenchWindow[1];
- DebugUIPlugin.getStandardDisplay().syncExec(() -> window[0] = DebugUIPlugin.getActiveWorkbenchWindow());
- return window[0];
+ }
+
+ /**
+ * Tries to run the task in the UI thread, and gives up if UI thread does not
+ * answer after given timeout
+ *
+ * @param timeout to wait for the UI lock
+ * @return may return null
+ * @throws Exception
+ */
+ static <V> V runInUIThreadWithTimeout(Callable<V> callable, long timeout, TimeUnit units) throws Exception {
+ FutureTask<V> task = new FutureTask<>(() -> syncExec(callable));
+ executor.execute(task);
+ return task.get(timeout, units);
+ }
+
+ static <V> V syncExec(Callable<V> callable) throws Exception {
+ AtomicReference<V> ref = new AtomicReference<>();
+ AtomicReference<Exception> ex = new AtomicReference<>();
+ DebugUIPlugin.getStandardDisplay().syncExec(() -> {
+ try {
+ ref.set(callable.call());
+ } catch (Exception e) {
+ ex.set(e);
+ }
+ });
+ if (ex.get() != null) {
+ throw ex.get();
}
+ return ref.get();
}
+ /**
+ * Reports an error the log with thread stack information for current and UI threads
+ */
+ private static void reportTimeout() {
+ Thread nonUiThread = Thread.currentThread();
+
+ String msg = "To avoid deadlock while executing Display.syncExec() from a non UI thread '" //$NON-NLS-1$
+ + nonUiThread.getName() + "', operation was cancelled."; //$NON-NLS-1$
+ MultiStatus main = new MultiStatus(DebugUIPlugin.getUniqueIdentifier(), IStatus.ERROR, msg, null);
+
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().getThreadInfo(
+ new long[] { nonUiThread.getId(), Display.getDefault().getThread().getId() }, true, true);
+
+ for (ThreadInfo info : threads) {
+ String childMsg;
+ if (info.getThreadId() == nonUiThread.getId()) {
+ childMsg = nonUiThread.getName() + " thread probably holding a lock and trying to acquire UI lock"; //$NON-NLS-1$
+ } else {
+ childMsg = "UI thread waiting on a job or lock."; //$NON-NLS-1$
+ }
+ Exception childEx = new IllegalStateException("Call stack for thread " + info.getThreadName()); //$NON-NLS-1$
+ childEx.setStackTrace(info.getStackTrace());
+ main.add(DebugUIPlugin.newErrorStatus(childMsg, childEx));
+ }
+
+ DebugUIPlugin.log(main);
+ }
}

Back to the top