[571249] Avoid potential SWTException exception: "Widget is disposed"
This commit fixes the problem seen during validation (comment 7 [1] of
the issue):
* To avoid the "Widget is disposed" problem that occurs during the
UpdateRequest runnable launched in async by the DeferredUpdateManager,
the executions of them are forced before cleaning a the offscreen
diagram EditPart.
* The dispose of the shell is now done in
MappingBasedSiriusFormatManagerFactory.cleanAndDispose(DiagramEditPart).
Indeed, running it in async caused a problem with the above change.
* All the diagramEditPart are disposed. This is a not a problem detected
but a potential problem seen during analysis.
[1] https://bugs.eclipse.org/bugs/show_bug.cgi?id=571249#c7
Bug: 571249
Change-Id: Ib58067fd0cdceb77d42fd9f53ab4118a5525e4dd
Signed-off-by: Laurent Redor <laurent.redor@obeo.fr>
diff --git a/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/tools/api/format/MappingBasedSiriusFormatManagerFactory.java b/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/tools/api/format/MappingBasedSiriusFormatManagerFactory.java
index 719533e..1032c07 100644
--- a/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/tools/api/format/MappingBasedSiriusFormatManagerFactory.java
+++ b/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/tools/api/format/MappingBasedSiriusFormatManagerFactory.java
@@ -42,6 +42,7 @@
import org.eclipse.sirius.business.api.session.CustomDataConstants;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.common.tools.api.util.EqualityHelper;
+import org.eclipse.sirius.common.ui.tools.api.util.EclipseUIUtil;
import org.eclipse.sirius.diagram.DDiagram;
import org.eclipse.sirius.diagram.DDiagramElement;
import org.eclipse.sirius.diagram.DNode;
@@ -71,7 +72,7 @@
import org.eclipse.sirius.viewpoint.description.RepresentationDescription;
import org.eclipse.sirius.viewpoint.description.Viewpoint;
import org.eclipse.sirius.viewpoint.provider.SiriusEditPlugin;
-import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
/**
@@ -222,9 +223,11 @@
synchronizeTargetDiagram(targetSession, (DSemanticDiagram) targetDiagram);
DiagramEditPart sourceDiagramEditPart = null;
DiagramEditPart targetDiagramEditPart = null;
+ Collection<DiagramEditPart> sourceDiagramEditParts = null;
+ Collection<DiagramEditPart> targetDiagramEditParts = null;
try {
- Collection<DiagramEditPart> sourceDiagramEditParts = getDiagramEditPart(sourceSession, sourceDiagram);
- Collection<DiagramEditPart> targetDiagramEditParts = getDiagramEditPart(targetSession, targetDiagram);
+ sourceDiagramEditParts = getDiagramEditPart(sourceSession, sourceDiagram);
+ targetDiagramEditParts = getDiagramEditPart(targetSession, targetDiagram);
if (!sourceDiagramEditParts.isEmpty() && !targetDiagramEditParts.isEmpty()) {
sourceDiagramEditPart = sourceDiagramEditParts.stream().findFirst().get();
@@ -244,11 +247,18 @@
synchronizeTargetDiagram(targetSession, (DSemanticDiagram) targetDiagram);
}
} finally {
- if (sourceDiagramEditPart != null) {
- cleanAndDispose(sourceDiagramEditPart);
+ // Several org.eclipse.draw2d.DeferredUpdateManager (UpdateRequest) could be launched, in async, during the
+ // paste process.
+ // We should execute them to avoid potential "Widget is disposed" during the cleanAndDispose.
+ EclipseUIUtil.synchronizeWithUIThread();
+ // Even if in theory, only one diagram is created, the above code can create several DiagramEditParts for
+ // source and target, so we clean all of them and not directly sourceDiagramEditPart and
+ // targetDiagramEditPart
+ if (sourceDiagramEditParts != null) {
+ sourceDiagramEditParts.stream().forEach(dep -> cleanAndDispose(dep));
}
- if (targetDiagramEditPart != null) {
- cleanAndDispose(targetDiagramEditPart);
+ if (targetDiagramEditParts != null) {
+ targetDiagramEditParts.stream().forEach(dep -> cleanAndDispose(dep));
}
}
}
@@ -536,6 +546,7 @@
private Collection<DiagramEditPart> getDiagramEditPart(Session session, DRepresentation representation) {
final List<DiagramEditPart> result = new ArrayList<DiagramEditPart>();
final Collection<EObject> data = session.getServices().getCustomData(CustomDataConstants.GMF_DIAGRAMS, representation);
+ // Create a new shell for the diagramEditPart, it will be disposed later in cleanAndDispose(DiagramEditPart).
Shell shell = new Shell();
for (final EObject dataElement : data) {
if (dataElement instanceof Diagram) {
@@ -545,12 +556,6 @@
result.add(diagramEditPart);
}
}
- Display.getCurrent().asyncExec(new Runnable() {
- @Override
- public void run() {
- shell.dispose();
- }
- });
return result;
}
@@ -744,16 +749,26 @@
* Clean {@code diagramEditPart} element to avoid memory leaks.
*
* @param diagramEditPart
+ * The diagram edit part to dispose and to use to also dispose associated elements.
*/
private void cleanAndDispose(DiagramEditPart diagramEditPart) {
- // Clean to avoid memory leaks
- diagramEditPart.deactivate();
- // Memory leak : also disposing the
- // DiagramGraphicalViewer associated to this
- // DiagramEditPart
- diagramEditPart.getViewer().flush();
- diagramEditPart.getViewer().getEditDomain().getCommandStack().flush();
- diagramEditPart.getViewer().getControl().dispose();
- ((DiagramEditDomain) diagramEditPart.getViewer().getEditDomain()).removeViewer(diagramEditPart.getViewer());
+ if (diagramEditPart != null) {
+ // Clean to avoid memory leaks
+ diagramEditPart.deactivate();
+ // Memory leak : also disposing the
+ // DiagramGraphicalViewer associated to this
+ // DiagramEditPart
+ diagramEditPart.getViewer().flush();
+ diagramEditPart.getViewer().getEditDomain().getCommandStack().flush();
+ Control control = diagramEditPart.getViewer().getControl();
+ if (control.getParent() != null) {
+ // Dispose the shell created in method getDiagramEditPart(Session, DRepresentation).
+ control.getParent().dispose();
+ } else {
+ // This code should not occurred, but it's just to be sure.
+ control.isDisposed();
+ }
+ ((DiagramEditDomain) diagramEditPart.getViewer().getEditDomain()).removeViewer(diagramEditPart.getViewer());
+ }
}
}