Skip to content

Commit 16d27c3

Browse files
committed
[GR-23488] All accesses to registeredAutoloads or the nested data structures should be done under the lock
1 parent d8f759e commit 16d27c3

File tree

1 file changed

+26
-18
lines changed

1 file changed

+26
-18
lines changed

src/main/java/org/truffleruby/language/loader/FeatureLoader.java

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ public class FeatureLoader {
5555

5656
private final ReentrantLockFreeingMap<String> fileLocks = new ReentrantLockFreeingMap<>();
5757
/** 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. */
5961
private final Map<String, Map<String, List<RubyConstant>>> registeredAutoloads = new HashMap<>();
6062
private final ReentrantLock registeredAutoloadsLock = new ReentrantLock();
6163

@@ -86,7 +88,9 @@ public void addAutoload(RubyConstant autoloadConstant) {
8688
try {
8789
final Map<String, List<RubyConstant>> constants = ConcurrentOperations
8890
.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);
9094
} finally {
9195
registeredAutoloadsLock.unlock();
9296
}
@@ -102,27 +106,31 @@ public List<RubyConstant> getAutoloadConstants(String expandedPath) {
102106
if (constantsMap == null || constantsMap.isEmpty()) {
103107
return null;
104108
}
105-
} finally {
106-
registeredAutoloadsLock.unlock();
107-
}
108109

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+
}
117123
}
118124
}
119125
}
120-
}
121126

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();
126134
}
127135
}
128136

0 commit comments

Comments
 (0)