Eliminated potential source of deadlocks in ResourceProblemHandler by
making delegation of handling of changed and saved files to
ResourceProblemMarkerService asynchronous
diff --git a/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/messages/Messages.java b/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/messages/Messages.java
index 5984d35..201e1d8 100644
--- a/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/messages/Messages.java
+++ b/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/messages/Messages.java
@@ -1,7 +1,7 @@
 /**

  * <copyright>

  *

- * Copyright (c) 2008-2013 See4sys, itemis and others.

+ * Copyright (c) 2008-2019 See4sys, itemis 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

@@ -58,6 +58,8 @@
 	public static String job_removingModelDescriptors;

 	public static String job_clearingOldMetaModelDescriptors;

 	public static String job_initializingModelDescriptorRegistry;

+	public static String job_addingProblemMarkers;

+	public static String job_removingProblemMarkers;

 	public static String task_analyzingProjects;

 	public static String subtask_analyzingFile;

 	public static String task_validatingResourceScopes;

diff --git a/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/messages/Messages.properties b/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/messages/Messages.properties
index ce9adeb..b0c8d33 100644
--- a/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/messages/Messages.properties
+++ b/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/messages/Messages.properties
@@ -1,6 +1,6 @@
 # <copyright>

 #

-# Copyright (c) 2008-2013 See4sys, itemis and others.

+# Copyright (c) 2008-2019 See4sys, itemis 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

@@ -52,6 +52,8 @@
 job_removingModelDescriptors=Removing Model Descriptors

 job_clearingOldMetaModelDescriptors=Clearing Old Meta-model Descriptors

 job_initializingModelDescriptorRegistry= Initializing Model Descriptor Registry

+job_addingProblemMarkers=Adding Problem Markers

+job_removingProblemMarkers=Removing Problem Markers

 task_analyzingProjects=Analyzing projects

 subtask_analyzingFile=Analyzing file \"{0}\"

 task_validatingResourceScopes=Validating Resource Scopes

diff --git a/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/resource/ResourceProblemHandler.java b/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/resource/ResourceProblemHandler.java
index 7320809..b69f3d0 100644
--- a/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/resource/ResourceProblemHandler.java
+++ b/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/resource/ResourceProblemHandler.java
@@ -1,7 +1,7 @@
 /**

  * <copyright>

  *

- * Copyright (c) 2008-2013 See4sys, BMW Car IT, itemis and others.

+ * Copyright (c) 2008-2019 See4sys, BMW Car IT, itemis 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

@@ -30,6 +30,11 @@
 import org.eclipse.core.resources.IResourceDeltaVisitor;

 import org.eclipse.core.resources.ResourcesPlugin;

 import org.eclipse.core.runtime.Assert;

+import org.eclipse.core.runtime.IProgressMonitor;

+import org.eclipse.core.runtime.IStatus;

+import org.eclipse.core.runtime.OperationCanceledException;

+import org.eclipse.core.runtime.Status;

+import org.eclipse.core.runtime.jobs.Job;

 import org.eclipse.emf.common.notify.Notification;

 import org.eclipse.emf.common.util.URI;

 import org.eclipse.emf.ecore.EcorePackage;

@@ -41,12 +46,16 @@
 import org.eclipse.emf.transaction.TransactionalEditingDomain;

 import org.eclipse.sphinx.emf.Activator;

 import org.eclipse.sphinx.emf.domain.factory.AbstractResourceSetListenerInstaller;

+import org.eclipse.sphinx.emf.internal.messages.Messages;

 import org.eclipse.sphinx.emf.saving.SaveIndicatorUtil;

 import org.eclipse.sphinx.emf.util.EcorePlatformUtil;

 import org.eclipse.sphinx.emf.util.WorkspaceEditingDomainUtil;

+import org.eclipse.sphinx.platform.IExtendedPlatformConstants;

 import org.eclipse.sphinx.platform.resources.DefaultResourceChangeHandler;

 import org.eclipse.sphinx.platform.resources.ResourceDeltaVisitor;

+import org.eclipse.sphinx.platform.util.ExtendedPlatform;

 import org.eclipse.sphinx.platform.util.PlatformLogUtil;

+import org.eclipse.sphinx.platform.util.StatusUtil;

 

 /**

  * Listens for {@link Resource resource}s that have been loaded or saved and requests the problem markers of underlying

@@ -68,8 +77,8 @@
 	 * Default constructor.

 	 */

 	public ResourceProblemHandler() {

-		super(NotificationFilter.createFeatureFilter(EcorePackage.eINSTANCE.getEResource(), Resource.RESOURCE__IS_LOADED).or(

-				NotificationFilter.createFeatureFilter(EcorePackage.eINSTANCE.getEResourceSet(), ResourceSet.RESOURCE_SET__RESOURCES)));

+		super(NotificationFilter.createFeatureFilter(EcorePackage.eINSTANCE.getEResource(), Resource.RESOURCE__IS_LOADED)

+				.or(NotificationFilter.createFeatureFilter(EcorePackage.eINSTANCE.getEResourceSet(), ResourceSet.RESOURCE_SET__RESOURCES)));

 	}

 

 	@Override

