13
13
package clojure .lang ;
14
14
15
15
import java .util .Map ;
16
+ import java .util .concurrent .locks .ReentrantReadWriteLock ;
16
17
17
18
public class MultiFn extends AFn {
18
19
final public IFn dispatchFn ;
19
20
final public Object defaultDispatchVal ;
20
21
final public IRef hierarchy ;
21
22
final String name ;
22
- IPersistentMap methodTable ;
23
- IPersistentMap preferTable ;
24
- IPersistentMap methodCache ;
25
- Object cachedHierarchy ;
23
+ final ReentrantReadWriteLock rw ;
24
+ volatile IPersistentMap methodTable ;
25
+ volatile IPersistentMap preferTable ;
26
+ volatile IPersistentMap methodCache ;
27
+ volatile Object cachedHierarchy ;
26
28
27
29
static final Var assoc = RT .var ("clojure.core" , "assoc" );
28
30
static final Var dissoc = RT .var ("clojure.core" , "dissoc" );
29
31
static final Var isa = RT .var ("clojure.core" , "isa?" );
30
32
static final Var parents = RT .var ("clojure.core" , "parents" );
31
33
32
34
public MultiFn (String name , IFn dispatchFn , Object defaultDispatchVal , IRef hierarchy ) {
35
+ this .rw = new ReentrantReadWriteLock ();
33
36
this .name = name ;
34
37
this .dispatchFn = dispatchFn ;
35
38
this .defaultDispatchVal = defaultDispatchVal ;
@@ -40,35 +43,63 @@ public MultiFn(String name, IFn dispatchFn, Object defaultDispatchVal, IRef hier
40
43
cachedHierarchy = null ;
41
44
}
42
45
43
- synchronized public MultiFn reset (){
44
- methodTable = methodCache = preferTable = PersistentHashMap .EMPTY ;
45
- cachedHierarchy = null ;
46
- return this ;
46
+ public MultiFn reset (){
47
+ rw .writeLock ().lock ();
48
+ try {
49
+ methodTable = methodCache = preferTable = PersistentHashMap .EMPTY ;
50
+ cachedHierarchy = null ;
51
+ return this ;
52
+ }
53
+ finally {
54
+ rw .writeLock ().unlock ();
55
+ }
47
56
}
48
57
49
- synchronized public MultiFn addMethod (Object dispatchVal , IFn method ) {
50
- methodTable = getMethodTable ().assoc (dispatchVal , method );
51
- resetCache ();
52
- return this ;
58
+ public MultiFn addMethod (Object dispatchVal , IFn method ) {
59
+ rw .writeLock ().lock ();
60
+ try {
61
+ methodTable = getMethodTable ().assoc (dispatchVal , method );
62
+ resetCache ();
63
+ return this ;
64
+ }
65
+ finally {
66
+ rw .writeLock ().unlock ();
67
+ }
53
68
}
54
69
55
- synchronized public MultiFn removeMethod (Object dispatchVal ) {
56
- methodTable = getMethodTable ().without (dispatchVal );
57
- resetCache ();
58
- return this ;
70
+ public MultiFn removeMethod (Object dispatchVal ) {
71
+ rw .writeLock ().lock ();
72
+ try
73
+ {
74
+ methodTable = getMethodTable ().without (dispatchVal );
75
+ resetCache ();
76
+ return this ;
77
+ }
78
+ finally
79
+ {
80
+ rw .writeLock ().unlock ();
81
+ }
59
82
}
60
83
61
- synchronized public MultiFn preferMethod (Object dispatchValX , Object dispatchValY ) {
62
- if (prefers (dispatchValY , dispatchValX ))
63
- throw new IllegalStateException (
64
- String .format ("Preference conflict in multimethod '%s': %s is already preferred to %s" ,
65
- name , dispatchValY , dispatchValX ));
66
- preferTable = getPreferTable ().assoc (dispatchValX , RT .conj ((IPersistentCollection ) RT .get (getPreferTable (),
67
- dispatchValX ,
68
- PersistentHashSet .EMPTY ),
69
- dispatchValY ));
70
- resetCache ();
71
- return this ;
84
+ public MultiFn preferMethod (Object dispatchValX , Object dispatchValY ) {
85
+ rw .writeLock ().lock ();
86
+ try
87
+ {
88
+ if (prefers (dispatchValY , dispatchValX ))
89
+ throw new IllegalStateException (
90
+ String .format ("Preference conflict in multimethod '%s': %s is already preferred to %s" ,
91
+ name , dispatchValY , dispatchValX ));
92
+ preferTable = getPreferTable ().assoc (dispatchValX , RT .conj ((IPersistentCollection ) RT .get (getPreferTable (),
93
+ dispatchValX ,
94
+ PersistentHashSet .EMPTY ),
95
+ dispatchValY ));
96
+ resetCache ();
97
+ return this ;
98
+ }
99
+ finally
100
+ {
101
+ rw .writeLock ().unlock ();
102
+ }
72
103
}
73
104
74
105
private boolean prefers (Object x , Object y ) {
@@ -97,18 +128,26 @@ private boolean dominates(Object x, Object y) {
97
128
}
98
129
99
130
private IPersistentMap resetCache () {
100
- methodCache = getMethodTable ();
101
- cachedHierarchy = hierarchy .deref ();
102
- return methodCache ;
131
+ rw .writeLock ().lock ();
132
+ try
133
+ {
134
+ methodCache = getMethodTable ();
135
+ cachedHierarchy = hierarchy .deref ();
136
+ return methodCache ;
137
+ }
138
+ finally
139
+ {
140
+ rw .writeLock ().unlock ();
141
+ }
103
142
}
104
143
105
- synchronized public IFn getMethod (Object dispatchVal ) {
144
+ public IFn getMethod (Object dispatchVal ) {
106
145
if (cachedHierarchy != hierarchy .deref ())
107
146
resetCache ();
108
147
IFn targetFn = (IFn ) methodCache .valAt (dispatchVal );
109
148
if (targetFn != null )
110
149
return targetFn ;
111
- targetFn = findAndCacheBestMethod (dispatchVal );
150
+ targetFn = findAndCacheBestMethod (dispatchVal );
112
151
if (targetFn != null )
113
152
return targetFn ;
114
153
targetFn = (IFn ) getMethodTable ().valAt (defaultDispatchVal );
@@ -124,34 +163,59 @@ private IFn getFn(Object dispatchVal) {
124
163
}
125
164
126
165
private IFn findAndCacheBestMethod (Object dispatchVal ) {
127
- Map .Entry bestEntry = null ;
128
- for (Object o : getMethodTable ())
166
+ rw .readLock ().lock ();
167
+ Map .Entry bestEntry ;
168
+ IPersistentMap mt = methodTable ;
169
+ IPersistentMap pt = preferTable ;
170
+ Object ch = cachedHierarchy ;
171
+ try
129
172
{
130
- Map . Entry e = ( Map . Entry ) o ;
131
- if ( isA ( dispatchVal , e . getKey () ))
173
+ bestEntry = null ;
174
+ for ( Object o : getMethodTable ( ))
132
175
{
133
- if (bestEntry == null || dominates (e .getKey (), bestEntry .getKey ()))
134
- bestEntry = e ;
135
- if (!dominates (bestEntry .getKey (), e .getKey ()))
136
- throw new IllegalArgumentException (
137
- String .format (
138
- "Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred" ,
139
- name , dispatchVal , e .getKey (), bestEntry .getKey ()));
176
+ Map .Entry e = (Map .Entry ) o ;
177
+ if (isA (dispatchVal , e .getKey ()))
178
+ {
179
+ if (bestEntry == null || dominates (e .getKey (), bestEntry .getKey ()))
180
+ bestEntry = e ;
181
+ if (!dominates (bestEntry .getKey (), e .getKey ()))
182
+ throw new IllegalArgumentException (
183
+ String .format (
184
+ "Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred" ,
185
+ name , dispatchVal , e .getKey (), bestEntry .getKey ()));
186
+ }
140
187
}
188
+ if (bestEntry == null )
189
+ return null ;
141
190
}
142
- if (bestEntry == null )
143
- return null ;
191
+ finally
192
+ {
193
+ rw .readLock ().unlock ();
194
+ }
195
+
196
+
144
197
//ensure basis has stayed stable throughout, else redo
145
- if (cachedHierarchy == hierarchy .deref ())
198
+ rw .writeLock ().lock ();
199
+ try
146
200
{
147
- //place in cache
148
- methodCache = methodCache .assoc (dispatchVal , bestEntry .getValue ());
149
- return (IFn ) bestEntry .getValue ();
201
+ if ( mt == methodTable &&
202
+ pt == preferTable &&
203
+ ch == cachedHierarchy &&
204
+ cachedHierarchy == hierarchy .deref ())
205
+ {
206
+ //place in cache
207
+ methodCache = methodCache .assoc (dispatchVal , bestEntry .getValue ());
208
+ return (IFn ) bestEntry .getValue ();
209
+ }
210
+ else
211
+ {
212
+ resetCache ();
213
+ return findAndCacheBestMethod (dispatchVal );
214
+ }
150
215
}
151
- else
216
+ finally
152
217
{
153
- resetCache ();
154
- return findAndCacheBestMethod (dispatchVal );
218
+ rw .writeLock ().unlock ();
155
219
}
156
220
}
157
221
0 commit comments