Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2016-07-19 14:35:41 +0000
committerChristian W. Damus2016-07-19 15:16:16 +0000
commit1715bb61f46aa6251ff5bda74115e8e0530f5856 (patch)
treea8a06e1a00a199f90a97f0ae63e6e8bee58a1404 /plugins/infra/core
parent68f29daa4d3427f79f12524790fd71f8431486bc (diff)
downloadorg.eclipse.papyrus-1715bb61f46aa6251ff5bda74115e8e0530f5856.tar.gz
org.eclipse.papyrus-1715bb61f46aa6251ff5bda74115e8e0530f5856.tar.xz
org.eclipse.papyrus-1715bb61f46aa6251ff5bda74115e8e0530f5856.zip
Bug 498140: Memory leak in validation privileged runnables
https://bugs.eclipse.org/bugs/show_bug.cgi?id=498140 Forget the privileged runnable and its transaction context after executing the wrapping runnable-with-progress. Clarify the one-shot nature of the API. Similar changes for the headless progress runnable/callable APIs in the core. Change-Id: I5ab1361032ea5c48e493b86823e46413f1d199d3 (cherry picked from commit 03dd4b0b4844e55d22563ba72eb8eaabb7a9ca49)
Diffstat (limited to 'plugins/infra/core')
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java85
1 files changed, 70 insertions, 15 deletions
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
index 7f00384381a..90315f531b5 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014, 2015 CEA LIST, Christian W. Damus, and others.
+ * Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,7 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bugs 429826, 408491, 433320
- * Christian W. Damus - bugs 451557, 457560, 461629, 463564, 466997, 465416, 485220
+ * Christian W. Damus - bugs 451557, 457560, 461629, 463564, 466997, 465416, 485220, 498140
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.utils;
@@ -19,6 +19,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.edit.domain.EditingDomain;
@@ -473,9 +474,16 @@ public class TransactionHelper {
}
/**
+ * <p>
* Create a privileged progress runnable, which is like a regular {@linkplain TransactionalEditingDomain#createPrivilegedRunnable(Runnable)
* privileged runnable} except that it is given a progress monitor for progress reporting.
- *
+ * </p>
+ * <b>Note</b> that a privileged progress-runnable can be used only once. Because it has the
+ * context of a borrowed transaction, repeated execution is invalid because the required
+ * transaction context will have expired. Attempting to run a privileged runnable a second
+ * time will throw an {@link IllegalStateException}.
+ * </p>
+ *
* @param domain
* an editing domain
* @param runnable
@@ -488,16 +496,21 @@ public class TransactionHelper {
Runnable privileged = domain.createPrivilegedRunnable(() -> runnable.run(monitorHolder[0]));
- return monitor -> {
- monitorHolder[0] = monitor;
- privileged.run();
- };
+ return new PrivilegedProgressRunnable(privileged, m -> monitorHolder[0] = m);
}
/**
+ * <p>
* Create a privileged progress callable, which is like a {@linkplain TransactionalEditingDomain#createPrivilegedRunnable(Runnable)
* privileged runnable} except that it is given a progress monitor for progress reporting and it computes a result.
- *
+ * </p>
+ * <p>
+ * <b>Note</b> that a privileged progress-callable can be used only once. Because it has the
+ * context of a borrowed transaction, repeated execution is invalid because the required
+ * transaction context will have expired. Attempting to run a privileged callable a second
+ * time will throw an {@link IllegalStateException}.
+ * </p>
+ *
* @param callable
* an editing domain
* @param callable
@@ -506,22 +519,27 @@ public class TransactionHelper {
* @since 2.0
*/
public static <V> IProgressCallable<V> createPrivilegedCallable(TransactionalEditingDomain domain, final IProgressCallable<V> callable) {
- IProgressMonitor monitorHolder[] = { null };
- AtomicReference<V> resultHolder = new AtomicReference<V>();
+ AtomicReference<V> resultHolder = new AtomicReference<>();
Exception failHolder[] = { null };
- Runnable privileged = domain.createPrivilegedRunnable(() -> {
+ IProgressRunnable[] privileged = { createPrivilegedRunnable(domain, monitor -> {
try {
- resultHolder.set(callable.call(monitorHolder[0]));
+ resultHolder.set(callable.call(monitor));
} catch (Exception e) {
failHolder[0] = e;
}
- });
+ }) };
return monitor -> {
- monitorHolder[0] = monitor;
+ if (privileged[0] == null) {
+ throw new IllegalStateException("Privileged callable was already run"); //$NON-NLS-1$
+ }
- privileged.run();
+ try {
+ privileged[0].run(monitor);
+ } finally {
+ privileged[0] = null;
+ }
if (failHolder[0] != null) {
throw failHolder[0];
@@ -530,4 +548,41 @@ public class TransactionHelper {
return resultHolder.get();
};
}
+
+ //
+ // Nested types
+ //
+
+ private static final class PrivilegedProgressRunnable implements IProgressRunnable {
+ private final Consumer<? super IProgressMonitor> monitorSlot;
+ private Runnable privileged;
+
+ PrivilegedProgressRunnable(Runnable privileged, Consumer<? super IProgressMonitor> monitorSlot) {
+ super();
+
+ this.monitorSlot = monitorSlot;
+ this.privileged = privileged;
+ }
+
+ @Override
+ public void run(IProgressMonitor monitor) {
+ if (privileged == null) {
+ throw new IllegalArgumentException("Privileged runnable was already run"); //$NON-NLS-1$
+ }
+
+ monitorSlot.accept(monitor);
+
+ try {
+ privileged.run();
+ } finally {
+ // Clear our reference to the runnable, which holds a reference
+ // to the transaction which, in turn, holds monitors. If we
+ // are run in the ModalContextThread and initialize an Xtext UI
+ // bundle, then that thread will be retained forever in its
+ // Guice injector as the creating thread, and that thread then
+ // will retain me (its runnable). See bug 498140
+ privileged = null;
+ }
+ }
+ }
}

Back to the top