Skip to content

Commit c49cf17

Browse files
committed
[GR-33075] Inline the state of FiberManager in RubyThread to remove one indirection
PullRequest: truffleruby/2869
2 parents 022b62c + 66ce8e4 commit c49cf17

File tree

11 files changed

+135
-155
lines changed

11 files changed

+135
-155
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.truffleruby.core.array.RubyArray;
4848
import org.truffleruby.core.encoding.EncodingManager;
4949
import org.truffleruby.core.exception.CoreExceptions;
50+
import org.truffleruby.core.fiber.FiberManager;
5051
import org.truffleruby.core.hash.PreInitializationManager;
5152
import org.truffleruby.core.hash.ReHashable;
5253
import org.truffleruby.core.inlined.CoreMethods;
@@ -142,6 +143,7 @@ public class RubyContext {
142143
private final CoreLibrary coreLibrary;
143144
@CompilationFinal private CoreMethods coreMethods;
144145
private final ThreadManager threadManager;
146+
public final FiberManager fiberManager;
145147
private final LexicalScope rootLexicalScope;
146148
private volatile ConsoleHolder consoleHolder;
147149

@@ -226,6 +228,7 @@ public RubyContext(RubyLanguage language, TruffleLanguage.Env env) {
226228

227229
Metrics.printTime("before-thread-manager");
228230
threadManager = new ThreadManager(this, language);
231+
fiberManager = new FiberManager(language, this);
229232
threadManager.initialize();
230233
threadManager.initializeMainThread(Thread.currentThread());
231234
Metrics.printTime("after-thread-manager");

src/main/java/org/truffleruby/core/fiber/FiberManager.java

Lines changed: 19 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,16 @@
99
*/
1010
package org.truffleruby.core.fiber;
1111

12-
import java.util.Set;
13-
import java.util.concurrent.ConcurrentHashMap;
1412
import java.util.concurrent.CountDownLatch;
1513

1614
import com.oracle.truffle.api.TruffleContext;
1715
import com.oracle.truffle.api.TruffleSafepoint;
1816
import org.truffleruby.RubyContext;
1917
import org.truffleruby.RubyLanguage;
2018
import org.truffleruby.core.DummyNode;
21-
import org.truffleruby.core.array.ArrayHelpers;
22-
import org.truffleruby.core.array.RubyArray;
2319
import org.truffleruby.core.basicobject.BasicObjectNodes.ObjectIDNode;
24-
import org.truffleruby.core.basicobject.RubyBasicObject;
2520
import org.truffleruby.core.exception.ExceptionOperations;
2621
import org.truffleruby.core.exception.RubyException;
27-
import org.truffleruby.core.klass.RubyClass;
2822
import org.truffleruby.core.proc.ProcOperations;
2923
import org.truffleruby.core.thread.RubyThread;
3024
import org.truffleruby.core.proc.RubyProc;
@@ -37,79 +31,24 @@
3731
import org.truffleruby.language.control.RaiseException;
3832
import org.truffleruby.language.control.TerminationException;
3933

40-
import com.oracle.truffle.api.CompilerAsserts;
4134
import com.oracle.truffle.api.CompilerDirectives;
4235
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4336
import com.oracle.truffle.api.nodes.Node;
44-
import com.oracle.truffle.api.object.Shape;
4537
import com.oracle.truffle.api.profiles.BranchProfile;
4638
import com.oracle.truffle.api.source.SourceSection;
47-
import org.truffleruby.language.objects.ObjectGraphNode;
4839
import org.truffleruby.language.objects.shared.SharedObjects;
4940

50-
/** Manages Ruby {@code Fiber} objects for a given Ruby thread. */
51-
public class FiberManager implements ObjectGraphNode {
41+
/** Helps managing Ruby {@code Fiber} objects. Only one per {@link RubyContext}. */
42+
public class FiberManager {
5243

5344
public static final String NAME_PREFIX = "Ruby Fiber";
5445

5546
private final RubyLanguage language;
5647
private final RubyContext context;
57-
private final RubyFiber rootFiber;
58-
private RubyFiber currentFiber;
59-
private final Set<RubyFiber> runningFibers = newFiberSet();
6048

61-
public FiberManager(RubyLanguage language, RubyContext context, RubyThread rubyThread) {
49+
public FiberManager(RubyLanguage language, RubyContext context) {
6250
this.language = language;
6351
this.context = context;
64-
this.rootFiber = createRootFiber(language, context, rubyThread);
65-
this.currentFiber = rootFiber;
66-
}
67-
68-
@TruffleBoundary
69-
private static Set<RubyFiber> newFiberSet() {
70-
return ConcurrentHashMap.newKeySet();
71-
}
72-
73-
public RubyFiber getRootFiber() {
74-
return rootFiber;
75-
}
76-
77-
public RubyFiber getCurrentFiber() {
78-
assert language
79-
.getCurrentThread().fiberManager == this : "Trying to read the current Fiber of another Thread which is inherently racy";
80-
return currentFiber;
81-
}
82-
83-
// If the currentFiber is read from another Ruby Thread,
84-
// there is no guarantee that fiber will remain the current one
85-
// as it could switch to another Fiber before the actual operation on the returned fiber.
86-
public RubyFiber getCurrentFiberRacy() {
87-
return currentFiber;
88-
}
89-
90-
private void setCurrentFiber(RubyFiber fiber) {
91-
currentFiber = fiber;
92-
}
93-
94-
private RubyFiber createRootFiber(RubyLanguage language, RubyContext context, RubyThread thread) {
95-
return createFiber(
96-
language,
97-
context,
98-
thread,
99-
context.getCoreLibrary().fiberClass,
100-
language.fiberShape,
101-
"root");
102-
}
103-
104-
public RubyFiber createFiber(RubyLanguage language, RubyContext context, RubyThread thread, RubyClass rubyClass,
105-
Shape shape, String sourceLocation) {
106-
CompilerAsserts.partialEvaluationConstant(language);
107-
final RubyBasicObject fiberLocals = new RubyBasicObject(
108-
context.getCoreLibrary().objectClass,
109-
language.basicObjectShape);
110-
final RubyArray catchTags = ArrayHelpers.createEmptyArray(context, language);
111-
112-
return new RubyFiber(rubyClass, shape, fiberLocals, catchTags, thread, sourceLocation);
11352
}
11453

11554
public void initialize(RubyFiber fiber, RubyProc block, Node currentNode) {
@@ -149,7 +88,7 @@ public static void waitForInitialization(RubyContext context, RubyFiber fiber, N
14988
private static final BranchProfile UNPROFILED = BranchProfile.getUncached();
15089

15190
private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Node currentNode) {
152-
assert fiber != rootFiber : "Root Fibers execute threadMain() and not fiberMain()";
91+
assert !fiber.isRootFiber() : "Root Fibers execute threadMain() and not fiberMain()";
15392

15493
final boolean entered = !context.getOptions().FIBER_LEAVE_CONTEXT;
15594
assert entered == context.getEnv().getContext().isEntered();
@@ -220,8 +159,9 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
220159
}
221160

222161
public RubyFiber getReturnFiber(RubyFiber currentFiber, Node currentNode, BranchProfile errorProfile) {
223-
assert currentFiber == this.currentFiber;
162+
assert currentFiber == currentFiber.rubyThread.getCurrentFiber();
224163

164+
final RubyFiber rootFiber = currentFiber.rubyThread.getRootFiber();
225165
if (currentFiber == rootFiber) {
226166
errorProfile.enter();
227167
throw new RaiseException(context, context.getCoreExceptions().yieldFromRootFiberError(currentNode));
@@ -270,7 +210,7 @@ private void assertNotEntered(String reason) {
270210

271211
@TruffleBoundary
272212
private Object[] handleMessage(RubyFiber fiber, FiberMessage message, Node currentNode) {
273-
setCurrentFiber(fiber);
213+
fiber.rubyThread.setCurrentFiber(fiber);
274214

275215
if (message instanceof FiberShutdownMessage) {
276216
throw new FiberShutdownException(currentNode);
@@ -318,9 +258,8 @@ public void start(RubyFiber fiber, Thread javaThread) {
318258

319259
if (Thread.currentThread() == javaThread) {
320260
context.getThreadManager().rubyFiber.set(fiber);
321-
} else {
322-
context.getThreadManager().javaThreadToRubyFiber.put(javaThread, fiber);
323261
}
262+
context.getThreadManager().javaThreadToRubyFiber.put(javaThread, fiber);
324263

325264
fiber.thread = javaThread;
326265

@@ -329,7 +268,7 @@ public void start(RubyFiber fiber, Thread javaThread) {
329268

330269
// share RubyFiber as its fiberLocals might be accessed by other threads with Thread#[]
331270
SharedObjects.propagate(language, rubyThread, fiber);
332-
runningFibers.add(fiber);
271+
rubyThread.runningFibers.add(fiber);
333272
}
334273

335274
public void cleanup(RubyFiber fiber, Thread javaThread) {
@@ -339,7 +278,7 @@ public void cleanup(RubyFiber fiber, Thread javaThread) {
339278

340279
threadManager.cleanupValuesForJavaThread(javaThread);
341280

342-
runningFibers.remove(fiber);
281+
fiber.rubyThread.runningFibers.remove(fiber);
343282

344283
fiber.thread = null;
345284

@@ -352,7 +291,7 @@ public void cleanup(RubyFiber fiber, Thread javaThread) {
352291
}
353292

354293
@TruffleBoundary
355-
public void killOtherFibers() {
294+
public void killOtherFibers(RubyThread thread) {
356295
// All Fibers except the current one are in waitForResume(),
357296
// so sending a FiberShutdownMessage is enough to finish them.
358297
// This also avoids the performance cost of a safepoint.
@@ -366,17 +305,17 @@ public void killOtherFibers() {
366305
try {
367306
final TruffleContext truffleContext = context.getEnv().getContext();
368307
context.getThreadManager().leaveAndEnter(truffleContext, DummyNode.INSTANCE, () -> {
369-
doKillOtherFibers();
308+
doKillOtherFibers(thread);
370309
return BlockingAction.SUCCESS;
371310
});
372311
} finally {
373312
safepoint.setAllowSideEffects(allowSideEffects);
374313
}
375314
}
376315

377-
private void doKillOtherFibers() {
378-
for (RubyFiber fiber : runningFibers) {
379-
if (fiber != rootFiber) {
316+
private void doKillOtherFibers(RubyThread thread) {
317+
for (RubyFiber fiber : thread.runningFibers) {
318+
if (!fiber.isRootFiber()) {
380319
addToMessageQueue(fiber, new FiberShutdownMessage());
381320

382321
// Wait for the Fiber to finish so we only run one Fiber at a time
@@ -394,16 +333,10 @@ private void doKillOtherFibers() {
394333
}
395334
}
396335

397-
@Override
398-
public void getAdjacentObjects(Set<Object> reachable) {
399-
// share fibers of a thread as its fiberLocals might be accessed by other threads with Thread#[]
400-
reachable.addAll(runningFibers);
401-
}
402-
403-
public String getFiberDebugInfo() {
336+
public String getFiberDebugInfo(RubyThread rubyThread) {
404337
final StringBuilder builder = new StringBuilder();
405338

406-
for (RubyFiber fiber : runningFibers) {
339+
for (RubyFiber fiber : rubyThread.runningFibers) {
407340
builder.append(" fiber @");
408341
builder.append(ObjectIDNode.getUncached().execute(fiber));
409342

@@ -414,11 +347,11 @@ public String getFiberDebugInfo() {
414347
builder.append(" #").append(thread.getId()).append(' ').append(thread);
415348
}
416349

417-
if (fiber == rootFiber) {
350+
if (fiber.isRootFiber()) {
418351
builder.append(" (root)");
419352
}
420353

421-
if (fiber == currentFiber) {
354+
if (fiber == fiber.rubyThread.getCurrentFiber()) {
422355
builder.append(" (current)");
423356
}
424357

src/main/java/org/truffleruby/core/fiber/FiberNodes.java

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ protected Object transfer(
7979
coreExceptions().fiberError("fiber called across threads", this));
8080
}
8181

82-
final FiberManager fiberManager = currentThread.fiberManager;
83-
return singleValue(fiberManager.transferControlTo(currentFiber, fiber, operation, args, this));
82+
return singleValue(getContext().fiberManager.transferControlTo(currentFiber, fiber, operation, args, this));
8483
}
8584

8685
}
@@ -95,14 +94,13 @@ protected RubyFiber allocate(RubyClass rubyClass) {
9594
}
9695

9796
final RubyThread thread = getLanguage().getCurrentThread();
98-
final RubyFiber fiber = thread.fiberManager
99-
.createFiber(
100-
getLanguage(),
101-
getContext(),
102-
thread,
103-
rubyClass,
104-
getLanguage().fiberShape,
105-
"<uninitialized>");
97+
final RubyFiber fiber = new RubyFiber(
98+
rubyClass,
99+
getLanguage().fiberShape,
100+
getContext(),
101+
getLanguage(),
102+
thread,
103+
"<uninitialized>");
106104
AllocationTracing.trace(fiber, this);
107105
return fiber;
108106
}
@@ -115,7 +113,7 @@ public abstract static class InitializeNode extends CoreMethodArrayArgumentsNode
115113
@Specialization
116114
protected Object initialize(RubyFiber fiber, RubyProc block) {
117115
final RubyThread thread = getLanguage().getCurrentThread();
118-
thread.fiberManager.initialize(fiber, block, this);
116+
getContext().fiberManager.initialize(fiber, block, this);
119117
return nil;
120118
}
121119

@@ -138,8 +136,7 @@ protected Object resume(RubyFiber fiber, Object[] args,
138136
fiber.transferred = true;
139137

140138
final RubyThread currentThread = getLanguage().getCurrentThread();
141-
final FiberManager fiberManager = currentThread.fiberManager;
142-
final RubyFiber currentFiber = fiberManager.getCurrentFiber();
139+
final RubyFiber currentFiber = currentThread.getCurrentFiber();
143140

144141
if (sameFiberProfile.profile(currentFiber == fiber)) {
145142
// A Fiber can transfer to itself
@@ -169,9 +166,8 @@ protected Object resume(FiberOperation operation, RubyFiber fiber, Object[] args
169166
@Cached ConditionProfile transferredProfile) {
170167

171168
final RubyFiber parentFiber = fiber.lastResumedByFiber;
172-
final FiberManager fiberToResumeManager = fiber.rubyThread.fiberManager;
173169

174-
if (doubleResumeProfile.profile(parentFiber != null || fiber == fiberToResumeManager.getRootFiber())) {
170+
if (doubleResumeProfile.profile(parentFiber != null || fiber.isRootFiber())) {
175171
throw new RaiseException(getContext(), coreExceptions().fiberError("double resume", this));
176172
}
177173

@@ -182,8 +178,7 @@ protected Object resume(FiberOperation operation, RubyFiber fiber, Object[] args
182178
}
183179

184180
final RubyThread currentThread = getLanguage().getCurrentThread();
185-
final FiberManager fiberManager = currentThread.fiberManager;
186-
final RubyFiber currentFiber = fiberManager.getCurrentFiber();
181+
final RubyFiber currentFiber = currentThread.getCurrentFiber();
187182

188183
return fiberTransferNode
189184
.executeTransferControlTo(currentThread, currentFiber, fiber, operation, args);
@@ -233,10 +228,10 @@ protected Object fiberYield(Object[] args,
233228
@Cached BranchProfile errorProfile) {
234229

235230
final RubyThread currentThread = getLanguage().getCurrentThread();
236-
final FiberManager fiberManager = currentThread.fiberManager;
237-
final RubyFiber currentFiber = fiberManager.getCurrentFiber();
231+
final RubyFiber currentFiber = currentThread.getCurrentFiber();
238232

239-
final RubyFiber fiberYieldedTo = fiberManager.getReturnFiber(currentFiber, this, errorProfile);
233+
final RubyFiber fiberYieldedTo = getContext().fiberManager
234+
.getReturnFiber(currentFiber, this, errorProfile);
240235

241236
return fiberTransferNode.executeTransferControlTo(
242237
currentThread,
@@ -263,8 +258,7 @@ public abstract static class CurrentNode extends CoreMethodNode {
263258

264259
@Specialization
265260
protected RubyFiber current() {
266-
final RubyThread currentThread = getLanguage().getCurrentThread();
267-
return currentThread.fiberManager.getCurrentFiber();
261+
return getLanguage().getCurrentThread().getCurrentFiber();
268262
}
269263

270264
}
@@ -300,8 +294,7 @@ public abstract static class FiberGetCatchTagsNode extends PrimitiveArrayArgumen
300294

301295
@Specialization
302296
protected RubyArray getCatchTags() {
303-
final RubyThread currentThread = getLanguage().getCurrentThread();
304-
final RubyFiber currentFiber = currentThread.fiberManager.getCurrentFiber();
297+
final RubyFiber currentFiber = getLanguage().getCurrentThread().getCurrentFiber();
305298
return currentFiber.catchTags;
306299
}
307300
}

0 commit comments

Comments
 (0)