Skip to content

Commit 8050bb4

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 cbf687d commit 8050bb4

File tree

3 files changed

+74
-14
lines changed

3 files changed

+74
-14
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: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,30 @@ protected IAnnotationMap getAnnotationMap() {
338338
return (IAnnotationMap) fAnnotations;
339339
}
340340

341+
/**
342+
* Subclasses should <b>never</b> return null. Clients should use the lock
343+
* object in order to synchronize concurrent access to the internal map data.
344+
*
345+
* @return <b>never</b> returns null
346+
*/
341347
@Override
342348
public Object getLockObject() {
343349
return getAnnotationMap().getLockObject();
344350
}
345351

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

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);
683+
synchronized (mapLock) {
684+
annotations.forEach((a, p) -> {
669685
if (p == null || p.isDeleted()) {
670686
deleted.add(a);
671687
}
672-
}
673-
} else {
674-
synchronized (mapLock) {
675-
annotations.forEach((a, p) -> {
676-
if (p == null || p.isDeleted()) {
677-
deleted.add(a);
678-
}
679-
});
680-
}
688+
});
681689
}
682690

683691
if (fireModelChanged && forkNotification) {

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,27 @@ 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 <b>never</b> returns null
88+
*/
89+
@Override
90+
Object getLockObject();
91+
92+
/**
93+
* Sets the lock object for this object. Subsequent calls to specified methods of this object
94+
* are synchronized on this lock object. Which methods are synchronized is specified by the
95+
* implementer.
96+
* <p>
97+
* <em>You should not override an existing lock object unless you own that lock object yourself.
98+
* Use the existing lock object instead.</em>
99+
* </p>
100+
*
101+
* @param lockObject the lock object. Never <code>null</code>.
102+
*/
103+
@Override
104+
void setLockObject(Object lockObject);
82105
}

0 commit comments

Comments
 (0)