Skip to content

Commit 2d03437

Browse files
committed
Fixed map concurrency bug in TypeFunctions.
1 parent 973a2e1 commit 2d03437

File tree

3 files changed

+39
-10
lines changed

3 files changed

+39
-10
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515

1616
<groupId>rwperrott</groupId>
1717
<artifactId>rwperrott-string-template-utils</artifactId>
18-
<version>2.2.2</version>
18+
<version>2.2.3</version>
1919
<packaging>jar</packaging>
2020

21-
<name>StringTemplate Utils</name>
21+
<name>${project.artifactId}:%{project.version}</name>
2222

2323
<dependencies>
2424
<dependency>

src/main/java/rwperrott/stringtemplate/v4/TypeFunctions.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,12 @@ private static class ByName extends Object2ObjectAVLTreeMap<String, MemberInvoke
3535
private final Class<?> valueType;
3636

3737
@SuppressWarnings("LeakingThisInConstructor")
38-
private ByName(final Class<?> valueType) {
38+
private ByName(final Class<?> valueType, final ByName superInstance) {
3939
dejaVu.add(valueType);
4040
this.valueType = valueType;
4141
ClassMembers.of(valueType).addTo(this);
42-
// Shouldn't need to do this!
43-
final Class<?> superType = valueType.getSuperclass();
44-
if (null != superType) {
45-
ByName parent = FOR_TYPE.computeIfAbsent(superType, ByName::new);
46-
final ObjectSet<MemberInvoker> unique = new ObjectOpenHashSet<>();
47-
mergeInstanceInvokers(unique, parent);
42+
if (null != superInstance) {
43+
mergeInstanceInvokers(new ObjectOpenHashSet<>(), superInstance);
4844
}
4945
}
5046

@@ -130,9 +126,23 @@ public static void registerFunctionClasses(Class<?> valueType, Class<?>... funct
130126
}
131127
}
132128

129+
private static ByName get0(Class<?> valueType) {
130+
// Have to split get and put to avoid ConcurrentModificationException.
131+
ByName byName = FOR_TYPE.get(valueType);
132+
if (null != byName)
133+
return byName;
134+
ByName superInstance = null;
135+
final Class<?> superType = valueType.getSuperclass();
136+
if (null != superType) {
137+
superInstance = get0(superType);
138+
}
139+
FOR_TYPE.put(valueType, byName = new ByName(valueType, superInstance));
140+
return byName;
141+
}
142+
133143
private static ByName get(Class<?> valueType) {
134144
synchronized (FOR_TYPE) {
135-
return FOR_TYPE.computeIfAbsent(valueType, ByName::new);
145+
return get0(valueType);
136146
}
137147
}
138148

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package rwperrott.stringtemplate.v4;
2+
3+
import org.testng.annotations.Test;
4+
5+
import static org.testng.Assert.*;
6+
7+
public class TypeFunctionsTest {
8+
@Test
9+
public void test() {
10+
try {
11+
new StringInvokeRenderer();
12+
new ObjectInvokeAdaptor();
13+
new StringInvokeAdaptor();
14+
} catch (Throwable t) {
15+
t.printStackTrace();
16+
fail("failed",t);
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)