Skip to content

Commit 7969535

Browse files
committed
[GR-33075] Use a ContextThreadLocal to get the current RubyThread
PullRequest: truffleruby/2867
2 parents a3dcf3d + 2211481 commit 7969535

27 files changed

+202
-305
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Performance:
3636
* Improved `String#gsub` performance by adding a fast path for the `string_byte_index` primitive (#2380, @nirvdrum).
3737
* Improved `String#index` performance by adding a fast path for the `string_character_index` primitive (#2383, @LillianZ).
3838
* Optimized conversion of strings to integers if the string contained a numeric value (#2401, @nirvdrum).
39+
* Use Truffle's `ContextThreadLocal` to speedup access to thread-local data.
3940

4041
Changes:
4142

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ public void initialize() {
250250
coreMethods = new CoreMethods(language, this);
251251

252252
// Load the part of the core library defined in Ruby
253-
254253
Metrics.printTime("before-load-core");
255254
coreLibrary.loadRubyCoreLibraryAndPostBoot();
256255
Metrics.printTime("after-load-core");

src/main/java/org/truffleruby/RubyLanguage.java

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import com.oracle.truffle.api.CompilerAsserts;
2323
import com.oracle.truffle.api.CompilerDirectives;
24+
import com.oracle.truffle.api.ContextThreadLocal;
2425
import com.oracle.truffle.api.TruffleFile;
2526
import com.oracle.truffle.api.instrumentation.AllocationReporter;
2627
import com.oracle.truffle.api.instrumentation.Instrumenter;
@@ -171,6 +172,15 @@ public final class RubyLanguage extends TruffleLanguage<RubyContext> {
171172

172173
public static final TruffleLogger LOGGER = TruffleLogger.getLogger(TruffleRuby.LANGUAGE_ID);
173174

175+
/** We need an extra indirection added to ContextThreadLocal due to multiple Fibers of different Ruby Threads
176+
* sharing the same Java Thread when using the fiber pool. */
177+
private static final class ThreadLocalState {
178+
private RubyThread rubyThread;
179+
}
180+
181+
private final ContextThreadLocal<ThreadLocalState> threadLocalState = createContextThreadLocal(
182+
(context, thread) -> new ThreadLocalState());
183+
174184
private final CyclicAssumption tracingCyclicAssumption = new CyclicAssumption("object-space-tracing");
175185
@CompilationFinal private volatile Assumption tracingAssumption = tracingCyclicAssumption.getAssumption();
176186

@@ -276,16 +286,6 @@ public static RubyLanguage get(Node node) {
276286
return REFERENCE.get(node);
277287
}
278288

279-
public RubyLanguage() {
280-
coreMethodAssumptions = new CoreMethodAssumptions(this);
281-
coreStrings = new CoreStrings(this);
282-
coreSymbols = new CoreSymbols();
283-
primitiveManager = new PrimitiveManager();
284-
ropeCache = new RopeCache(coreSymbols);
285-
symbolTable = new SymbolTable(ropeCache, coreSymbols);
286-
frozenStringLiterals = new FrozenStringLiterals(ropeCache);
287-
}
288-
289289
public static String getMimeType(boolean coverageEnabled) {
290290
return coverageEnabled ? MIME_TYPE_COVERAGE : MIME_TYPE;
291291
}
@@ -321,12 +321,36 @@ public static String filenameLine(SourceSection section) {
321321
}
322322
}
323323

324+
public RubyLanguage() {
325+
coreMethodAssumptions = new CoreMethodAssumptions(this);
326+
coreStrings = new CoreStrings(this);
327+
coreSymbols = new CoreSymbols();
328+
primitiveManager = new PrimitiveManager();
329+
ropeCache = new RopeCache(coreSymbols);
330+
symbolTable = new SymbolTable(ropeCache, coreSymbols);
331+
frozenStringLiterals = new FrozenStringLiterals(ropeCache);
332+
}
333+
334+
public RubyThread getCurrentThread() {
335+
final RubyThread rubyThread = threadLocalState.get().rubyThread;
336+
if (rubyThread == null) {
337+
CompilerDirectives.transferToInterpreterAndInvalidate();
338+
throw CompilerDirectives.shouldNotReachHere(
339+
"No Ruby Thread is associated with current Java Thread: " + Thread.currentThread());
340+
}
341+
return rubyThread;
342+
}
343+
344+
public void setupCurrentThread(Thread javaThread, RubyThread rubyThread) {
345+
final ThreadLocalState threadLocalState = this.threadLocalState.get(javaThread);
346+
threadLocalState.rubyThread = rubyThread;
347+
}
348+
324349
@TruffleBoundary
325350
public RubySymbol getSymbol(String string) {
326351
return symbolTable.getSymbol(string);
327352
}
328353

329-
330354
@TruffleBoundary
331355
public RubySymbol getSymbol(Rope rope, RubyEncoding encoding) {
332356
return symbolTable.getSymbol(rope, encoding);
@@ -555,6 +579,7 @@ public void initializeThread(RubyContext context, Thread thread) {
555579

556580
if (thread == context.getThreadManager().getOrInitializeRootJavaThread()) {
557581
// Already initialized when creating the context
582+
setupCurrentThread(thread, context.getThreadManager().getRootThread());
558583
return;
559584
}
560585

@@ -566,6 +591,7 @@ public void initializeThread(RubyContext context, Thread thread) {
566591
.shouldNotReachHere("Ruby threads should be initialized on their Java thread");
567592
}
568593
context.getThreadManager().start(rubyThread, thread);
594+
setupCurrentThread(thread, rubyThread);
569595
} else {
570596
// Fiber
571597
}
@@ -574,12 +600,13 @@ public void initializeThread(RubyContext context, Thread thread) {
574600

575601
final RubyThread foreignThread = context.getThreadManager().createForeignThread();
576602
context.getThreadManager().startForeignThread(foreignThread, thread);
603+
setupCurrentThread(thread, foreignThread);
577604
}
578605

579606
@Override
580607
public void disposeThread(RubyContext context, Thread thread) {
581608
LOGGER.fine(
582-
() -> "disposeThread(#" + thread.getId() + " " + thread + " " +
609+
() -> "disposeThread(#" + thread.getId() + " " + thread + " on " +
583610
context.getThreadManager().getCurrentThreadOrNull() + ")");
584611

585612
if (thread == context.getThreadManager().getRootJavaThread()) {
@@ -612,7 +639,7 @@ public void disposeThread(RubyContext context, Thread thread) {
612639
}
613640

614641
// A foreign Thread, its Fibers are considered isRubyManagedThread()
615-
final RubyThread rubyThread = context.getThreadManager().getRubyThread(thread);
642+
final RubyThread rubyThread = context.getThreadManager().getRubyThreadForJavaThread(thread);
616643
context.getThreadManager().cleanup(rubyThread, thread);
617644
}
618645

src/main/java/org/truffleruby/core/ReferenceProcessingService.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,19 +167,21 @@ protected void processReferenceQueue(Class<?> owner) {
167167
}
168168
}
169169

170+
private static final String THREAD_NAME = "Ruby-reference-processor";
171+
170172
protected void createProcessingThread(Class<?> owner) {
171173
final ThreadManager threadManager = context.getThreadManager();
172174
RubyThread newThread;
173175
synchronized (this) {
174176
if (processingThread != null) {
175177
return;
176178
}
177-
newThread = threadManager.createBootThread(threadName());
179+
newThread = threadManager.createBootThread(THREAD_NAME);
178180
processingThread = newThread;
179181
}
180-
final String sharingReason = "creating " + threadName() + " thread for " + owner.getSimpleName();
182+
final String sharingReason = "creating " + THREAD_NAME + " thread for " + owner.getSimpleName();
181183

182-
threadManager.initialize(newThread, DummyNode.INSTANCE, threadName(), sharingReason, () -> {
184+
threadManager.initialize(newThread, DummyNode.INSTANCE, THREAD_NAME, sharingReason, () -> {
183185
while (true) {
184186
final ProcessingReference<?> reference = threadManager
185187
.runUntilResult(DummyNode.INSTANCE, () -> {
@@ -224,10 +226,6 @@ public RubyThread getProcessingThread() {
224226
return processingThread;
225227
}
226228

227-
protected final String threadName() {
228-
return "Ruby-reference-processor";
229-
}
230-
231229
protected final void drainReferenceQueues() {
232230
ReferenceQueue<Object> sharedReferenceQueue = context.getLanguageSlow().sharedReferenceQueue;
233231
while (true) {

src/main/java/org/truffleruby/core/VMPrimitiveNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ protected boolean watchSignalProc(Object signalString, boolean isRubyDefaultHand
270270
@CachedLibrary(limit = "2") RubyStringLibrary libSignalString) {
271271
final RubyContext context = getContext();
272272

273-
if (context.getThreadManager().getCurrentThread() != context.getThreadManager().getRootThread()) {
273+
if (getLanguage().getCurrentThread() != context.getThreadManager().getRootThread()) {
274274
// The proc will be executed on the main thread
275275
SharedObjects.writeBarrier(getLanguage(), action);
276276
}

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ public RubyException frozenError(String message, Node currentNode, Object receiv
291291
RubyString errorMessage = StringOperations
292292
.createUTF8String(context, language, StringOperations.encodeRope(message, UTF8Encoding.INSTANCE));
293293
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
294-
final Object cause = ThreadGetExceptionNode.getLastException(context);
294+
final Object cause = ThreadGetExceptionNode.getLastException(language);
295295
showExceptionIfDebug(exceptionClass, errorMessage, backtrace);
296296
return new RubyFrozenError(
297297
exceptionClass,
@@ -826,7 +826,7 @@ public RubyNameError nameError(String message, Object receiver, String name, Nod
826826
.createUTF8String(context, language, StringOperations.encodeRope(message, UTF8Encoding.INSTANCE));
827827
final RubyClass exceptionClass = context.getCoreLibrary().nameErrorClass;
828828
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
829-
final Object cause = ThreadGetExceptionNode.getLastException(context);
829+
final Object cause = ThreadGetExceptionNode.getLastException(language);
830830
showExceptionIfDebug(exceptionClass, messageString, backtrace);
831831
return new RubyNameError(
832832
context.getCoreLibrary().nameErrorClass,
@@ -843,7 +843,7 @@ public RubyNameError nameErrorFromMethodMissing(ExceptionFormatter formatter, Ob
843843
Node currentNode) {
844844
// omit = 1 to skip over the call to `method_missing'. MRI does not show this is the backtrace.
845845
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode, 1);
846-
final Object cause = ThreadGetExceptionNode.getLastException(context);
846+
final Object cause = ThreadGetExceptionNode.getLastException(language);
847847

848848
final RubyProc formatterProc = formatter.getProc(context);
849849
final String message = formatter.getMessage(formatterProc, name, receiver);
@@ -870,7 +870,7 @@ public RubyNoMethodError noMethodErrorFromMethodMissing(ExceptionFormatter forma
870870

871871
// omit = 1 to skip over the call to `method_missing'. MRI does not show this is the backtrace.
872872
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode, 1);
873-
final Object cause = ThreadGetExceptionNode.getLastException(context);
873+
final Object cause = ThreadGetExceptionNode.getLastException(language);
874874

875875
final RubyProc formatterProc = formatter.getProc(context);
876876
final String message = formatter.getMessage(formatterProc, name, receiver);
@@ -896,7 +896,7 @@ public RubyNoMethodError noMethodError(String message, Object receiver, String n
896896
final RubyArray argsArray = createArray(context, language, args);
897897
final RubyClass exceptionClass = context.getCoreLibrary().noMethodErrorClass;
898898
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
899-
final Object cause = ThreadGetExceptionNode.getLastException(context);
899+
final Object cause = ThreadGetExceptionNode.getLastException(language);
900900
showExceptionIfDebug(exceptionClass, messageString, backtrace);
901901
return new RubyNoMethodError(
902902
context.getCoreLibrary().noMethodErrorClass,
@@ -917,7 +917,7 @@ public RubyNoMethodError noSuperMethodOutsideMethodError(Node currentNode) {
917917
StringOperations.encodeRope("super called outside of method", UTF8Encoding.INSTANCE));
918918
final RubyClass exceptionClass = context.getCoreLibrary().nameErrorClass;
919919
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
920-
final Object cause = ThreadGetExceptionNode.getLastException(context);
920+
final Object cause = ThreadGetExceptionNode.getLastException(language);
921921
showExceptionIfDebug(exceptionClass, messageString, backtrace);
922922
// TODO BJF Jul 21, 2016 Review to add receiver
923923
return new RubyNoMethodError(
@@ -998,7 +998,7 @@ public RubySyntaxError syntaxError(String message, Node currentNode, SourceSecti
998998
final RubyString messageString = StringOperations
999999
.createUTF8String(context, language, StringOperations.encodeRope(message, UTF8Encoding.INSTANCE));
10001000
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
1001-
final Object cause = ThreadGetExceptionNode.getLastException(context);
1001+
final Object cause = ThreadGetExceptionNode.getLastException(language);
10021002
showExceptionIfDebug(exceptionClass, messageString, backtrace);
10031003
return new RubySyntaxError(
10041004
exceptionClass,
@@ -1254,7 +1254,7 @@ public RubySystemExit systemExit(int exitStatus, Node currentNode) {
12541254
.createUTF8String(context, language, StringOperations.encodeRope("exit", UTF8Encoding.INSTANCE));
12551255
final RubyClass exceptionClass = context.getCoreLibrary().systemExitClass;
12561256
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
1257-
final Object cause = ThreadGetExceptionNode.getLastException(context);
1257+
final Object cause = ThreadGetExceptionNode.getLastException(language);
12581258
showExceptionIfDebug(exceptionClass, message, backtrace);
12591259
return new RubySystemExit(
12601260
exceptionClass,

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import com.oracle.truffle.api.CompilerDirectives;
1313
import org.truffleruby.RubyContext;
14+
import org.truffleruby.RubyLanguage;
1415
import org.truffleruby.core.kernel.KernelNodes;
1516
import org.truffleruby.core.klass.RubyClass;
1617
import org.truffleruby.core.module.ModuleFields;
@@ -112,30 +113,33 @@ public static RubyException createRubyException(RubyContext context, RubyClass r
112113
@TruffleBoundary
113114
public static RubyException createRubyException(RubyContext context, RubyClass rubyClass, Object message,
114115
Backtrace backtrace) {
115-
final Object cause = ThreadGetExceptionNode.getLastException(context);
116+
final RubyLanguage language = context.getLanguageSlow();
117+
final Object cause = ThreadGetExceptionNode.getLastException(language);
116118
context.getCoreExceptions().showExceptionIfDebug(rubyClass, message, backtrace);
117-
final Shape shape = context.getLanguageSlow().exceptionShape;
119+
final Shape shape = language.exceptionShape;
118120
return new RubyException(rubyClass, shape, message, backtrace, cause);
119121
}
120122

121123
@TruffleBoundary
122124
public static RubyException createSystemStackError(RubyContext context, Object message, Backtrace backtrace,
123125
boolean showExceptionIfDebug) {
126+
final RubyLanguage language = context.getLanguageSlow();
124127
final RubyClass rubyClass = context.getCoreLibrary().systemStackErrorClass;
125-
final Object cause = ThreadGetExceptionNode.getLastException(context);
128+
final Object cause = ThreadGetExceptionNode.getLastException(language);
126129
if (showExceptionIfDebug) {
127130
context.getCoreExceptions().showExceptionIfDebug(rubyClass, message, backtrace);
128131
}
129-
final Shape shape = context.getLanguageSlow().exceptionShape;
132+
final Shape shape = language.exceptionShape;
130133
return new RubyException(rubyClass, shape, message, backtrace, cause);
131134
}
132135

133136
@TruffleBoundary
134137
public static RubySystemCallError createSystemCallError(RubyContext context, RubyClass rubyClass,
135138
Object message, int errno, Backtrace backtrace) {
136-
final Object cause = ThreadGetExceptionNode.getLastException(context);
139+
final RubyLanguage language = context.getLanguageSlow();
140+
final Object cause = ThreadGetExceptionNode.getLastException(language);
137141
context.getCoreExceptions().showExceptionIfDebug(rubyClass, message, backtrace);
138-
final Shape shape = context.getLanguageSlow().systemCallErrorShape;
142+
final Shape shape = language.systemCallErrorShape;
139143
return new RubySystemCallError(rubyClass, shape, message, backtrace, cause, errno);
140144
}
141145

0 commit comments

Comments
 (0)