Skip to content

Commit 4082003

Browse files
Fix for multi-threading issues in AnnotationMap
- added synchronize block to methods in AnnotationModel, wherever reads / writes could happen in multiple threads. - changed execution order, to be able to synchronize only necessary code blocks. Co-authored-by: Andrey Loskutov <[email protected]>
1 parent 67ea81a commit 4082003

File tree

1 file changed

+68
-48
lines changed

1 file changed

+68
-48
lines changed

bundles/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.java

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -451,18 +451,17 @@ protected void replaceAnnotations(Annotation[] annotationsToRemove, Map<? extend
451451
*/
452452
protected void addAnnotation(Annotation annotation, Position position, boolean fireModelChanged) throws BadLocationException {
453453
IAnnotationMap annotations= getAnnotationMap();
454-
if (!annotations.containsKey(annotation)) {
455-
456-
addPosition(fDocument, position);
457-
annotations.put(annotation, position);
458-
fPositions.put(position, annotation);
459-
synchronized (getLockObject()) {
460-
getAnnotationModelEvent().annotationAdded(annotation);
461-
}
462-
463-
if (fireModelChanged) {
464-
fireModelChanged();
454+
synchronized (getLockObject()) {
455+
if (annotations.containsKey(annotation)) {
456+
return;
465457
}
458+
fPositions.put(position, annotation);
459+
annotations.put(annotation, position);
460+
getAnnotationModelEvent().annotationAdded(annotation);
461+
}
462+
addPosition(fDocument, position);
463+
if (fireModelChanged) {
464+
fireModelChanged();
466465
}
467466
}
468467

@@ -680,7 +679,7 @@ private void cleanup(boolean fireModelChanged, boolean forkNotification) {
680679
ArrayList<Annotation> deleted= new ArrayList<>();
681680
IAnnotationMap annotations= getAnnotationMap();
682681
Object mapLock = annotations.getLockObject();
683-
682+
Assert.isNotNull(mapLock);
684683
synchronized (mapLock) {
685684
annotations.forEach((a, p) -> {
686685
if (p == null || p.isDeleted()) {
@@ -693,10 +692,12 @@ private void cleanup(boolean fireModelChanged, boolean forkNotification) {
693692
removeAnnotations(deleted, false, false);
694693
synchronized (getLockObject()) {
695694
if (fModelEvent != null) {
695+
final AnnotationModelEvent event = fModelEvent;
696+
fModelEvent = null;
696697
new Thread() {
697698
@Override
698699
public void run() {
699-
fireModelChanged();
700+
fireModelChanged(event);
700701
}
701702
}.start();
702703
}
@@ -760,7 +761,11 @@ private Iterator<Annotation> getRegionAnnotationIterator(int offset, int length,
760761

761762
try {
762763
Position[] positions= document.getPositions(IDocument.DEFAULT_CATEGORY, offset, length, canStartBefore, canEndAfter);
763-
return new AnnotationsInterator(positions, fPositions);
764+
IdentityHashMap<Position, Annotation> positionsMap;
765+
synchronized (getLockObject()) {
766+
positionsMap= new IdentityHashMap<>(fPositions);
767+
}
768+
return new AnnotationsInterator(positions, positionsMap);
764769
} catch (BadPositionCategoryException e) {
765770
// can happen if e.g. the document doesn't contain such a category, or when removed in a different thread
766771
return Collections.<Annotation>emptyList().iterator();
@@ -784,11 +789,14 @@ private Iterator<Annotation> getAnnotationIterator(boolean cleanup, boolean recu
784789
return iter;
785790
}
786791

787-
List<Iterator<Annotation>> iterators= new ArrayList<>(fAttachments.size() + 1);
788-
iterators.add(iter);
789-
Iterator<Object> it= fAttachments.keySet().iterator();
790-
while (it.hasNext()) {
791-
iterators.add(fAttachments.get(it.next()).getAnnotationIterator());
792+
List<Iterator<Annotation>> iterators;
793+
synchronized (getLockObject()) {
794+
iterators= new ArrayList<>(fAttachments.size() + 1);
795+
iterators.add(iter);
796+
Iterator<Object> it= fAttachments.keySet().iterator();
797+
while (it.hasNext()) {
798+
iterators.add(fAttachments.get(it.next()).getAnnotationIterator());
799+
}
792800
}
793801

794802
return new MetaIterator<>(iterators.iterator());
@@ -837,22 +845,21 @@ public void removeAllAnnotations() {
837845
*/
838846
protected void removeAllAnnotations(boolean fireModelChanged) {
839847
IAnnotationMap annotations= getAnnotationMap();
840-
if (fDocument != null) {
848+
List<Position> positionsToRemove = new ArrayList<>();
849+
synchronized (getLockObject()) {
841850
Iterator<Annotation> e= getAnnotationMap().keySetIterator();
842851
while (e.hasNext()) {
843852
Annotation a= e.next();
844853
Position p= annotations.get(a);
845-
removePosition(fDocument, p);
846-
// p.delete();
847-
synchronized (getLockObject()) {
848-
getAnnotationModelEvent().annotationRemoved(a, p);
849-
}
854+
positionsToRemove.add(p);
855+
getAnnotationModelEvent().annotationRemoved(a, p);
850856
}
857+
annotations.clear();
858+
fPositions.clear();
859+
}
860+
for (Position position : positionsToRemove) {
861+
removePosition(fDocument, position);
851862
}
852-
853-
annotations.clear();
854-
fPositions.clear();
855-
856863
if (fireModelChanged) {
857864
fireModelChanged();
858865
}
@@ -872,24 +879,22 @@ public void removeAnnotation(Annotation annotation) {
872879
*/
873880
protected void removeAnnotation(Annotation annotation, boolean fireModelChanged) {
874881
IAnnotationMap annotations= getAnnotationMap();
875-
if (annotations.containsKey(annotation)) {
876-
877-
Position p= null;
882+
Position p;
883+
synchronized (getLockObject()) {
878884
p= annotations.get(annotation);
879-
if (fDocument != null) {
880-
removePosition(fDocument, p);
881-
// p.delete();
885+
if (p == null) {
886+
return;
882887
}
883-
884888
annotations.remove(annotation);
885889
fPositions.remove(p);
886-
synchronized (getLockObject()) {
887-
getAnnotationModelEvent().annotationRemoved(annotation, p);
888-
}
890+
getAnnotationModelEvent().annotationRemoved(annotation, p);
891+
}
892+
if (fDocument != null) {
893+
removePosition(fDocument, p);
894+
}
889895

890-
if (fireModelChanged) {
891-
fireModelChanged();
892-
}
896+
if (fireModelChanged) {
897+
fireModelChanged();
893898
}
894899
}
895900

@@ -982,13 +987,20 @@ public void removeAnnotationModelListener(IAnnotationModelListener listener) {
982987
@Override
983988
public void addAnnotationModel(Object key, IAnnotationModel attachment) {
984989
Assert.isNotNull(attachment);
985-
if (!fAttachments.containsValue(attachment)) {
990+
Assert.isNotNull(key);
991+
synchronized (getLockObject()) {
992+
if (fAttachments.containsValue(attachment)) {
993+
return;
994+
}
986995
fAttachments.put(key, attachment);
996+
}
997+
IDocument document= fDocument;
998+
if (document != null) {
987999
for (int i= 0; i < fOpenConnections; i++) {
988-
attachment.connect(fDocument);
1000+
attachment.connect(document);
9891001
}
990-
attachment.addAnnotationModelListener(fModelListener);
9911002
}
1003+
attachment.addAnnotationModelListener(fModelListener);
9921004
}
9931005

9941006
/*
@@ -1006,13 +1018,21 @@ public IAnnotationModel getAnnotationModel(Object key) {
10061018
*/
10071019
@Override
10081020
public IAnnotationModel removeAnnotationModel(Object key) {
1009-
IAnnotationModel ret= fAttachments.remove(key);
1010-
if (ret != null) {
1021+
Assert.isNotNull(key);
1022+
IAnnotationModel ret;
1023+
synchronized (getLockObject()) {
1024+
ret= fAttachments.remove(key);
1025+
if (ret == null) {
1026+
return null;
1027+
}
1028+
}
1029+
IDocument document= fDocument;
1030+
if (document != null) {
10111031
for (int i= 0; i < fOpenConnections; i++) {
10121032
ret.disconnect(fDocument);
10131033
}
1014-
ret.removeAnnotationModelListener(fModelListener);
10151034
}
1035+
ret.removeAnnotationModelListener(fModelListener);
10161036
return ret;
10171037
}
10181038

0 commit comments

Comments
 (0)