Skip to content

Commit 67ea81a

Browse files
Synchronize IAnnotationMap/AnnotationModel contract with implementation
- Clarified contract of IAnnotationMap.getLockObject() and setLockObject(). - Clarified contract of AnnotationModel.getLockObject() and setLockObject(). - Added warning in AnnotationMap.setLockObject() if the lock is changed - Removed dead code in AnnotationModel.cleanup because mapLock can't be null. Co-authored-by: Andrey Loskutov <[email protected]>
1 parent c521dfc commit 67ea81a

File tree

3 files changed

+77
-15
lines changed

3 files changed

+77
-15
lines changed

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.Map;
2222
import java.util.Set;
2323

24+
import org.eclipse.core.runtime.ILog;
25+
2426
import org.eclipse.jface.text.Position;
2527

2628

@@ -54,11 +56,38 @@ public AnnotationMap(int capacity) {
5456
fInternalMap= new HashMap<>(capacity);
5557
}
5658

59+
/**
60+
* Sets the lock object for this object. Subsequent calls to specified methods of this object
61+
* are synchronized on this lock object. Which methods are synchronized is specified by the
62+
* implementer.
63+
* <p>
64+
* If the lock object is not explicitly set by this method, the annotation map uses its internal
65+
* lock object for synchronization.
66+
* </p>
67+
*
68+
* <p>
69+
* <em>You should not override an existing lock object unless you own that lock object yourself.
70+
* Use the existing lock object instead.</em>
71+
* </p>
72+
*
73+
* @param lockObject the lock object. If <code>null</code> is given, internal lock object is
74+
* used for locking.
75+
*/
5776
@Override
5877
public synchronized void setLockObject(Object lockObject) {
78+
if(fLockObject != null && fLockObject != lockObject) {
79+
ILog log= ILog.of(AnnotationMap.class);
80+
String message = "Attempt to override existing lock object of AnnotationMap."; //$NON-NLS-1$
81+
log.warn(message, new IllegalStateException(message));
82+
}
5983
fLockObject= lockObject;
6084
}
6185

86+
/**
87+
* Lock object used to synchronize concurrent access to the internal map data.
88+
*
89+
* @return <b>never</b> returns null
90+
*/
6291
@Override
6392
public synchronized Object getLockObject() {
6493
if (fLockObject == null) {

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

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Map;
2424
import java.util.Map.Entry;
2525
import java.util.NoSuchElementException;
26+
import java.util.Objects;
2627
import java.util.concurrent.ConcurrentHashMap;
2728

2829
import org.eclipse.core.runtime.Assert;
@@ -338,11 +339,30 @@ protected IAnnotationMap getAnnotationMap() {
338339
return (IAnnotationMap) fAnnotations;
339340
}
340341

342+
/**
343+
* Subclasses should <b>never</b> return null. Clients should use the lock
344+
* object in order to synchronize concurrent access to the internal map data.
345+
*
346+
* @return <b>never</b> returns null
347+
*/
341348
@Override
342349
public Object getLockObject() {
343-
return getAnnotationMap().getLockObject();
350+
return Objects.requireNonNull(getAnnotationMap().getLockObject());
344351
}
345352

353+
/**
354+
* Sets the lock object for this object. Subsequent calls to specified methods of this object
355+
* are synchronized on this lock object. Which methods are synchronized is specified by the
356+
* implementer.
357+
*
358+
* <p>
359+
* <em>You should not override an existing lock object unless you own that lock object yourself.
360+
* Use the existing lock object instead.</em>
361+
* </p>
362+
*
363+
* @param lockObject the lock object. If <code>null</code> is given, default implementation uses
364+
* the internal lock object for locking.
365+
*/
346366
@Override
347367
public void setLockObject(Object lockObject) {
348368
getAnnotationMap().setLockObject(lockObject);
@@ -661,23 +681,12 @@ private void cleanup(boolean fireModelChanged, boolean forkNotification) {
661681
IAnnotationMap annotations= getAnnotationMap();
662682
Object mapLock = annotations.getLockObject();
663683

664-
if (mapLock == null) {
665-
Iterator<Annotation> e= annotations.keySetIterator();
666-
while (e.hasNext()) {
667-
Annotation a= e.next();
668-
Position p= annotations.get(a);
684+
synchronized (mapLock) {
685+
annotations.forEach((a, p) -> {
669686
if (p == null || p.isDeleted()) {
670687
deleted.add(a);
671688
}
672-
}
673-
} else {
674-
synchronized (mapLock) {
675-
annotations.forEach((a, p) -> {
676-
if (p == null || p.isDeleted()) {
677-
deleted.add(a);
678-
}
679-
});
680-
}
689+
});
681690
}
682691

683692
if (fireModelChanged && forkNotification) {

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,28 @@ public interface IAnnotationMap extends Map<Annotation, Position>, ISynchronizab
7979
*/
8080
@Override
8181
Collection<Position> values();
82+
83+
/**
84+
* Implementers of this interface should <b>never</b> return null. Clients should use the lock
85+
* object in order to synchronize concurrent access to the implementer.
86+
*
87+
* @return the lock object to be used, if no explicit lock object is set, internal one should
88+
* be used, therefore never <code>null</code>
89+
*/
90+
@Override
91+
Object getLockObject();
92+
93+
/**
94+
* Sets the lock object for this object. Subsequent calls to specified methods of this object
95+
* are synchronized on this lock object. Which methods are synchronized is specified by the
96+
* implementer.
97+
* <p>
98+
* <em>You should not override an existing lock object unless you own that lock object yourself.
99+
* Use the existing lock object instead.</em>
100+
* </p>
101+
*
102+
* @param lockObject the lock object. Never <code>null</code>.
103+
*/
104+
@Override
105+
void setLockObject(Object lockObject);
82106
}

0 commit comments

Comments
 (0)