Skip to content

Commit 31c83fd

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 0ce1430 commit 31c83fd

File tree

1 file changed

+69
-50
lines changed

1 file changed

+69
-50
lines changed

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

Lines changed: 69 additions & 50 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

@@ -679,9 +678,8 @@ private void cleanup(boolean fireModelChanged, boolean forkNotification) {
679678

680679
ArrayList<Annotation> deleted= new ArrayList<>();
681680
IAnnotationMap annotations= getAnnotationMap();
682-
Object mapLock = annotations.getLockObject();
683681

684-
synchronized (mapLock) {
682+
synchronized (getLockObject()) {
685683
annotations.forEach((a, p) -> {
686684
if (p == null || p.isDeleted()) {
687685
deleted.add(a);
@@ -693,10 +691,12 @@ private void cleanup(boolean fireModelChanged, boolean forkNotification) {
693691
removeAnnotations(deleted, false, false);
694692
synchronized (getLockObject()) {
695693
if (fModelEvent != null) {
694+
final AnnotationModelEvent event = fModelEvent;
695+
fModelEvent = null;
696696
new Thread() {
697697
@Override
698698
public void run() {
699-
fireModelChanged();
699+
fireModelChanged(event);
700700
}
701701
}.start();
702702
}
@@ -760,7 +760,11 @@ private Iterator<Annotation> getRegionAnnotationIterator(int offset, int length,
760760

761761
try {
762762
Position[] positions= document.getPositions(IDocument.DEFAULT_CATEGORY, offset, length, canStartBefore, canEndAfter);
763-
return new AnnotationsInterator(positions, fPositions);
763+
IdentityHashMap<Position, Annotation> positionsMap;
764+
synchronized (getLockObject()) {
765+
positionsMap= new IdentityHashMap<>(fPositions);
766+
}
767+
return new AnnotationsInterator(positions, positionsMap);
764768
} catch (BadPositionCategoryException e) {
765769
// can happen if e.g. the document doesn't contain such a category, or when removed in a different thread
766770
return Collections.<Annotation>emptyList().iterator();
@@ -784,11 +788,14 @@ private Iterator<Annotation> getAnnotationIterator(boolean cleanup, boolean recu
784788
return iter;
785789
}
786790

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());
791+
List<Iterator<Annotation>> iterators;
792+
synchronized (getLockObject()) {
793+
iterators= new ArrayList<>(fAttachments.size() + 1);
794+
iterators.add(iter);
795+
Iterator<Object> it= fAttachments.keySet().iterator();
796+
while (it.hasNext()) {
797+
iterators.add(fAttachments.get(it.next()).getAnnotationIterator());
798+
}
792799
}
793800

794801
return new MetaIterator<>(iterators.iterator());
@@ -837,22 +844,21 @@ public void removeAllAnnotations() {
837844
*/
838845
protected void removeAllAnnotations(boolean fireModelChanged) {
839846
IAnnotationMap annotations= getAnnotationMap();
840-
if (fDocument != null) {
847+
List<Position> positionsToRemove = new ArrayList<>();
848+
synchronized (getLockObject()) {
841849
Iterator<Annotation> e= getAnnotationMap().keySetIterator();
842850
while (e.hasNext()) {
843851
Annotation a= e.next();
844852
Position p= annotations.get(a);
845-
removePosition(fDocument, p);
846-
// p.delete();
847-
synchronized (getLockObject()) {
848-
getAnnotationModelEvent().annotationRemoved(a, p);
849-
}
853+
positionsToRemove.add(p);
854+
getAnnotationModelEvent().annotationRemoved(a, p);
850855
}
856+
annotations.clear();
857+
fPositions.clear();
858+
}
859+
for (Position position : positionsToRemove) {
860+
removePosition(fDocument, position);
851861
}
852-
853-
annotations.clear();
854-
fPositions.clear();
855-
856862
if (fireModelChanged) {
857863
fireModelChanged();
858864
}
@@ -872,24 +878,22 @@ public void removeAnnotation(Annotation annotation) {
872878
*/
873879
protected void removeAnnotation(Annotation annotation, boolean fireModelChanged) {
874880
IAnnotationMap annotations= getAnnotationMap();
875-
if (annotations.containsKey(annotation)) {
876-
877-
Position p= null;
881+
Position p;
882+
synchronized (getLockObject()) {
878883
p= annotations.get(annotation);
879-
if (fDocument != null) {
880-
removePosition(fDocument, p);
881-
// p.delete();
884+
if (p == null) {
885+
return;
882886
}
883-
884887
annotations.remove(annotation);
885888
fPositions.remove(p);
886-
synchronized (getLockObject()) {
887-
getAnnotationModelEvent().annotationRemoved(annotation, p);
888-
}
889+
getAnnotationModelEvent().annotationRemoved(annotation, p);
890+
}
891+
if (fDocument != null) {
892+
removePosition(fDocument, p);
893+
}
889894

890-
if (fireModelChanged) {
891-
fireModelChanged();
892-
}
895+
if (fireModelChanged) {
896+
fireModelChanged();
893897
}
894898
}
895899

@@ -982,13 +986,20 @@ public void removeAnnotationModelListener(IAnnotationModelListener listener) {
982986
@Override
983987
public void addAnnotationModel(Object key, IAnnotationModel attachment) {
984988
Assert.isNotNull(attachment);
985-
if (!fAttachments.containsValue(attachment)) {
989+
Assert.isNotNull(key);
990+
synchronized (getLockObject()) {
991+
if (fAttachments.containsValue(attachment)) {
992+
return;
993+
}
986994
fAttachments.put(key, attachment);
995+
}
996+
IDocument document= fDocument;
997+
if (document != null) {
987998
for (int i= 0; i < fOpenConnections; i++) {
988-
attachment.connect(fDocument);
999+
attachment.connect(document);
9891000
}
990-
attachment.addAnnotationModelListener(fModelListener);
9911001
}
1002+
attachment.addAnnotationModelListener(fModelListener);
9921003
}
9931004

9941005
/*
@@ -1006,13 +1017,21 @@ public IAnnotationModel getAnnotationModel(Object key) {
10061017
*/
10071018
@Override
10081019
public IAnnotationModel removeAnnotationModel(Object key) {
1009-
IAnnotationModel ret= fAttachments.remove(key);
1010-
if (ret != null) {
1020+
Assert.isNotNull(key);
1021+
IAnnotationModel ret;
1022+
synchronized (getLockObject()) {
1023+
ret= fAttachments.remove(key);
1024+
if (ret == null) {
1025+
return null;
1026+
}
1027+
}
1028+
IDocument document= fDocument;
1029+
if (document != null) {
10111030
for (int i= 0; i < fOpenConnections; i++) {
1012-
ret.disconnect(fDocument);
1031+
ret.disconnect(document);
10131032
}
1014-
ret.removeAnnotationModelListener(fModelListener);
10151033
}
1034+
ret.removeAnnotationModelListener(fModelListener);
10161035
return ret;
10171036
}
10181037

0 commit comments

Comments
 (0)