Skip to content

Commit 25eacca

Browse files
committed
[GR-26850] Fix synchronization for concurrent Module#autoload on the same constant
PullRequest: truffleruby/2097
2 parents 509467a + b4fcc2c commit 25eacca

File tree

14 files changed

+70
-35
lines changed

14 files changed

+70
-35
lines changed

src/main/java/org/truffleruby/core/array/ArrayNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1591,7 +1591,7 @@ private RubyString finishPack(int formatLength, BytesResult result) {
15911591

15921592
@TruffleBoundary
15931593
protected RootCallTarget compileFormat(RubyString format) {
1594-
return new PackCompiler(getContext(), this).compile(format.toString());
1594+
return new PackCompiler(getContext(), this).compile(format.getJavaString());
15951595
}
15961596

15971597
protected int getCacheLimit() {

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ public void showExceptionIfDebug(RubyClass rubyClass, Object message, Backtrace
8989
}
9090
}
9191

92+
public String inspectReceiver(Object receiver) {
93+
RubyString rubyString = (RubyString) context.send(
94+
context.getCoreLibrary().truffleExceptionOperationsModule,
95+
"receiver_string",
96+
receiver);
97+
return rubyString.getJavaString();
98+
}
99+
92100
// ArgumentError
93101

94102
public RubyException argumentErrorOneHashRequired(Node currentNode) {
@@ -488,17 +496,17 @@ public RubyException typeErrorCantConvertInto(Object from, String toClass, Node
488496

489497
@TruffleBoundary
490498
public RubyException typeErrorIsNotA(Object value, String expectedType, Node currentNode) {
491-
return typeErrorIsNotA(value.toString(), expectedType, currentNode);
499+
return typeErrorIsNotA(inspectReceiver(value), expectedType, currentNode);
492500
}
493501

494502
@TruffleBoundary
495503
public RubyException typeErrorIsNotA(String value, String expectedType, Node currentNode) {
496-
return typeError(StringUtils.format("%s is not a %s", value, expectedType), currentNode);
504+
return typeError(value + " is not a " + expectedType, currentNode);
497505
}
498506

499507
@TruffleBoundary
500508
public RubyException typeErrorIsNotAClassModule(Object value, Node currentNode) {
501-
return typeError(StringUtils.format("%s is not a class/module", value), currentNode);
509+
return typeError(inspectReceiver(value) + " is not a class/module", currentNode);
502510
}
503511

504512
@TruffleBoundary

src/main/java/org/truffleruby/core/module/ModuleFields.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ public RubyConstant setConstant(RubyContext context, Node currentNode, String na
343343
@TruffleBoundary
344344
public void setAutoloadConstant(RubyContext context, Node currentNode, String name, RubyString filename) {
345345
RubyConstant autoloadConstant = setConstantInternal(context, currentNode, name, filename, true);
346+
if (autoloadConstant == null) {
347+
return;
348+
}
349+
346350
if (context.getOptions().LOG_AUTOLOAD) {
347351
RubyLanguage.LOGGER.info(() -> String.format(
348352
"%s: setting up autoload %s with %s",
@@ -351,7 +355,7 @@ public void setAutoloadConstant(RubyContext context, Node currentNode, String na
351355
filename));
352356
}
353357
final ReentrantLockFreeingMap<String> fileLocks = getContext().getFeatureLoader().getFileLocks();
354-
final ReentrantLock lock = fileLocks.get(filename.toString());
358+
final ReentrantLock lock = fileLocks.get(filename.getJavaString());
355359
if (lock.isLocked()) {
356360
// We need to handle the new autoload constant immediately
357361
// if Object.autoload(name, filename) is executed from filename.rb
@@ -367,10 +371,22 @@ private RubyConstant setConstantInternal(RubyContext context, Node currentNode,
367371

368372
SharedObjects.propagate(context, rubyModule, value);
369373

374+
final String autoloadPath = autoload ? ((RubyString) value).getJavaString() : null;
370375
RubyConstant previous;
371376
RubyConstant newConstant;
372377
do {
373378
previous = constants.get(name);
379+
if (autoload && previous != null) {
380+
if (previous.hasValue()) {
381+
// abort, do not set an autoload constant, the constant already has a value
382+
return null;
383+
} else if (previous.isAutoload() &&
384+
previous.getAutoloadConstant().getAutoloadPath().equals(autoloadPath)) {
385+
// already an autoload constant with the same path,
386+
// do nothing so we don't replace the AutoloadConstant#autoloadLock which might be already acquired
387+
return null;
388+
}
389+
}
374390
newConstant = newConstant(currentNode, name, value, autoload, previous);
375391
} while (!ConcurrentOperations.replace(constants, name, previous, newConstant));
376392

src/main/java/org/truffleruby/core/module/ModuleNodes.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,11 +580,7 @@ protected Object autoload(RubyModule module, String name, RubyString filename) {
580580
throw new RaiseException(getContext(), coreExceptions().argumentError("empty file name", this));
581581
}
582582

583-
final RubyConstant constant = module.fields.getConstant(name);
584-
if (constant == null || !constant.hasValue()) {
585-
module.fields.setAutoloadConstant(getContext(), this, name, filename);
586-
}
587-
583+
module.fields.setAutoloadConstant(getContext(), this, name, filename);
588584
return nil;
589585
}
590586
}

src/main/java/org/truffleruby/core/regexp/MatchDataNodes.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,11 @@ private int getBackRefFromRope(RubyMatchData matchData, Object index, Rope value
389389
0,
390390
value.byteLength(),
391391
matchData.region);
392-
} catch (final ValueException e) {
392+
} catch (ValueException e) {
393393
throw new RaiseException(
394394
getContext(),
395395
coreExceptions().indexError(
396-
StringUtils.format("undefined group name reference: %s", index.toString()),
396+
StringUtils.format("undefined group name reference: %s", index),
397397
this));
398398
}
399399
}

src/main/java/org/truffleruby/core/rope/ManagedRope.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@ public final byte[] getBytes() {
5454

5555
@Override
5656
public final String toString() {
57-
assert ALLOW_TO_STRING;
58-
59-
final byte[] bytesBefore = bytes;
60-
final String string = RopeOperations.decodeOrEscapeBinaryRope(this, RopeOperations.flattenBytes(this));
61-
assert bytes == bytesBefore : "bytes should not be modified by Rope#toString() as otherwise inspecting a Rope would have a side effect";
62-
return string;
57+
if (DEBUG_ROPE_BYTES) {
58+
final byte[] bytesBefore = bytes;
59+
final String string = RopeOperations.decodeOrEscapeBinaryRope(this, RopeOperations.flattenBytes(this));
60+
assert bytes == bytesBefore : "bytes should not be modified by Rope#toString() as otherwise inspecting a Rope would have a side effect";
61+
return string;
62+
} else {
63+
return RopeOperations.decodeRope(this);
64+
}
6365
}
6466

6567
}

src/main/java/org/truffleruby/core/rope/NativeRope.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ public int hashCode() {
184184

185185
@Override
186186
public String toString() {
187-
assert ALLOW_TO_STRING;
188187
return toLeafRope().toString();
189188
}
190189

src/main/java/org/truffleruby/core/rope/Rope.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public abstract class Rope implements Comparable<Rope> {
2020
// NativeRope, RepeatingRope, 3 LeafRope, ConcatRope, SubstringRope, 1 LazyRope
2121
public static final int NUMBER_OF_CONCRETE_CLASSES = 8;
2222

23-
// Useful for debugging. Setting to false allow to catch wrong usages.
24-
protected static final boolean ALLOW_TO_STRING = true;
23+
// Useful for debugging. Setting to true avoids ManagedRope#toString to populate bytes as a side-effect of the debugger calling toString().
24+
protected static final boolean DEBUG_ROPE_BYTES = false;
2525

2626
public final Encoding encoding;
2727
private final int byteLength;

src/main/java/org/truffleruby/core/thread/ThreadManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ private void threadMain(RubyThread thread, Node currentNode, Supplier<Object> ta
315315
final String message = StringUtils
316316
.format("%s terminated with internal error:", Thread.currentThread().getName());
317317
final RuntimeException runtimeException = new RuntimeException(message, e);
318+
// Immediately print internal exceptions, in case they would cause a deadlock
319+
runtimeException.printStackTrace();
318320
rethrowOnMainThread(currentNode, runtimeException);
319321
setThreadValue(context, thread, Nil.INSTANCE);
320322
} finally {

src/main/java/org/truffleruby/language/SafepointManager.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,12 @@ private void driveArrivalAtPhaser() {
209209
}
210210

211211
private void printStacktracesOfBlockedThreads() {
212+
final Thread drivingThread = Thread.currentThread();
213+
212214
System.err.println("Dumping stacktraces of all threads:");
213215
for (Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces().entrySet()) {
214216
final Thread thread = entry.getKey();
215-
if (thread != Thread.currentThread() && runningThreads.contains(thread)) {
217+
if (runningThreads.contains(thread)) {
216218
final StackTraceElement[] stackTrace = entry.getValue();
217219
boolean blocked = true;
218220

@@ -225,7 +227,8 @@ private void printStacktracesOfBlockedThreads() {
225227
}
226228
}
227229

228-
System.err.println((blocked ? "BLOCKED: " : "IN SAFEPOINT: ") + thread);
230+
String kind = thread == drivingThread ? "DRIVER" : (blocked ? "BLOCKED" : "IN SAFEPOINT");
231+
System.err.println(kind + ": " + thread);
229232
for (StackTraceElement stackTraceElement : stackTrace) {
230233
System.err.println(stackTraceElement);
231234
}

0 commit comments

Comments
 (0)