@@ -55,7 +55,9 @@ public class FeatureLoader {
55
55
56
56
private final ReentrantLockFreeingMap <String > fileLocks = new ReentrantLockFreeingMap <>();
57
57
/** Maps basename without extension -> autoload path -> autoload constant, to detect when require-ing a file already
58
- * registered with autoload. */
58
+ * registered with autoload.
59
+ *
60
+ * Synchronization: Both levels of Map and the Lists are protected by registeredAutoloadsLock. */
59
61
private final Map <String , Map <String , List <RubyConstant >>> registeredAutoloads = new HashMap <>();
60
62
private final ReentrantLock registeredAutoloadsLock = new ReentrantLock ();
61
63
@@ -86,29 +88,38 @@ public void addAutoload(RubyConstant autoloadConstant) {
86
88
try {
87
89
final Map <String , List <RubyConstant >> constants = ConcurrentOperations
88
90
.getOrCompute (registeredAutoloads , basename , k -> new LinkedHashMap <>());
89
- ConcurrentOperations .getOrCompute (constants , autoloadPath , k -> new ArrayList <>()).add (autoloadConstant );
91
+ final List <RubyConstant > list = ConcurrentOperations
92
+ .getOrCompute (constants , autoloadPath , k -> new ArrayList <>());
93
+ list .add (autoloadConstant );
90
94
} finally {
91
95
registeredAutoloadsLock .unlock ();
92
96
}
93
97
}
94
98
95
99
public List <RubyConstant > getAutoloadConstants (String expandedPath ) {
96
100
final String basename = basenameWithoutExtension (expandedPath );
97
- final Map <String , List < RubyConstant >> constantsMap ;
101
+ final Map <String , RubyConstant []> constantsMapCopy ;
98
102
99
103
registeredAutoloadsLock .lock ();
100
104
try {
101
- constantsMap = registeredAutoloads .get (basename );
105
+ final Map < String , List < RubyConstant >> constantsMap = registeredAutoloads .get (basename );
102
106
if (constantsMap == null || constantsMap .isEmpty ()) {
103
107
return null ;
104
108
}
109
+
110
+ // Deep-copy constantsMap so we can call findFeature() outside the lock
111
+ constantsMapCopy = new LinkedHashMap <>();
112
+ for (Map .Entry <String , List <RubyConstant >> entry : constantsMap .entrySet ()) {
113
+ constantsMapCopy .put (entry .getKey (), entry .getValue ().toArray (RubyConstant .EMPTY_ARRAY ));
114
+ }
105
115
} finally {
106
116
registeredAutoloadsLock .unlock ();
107
117
}
108
118
109
119
final List <RubyConstant > constants = new ArrayList <>();
110
- for (Map .Entry <String , List < RubyConstant >> entry : constantsMap .entrySet ()) {
120
+ for (Map .Entry <String , RubyConstant []> entry : constantsMapCopy .entrySet ()) {
111
121
final String expandedAutoloadPath = findFeature (entry .getKey ());
122
+
112
123
if (expandedPath .equals (expandedAutoloadPath )) {
113
124
for (RubyConstant constant : entry .getValue ()) {
114
125
// Do not autoload recursively from the #require call in GetConstantNode
@@ -252,9 +263,7 @@ public String findFeatureImpl(String feature) {
252
263
RubyContext .fileLine (sourceSection ),
253
264
originalFeature );
254
265
});
255
- }
256
266
257
- if (context .getOptions ().LOG_FEATURE_LOCATION ) {
258
267
RubyLanguage .LOGGER .info (String .format ("current directory: %s" , getWorkingDirectory ()));
259
268
}
260
269
@@ -392,7 +401,7 @@ public void ensureCExtImplementationLoaded(String feature, RequireNode requireNo
392
401
throw new RaiseException (
393
402
context ,
394
403
context .getCoreExceptions ().loadError (
395
- "cannot load as C extensions are disabled with -Xcexts =false" ,
404
+ "cannot load as C extensions are disabled with --ruby.cexts =false" ,
396
405
feature ,
397
406
null ));
398
407
}
0 commit comments