@@ -98,39 +98,42 @@ public void addAutoload(RubyConstant autoloadConstant) {
98
98
99
99
public List <RubyConstant > getAutoloadConstants (String expandedPath ) {
100
100
final String basename = basenameWithoutExtension (expandedPath );
101
- final Map <String , List < RubyConstant >> constantsMap ;
101
+ final Map <String , RubyConstant []> constantsMapCopy ;
102
102
103
103
registeredAutoloadsLock .lock ();
104
104
try {
105
- constantsMap = registeredAutoloads .get (basename );
105
+ final Map < String , List < RubyConstant >> constantsMap = registeredAutoloads .get (basename );
106
106
if (constantsMap == null || constantsMap .isEmpty ()) {
107
107
return null ;
108
108
}
109
109
110
- final List <RubyConstant > constants = new ArrayList <>();
110
+ // Deep-copy constantsMap so we can call findFeature() outside the lock
111
+ constantsMapCopy = new LinkedHashMap <>();
111
112
for (Map .Entry <String , List <RubyConstant >> entry : constantsMap .entrySet ()) {
112
- // NOTE: this call might be expensive but it seems difficult to move it outside the lock.
113
- // Contention does not seem a big issue since only addAutoload/removeAutoload need to wait.
114
- // At least, findFeature() does not access registeredAutoloads or use registeredAutoloadsLock.
115
- final String expandedAutoloadPath = findFeature (entry .getKey ());
116
-
117
- if (expandedPath .equals (expandedAutoloadPath )) {
118
- for (RubyConstant constant : entry .getValue ()) {
119
- // Do not autoload recursively from the #require call in GetConstantNode
120
- if (!constant .getAutoloadConstant ().isAutoloading ()) {
121
- constants .add (constant );
122
- }
113
+ constantsMapCopy .put (entry .getKey (), entry .getValue ().toArray (RubyConstant .EMPTY_ARRAY ));
114
+ }
115
+ } finally {
116
+ registeredAutoloadsLock .unlock ();
117
+ }
118
+
119
+ final List <RubyConstant > constants = new ArrayList <>();
120
+ for (Map .Entry <String , RubyConstant []> entry : constantsMapCopy .entrySet ()) {
121
+ final String expandedAutoloadPath = findFeature (entry .getKey ());
122
+
123
+ if (expandedPath .equals (expandedAutoloadPath )) {
124
+ for (RubyConstant constant : entry .getValue ()) {
125
+ // Do not autoload recursively from the #require call in GetConstantNode
126
+ if (!constant .getAutoloadConstant ().isAutoloading ()) {
127
+ constants .add (constant );
123
128
}
124
129
}
125
130
}
131
+ }
126
132
127
- if (constants .isEmpty ()) {
128
- return null ;
129
- } else {
130
- return constants ;
131
- }
132
- } finally {
133
- registeredAutoloadsLock .unlock ();
133
+ if (constants .isEmpty ()) {
134
+ return null ;
135
+ } else {
136
+ return constants ;
134
137
}
135
138
}
136
139
0 commit comments