@@ -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,7 +88,9 @@ 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
}
@@ -102,27 +106,31 @@ public List<RubyConstant> getAutoloadConstants(String expandedPath) {
102
106
if (constantsMap == null || constantsMap .isEmpty ()) {
103
107
return null ;
104
108
}
105
- } finally {
106
- registeredAutoloadsLock .unlock ();
107
- }
108
109
109
- final List <RubyConstant > constants = new ArrayList <>();
110
- for (Map .Entry <String , List <RubyConstant >> entry : constantsMap .entrySet ()) {
111
- final String expandedAutoloadPath = findFeature (entry .getKey ());
112
- if (expandedPath .equals (expandedAutoloadPath )) {
113
- for (RubyConstant constant : entry .getValue ()) {
114
- // Do not autoload recursively from the #require call in GetConstantNode
115
- if (!constant .getAutoloadConstant ().isAutoloading ()) {
116
- constants .add (constant );
110
+ final List <RubyConstant > constants = new ArrayList <>();
111
+ 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
+ }
117
123
}
118
124
}
119
125
}
120
- }
121
126
122
- if (constants .isEmpty ()) {
123
- return null ;
124
- } else {
125
- return constants ;
127
+ if (constants .isEmpty ()) {
128
+ return null ;
129
+ } else {
130
+ return constants ;
131
+ }
132
+ } finally {
133
+ registeredAutoloadsLock .unlock ();
126
134
}
127
135
}
128
136
0 commit comments