@@ -187,24 +196,80 @@
 	protected void handleChangedFiles(Collection<IFile> files) {

 		Assert.isNotNull(files);

 

-		ResourceProblemMarkerService.INSTANCE.removeProblemMarkers(files, null);

+		if (files.size() > 0) {

+			/*

+			 * !! Important Note !! Perform as asynchronous operation with exclusive access to the affected files in

+			 * order to avoid deadlocks. The workspace is locked while IResourceChangeListeners are processed (exclusive

+			 * workspace access) and updating a resource's problem markers may involve creating transactions (exclusive

+			 * model access). In cases where another thread is around while we are called here which already has

+			 * exclusive model access but waits for exclusive workspace access we would end up in a deadlock otherwise.

+			 */

+			Job job = new Job(Messages.job_removingProblemMarkers) {

+				@Override

+				protected IStatus run(IProgressMonitor monitor) {

+					try {

+						ResourceProblemMarkerService.INSTANCE.removeProblemMarkers(files, monitor);

+						return Status.OK_STATUS;

+					} catch (OperationCanceledException ex) {

+						return Status.CANCEL_STATUS;

+					} catch (Exception ex) {

+						return StatusUtil.createErrorStatus(Activator.getPlugin(), ex);

+					}

+				}

+

+				@Override

+				public boolean belongsTo(Object family) {

+					return IExtendedPlatformConstants.FAMILY_LONG_RUNNING.equals(family);

+				}

+			};

+			job.setPriority(Job.SHORT);

+			job.setRule(ExtendedPlatform.createModifySchedulingRule(files));

+			job.setSystem(true);

+			job.schedule();

+		}

 	}

 

 	protected void handleSavedFiles(Collection<IFile> files) {

 		Assert.isNotNull(files);

 

-		Set<Resource> resources = new HashSet<Resource>();

-		for (IFile file : files) {

-			Resource resource = EcorePlatformUtil.getResource(file);

-			if (resource != null) {

-				resources.add(resource);

-			}

+		if (files.size() > 0) {

+			/*

+			 * !! Important Note !! Perform as asynchronous operation with exclusive access to the affected files in

+			 * order to avoid deadlocks. The workspace is locked while IResourceChangeListeners are processed (exclusive

+			 * workspace access) and updating a resource's problem markers may involve creating transactions (exclusive

+			 * model access). In cases where another thread is around while we are called here which already has

+			 * exclusive model access but waits for exclusive workspace access we would end up in a deadlock otherwise.

+			 */

+			Job job = new Job(Messages.job_addingProblemMarkers) {

+				@Override

+				protected IStatus run(IProgressMonitor monitor) {

+					try {

+						Set<Resource> resources = new HashSet<Resource>();

+						for (IFile file : files) {

+							Resource resource = EcorePlatformUtil.getResource(file);

+							if (resource != null) {

+								resources.add(resource);

+							}

+						}

+

+						ResourceProblemMarkerService.INSTANCE.addProblemMarkers(resources, monitor);

+						return Status.OK_STATUS;

+					} catch (OperationCanceledException ex) {

+						return Status.CANCEL_STATUS;

+					} catch (Exception ex) {

+						return StatusUtil.createErrorStatus(Activator.getPlugin(), ex);

+					}

+				}

+

+				@Override

+				public boolean belongsTo(Object family) {

+					return IExtendedPlatformConstants.FAMILY_LONG_RUNNING.equals(family);

+				}

+			};

+			job.setPriority(Job.SHORT);

+			job.setRule(ExtendedPlatform.createModifySchedulingRule(files));

+			job.setSystem(true);

+			job.schedule();

 		}

-

-		handleSavedResources(resources);

-	}

-

-	protected void handleSavedResources(Collection<Resource> resources) {

-		ResourceProblemMarkerService.INSTANCE.addProblemMarkers(resources, null);

 	}

 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/resource/ResourceProblemMarkerService.java b/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/resource/ResourceProblemMarkerService.java
index 619cec7..2aca172 100644
--- a/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/resource/ResourceProblemMarkerService.java
+++ b/plugins/org.eclipse.sphinx.emf/src/org/eclipse/sphinx/emf/internal/resource/ResourceProblemMarkerService.java
@@ -1,7 +1,7 @@
 /**
  * <copyright>
  *
- * Copyright (c) 2008-2014 See4sys, BMW Car IT, itemis and others.
+ * Copyright (c) 2008-2019 See4sys, BMW Car IT, itemis 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
@@ -86,8 +86,8 @@
 	}
 
 	/**
-	 * Analyzes {@link Resource#getErrors() errors} and {@link Resource#getWarnings() warnings} of given
-	 * {@link Resource resource} and creates corresponding problem markers on underlying {@link IFile file}.
+	 * Analyzes {@link Resource#getErrors() errors} and {@link Resource#getWarnings() warnings} of given {@link Resource
+	 * resource} and creates corresponding problem markers on underlying {@link IFile file}.
 	 * <p>
 	 * The type of the problem marker being created depends on the type of {@link Resource#getErrors() error} or
 	 * {@link Resource#getWarnings() warning} of given {@link Resource resource} and can be one of the following:
@@ -257,10 +257,16 @@
 	 *            desired.
 	 */
 	public void removeProblemMarkers(final Collection<IFile> files, final IProgressMonitor monitor) {
+		SubMonitor progress = SubMonitor.convert(monitor, files.size());
 		for (IFile file : files) {
 			if (file.isAccessible()) {
 				getResourceProblemMarkerFactory(file).deleteMarkers(file);
 			}
+
+			progress.worked(1);
+			if (progress.isCanceled()) {
+				throw new OperationCanceledException();
+			}
 		}
 	}