diff --git a/bundles/org.eclipse.text/META-INF/MANIFEST.MF b/bundles/org.eclipse.text/META-INF/MANIFEST.MF index a247d39b795..516af6ca8a0 100644 --- a/bundles/org.eclipse.text/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.text/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.text -Bundle-Version: 3.14.500.qualifier +Bundle-Version: 3.14.600.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: diff --git a/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.java b/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.java index bb6fe764265..f2fd6f7cf7f 100644 --- a/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.java +++ b/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.IdentityHashMap; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -27,6 +28,10 @@ import java.util.concurrent.ConcurrentHashMap; import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.text.AbstractDocument; import org.eclipse.jface.text.BadLocationException; @@ -48,6 +53,45 @@ */ public class AnnotationModel implements IAnnotationModel, IAnnotationModelExtension, IAnnotationModelExtension2, ISynchronizable { + /** + * This job is replacing the original thread, to make it multi thread safe (fixes the race + * condition of fModelEvent) + */ + private final class FireModelChangeJob extends Job { + + private LinkedList events= new LinkedList<>(); + + private FireModelChangeJob() { + super(Messages.AnnotationModel_FireModelChangedEventJobTitle); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + synchronized (events) { + AnnotationModelEvent nextEvent= events.poll(); + while (nextEvent != null) { + fireModelChanged(nextEvent); + nextEvent= events.poll(); + } + } + return Status.OK_STATUS; + } + + /** + * Adds a new {@link AnnotationModelEvent} to the events list to fire it in the job + * execution. If job is running + * + * @param event the event to fire. + */ + public void fireDelayedModelChangedEvent(AnnotationModelEvent event) { + if (event != null) { + synchronized (events) { + events.add(event); + } + schedule(); + } + } + } /** * Iterator that returns the annotations for a given region. @@ -307,6 +351,12 @@ public void modelChanged(AnnotationModelEvent event) { */ private Object fModificationStamp= new Object(); + /** + * The job for firing the model changes + * @since 3.14 + */ + private FireModelChangeJob modelChangedEventJob; + /** * Creates a new annotation model. The annotation is empty, i.e. does not * manage any annotations and is not connected to any document. @@ -315,6 +365,7 @@ public AnnotationModel() { fAnnotations= new AnnotationMap(10); fPositions= new IdentityHashMap<>(10); fAnnotationModelListeners= new ArrayList<>(2); + modelChangedEventJob = new FireModelChangeJob(); fDocumentListener= new IDocumentListener() { @@ -667,10 +718,10 @@ protected void cleanup(boolean fireModelChanged) { /** * Removes all annotations from the model whose associated positions have been * deleted. If requested inform all model listeners about the change. If requested - * a new thread is created for the notification of the model listeners. + * a new job is created for the notification of the model listeners. * * @param fireModelChanged indicates whether to notify all model listeners - * @param forkNotification true iff notification should be done in a new thread + * @param forkNotification true if notification should be done in a new job * @since 3.0 */ private void cleanup(boolean fireModelChanged, boolean forkNotification) { @@ -692,14 +743,7 @@ private void cleanup(boolean fireModelChanged, boolean forkNotification) { if (fireModelChanged && forkNotification) { removeAnnotations(deleted, false, false); synchronized (getLockObject()) { - if (fModelEvent != null) { - new Thread() { - @Override - public void run() { - fireModelChanged(); - } - }.start(); - } + modelChangedEventJob.fireDelayedModelChangedEvent(fModelEvent); } } else { removeAnnotations(deleted, fireModelChanged, false); diff --git a/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/Messages.java b/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/Messages.java new file mode 100644 index 00000000000..2aaad215076 --- /dev/null +++ b/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/Messages.java @@ -0,0 +1,26 @@ +package org.eclipse.jface.text.source; + +import org.eclipse.osgi.util.NLS; + +/** + * @since 3.14 + */ +public class Messages extends NLS { + + /** + * @since 3.14 + */ + private static final String BUNDLE_NAME= Messages.class.getPackageName() + ".messages"; //$NON-NLS-1$ + + /** + * @since 3.14 + */ + public static String AnnotationModel_FireModelChangedEventJobTitle; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/messages.properties b/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/messages.properties new file mode 100644 index 00000000000..c5d44095cd2 --- /dev/null +++ b/bundles/org.eclipse.text/src/org/eclipse/jface/text/source/messages.properties @@ -0,0 +1 @@ +AnnotationModel_FireModelChangedEventJobTitle=Fire model changed event