Commit ad53429d authored by Stephan Eberle's avatar Stephan Eberle
Browse files

Eliminated potential source of deadlocks in ResourceProblemHandler by

making delegation of handling of changed and saved files to
ResourceProblemMarkerService asynchronous
parent a5ca646a
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
/**
 * <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 class Messages extends NLS {
	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;
+3 −1
Original line number Diff line number Diff line
# <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_updatingReferencedModelDescriptors=Updating Referenced Model Descriptors
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
+80 −15
Original line number Diff line number Diff line
/**
 * <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.IResourceDelta;
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.ResourceSetListenerImpl;
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 @@ public class ResourceProblemHandler extends ResourceSetListenerImpl implements I
	 * 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,12 +196,54 @@ public class ResourceProblemHandler extends ResourceSetListenerImpl implements I
	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);

		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);
@@ -201,10 +252,24 @@ public class ResourceProblemHandler extends ResourceSetListenerImpl implements I
							}
						}

		handleSavedResources(resources);
						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);
					}
				}

	protected void handleSavedResources(Collection<Resource> resources) {
		ResourceProblemMarkerService.INSTANCE.addProblemMarkers(resources, null);
				@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();
		}
	}
}
 No newline at end of file
+9 −3
Original line number Diff line number Diff line
/**
 * <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 @@ public class ResourceProblemMarkerService {
	}

	/**
	 * 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 @@ public class ResourceProblemMarkerService {
	 *            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();
			}
		}
	}