Skip to content

Commit ddce559

Browse files
committed
Inline the state of FiberManager in RubyThread to remove one indirection
1 parent 7969535 commit ddce559

File tree

10 files changed

+114
-119
lines changed

10 files changed

+114
-119
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: 21 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
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;
@@ -44,54 +42,22 @@
4442
import com.oracle.truffle.api.object.Shape;
4543
import com.oracle.truffle.api.profiles.BranchProfile;
4644
import com.oracle.truffle.api.source.SourceSection;
47-
import org.truffleruby.language.objects.ObjectGraphNode;
4845
import org.truffleruby.language.objects.shared.SharedObjects;
4946

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

5350
public static final String NAME_PREFIX = "Ruby Fiber";
5451

5552
private final RubyLanguage language;
5653
private final RubyContext context;
57-
private final RubyFiber rootFiber;
58-
private RubyFiber currentFiber;
59-
private final Set<RubyFiber> runningFibers = newFiberSet();
6054

61-
public FiberManager(RubyLanguage language, RubyContext context, RubyThread rubyThread) {
55+
public FiberManager(RubyLanguage language, RubyContext context) {
6256
this.language = language;
6357
this.context = context;
64-
this.rootFiber = createRootFiber(language, context, rubyThread);
65-
this.currentFiber = rootFiber;
6658
}
6759

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) {
60+
public static RubyFiber createRootFiber(RubyLanguage language, RubyContext context, RubyThread thread) {
9561
return createFiber(
9662
language,
9763
context,
@@ -101,8 +67,8 @@ private RubyFiber createRootFiber(RubyLanguage language, RubyContext context, Ru
10167
"root");
10268
}
10369

104-
public RubyFiber createFiber(RubyLanguage language, RubyContext context, RubyThread thread, RubyClass rubyClass,
105-
Shape shape, String sourceLocation) {
70+
public static RubyFiber createFiber(RubyLanguage language, RubyContext context, RubyThread thread,
71+
RubyClass rubyClass, Shape shape, String sourceLocation) {
10672
CompilerAsserts.partialEvaluationConstant(language);
10773
final RubyBasicObject fiberLocals = new RubyBasicObject(
10874
context.getCoreLibrary().objectClass,
@@ -149,7 +115,7 @@ public static void waitForInitialization(RubyContext context, RubyFiber fiber, N
149115
private static final BranchProfile UNPROFILED = BranchProfile.getUncached();
150116

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

154120
final boolean entered = !context.getOptions().FIBER_LEAVE_CONTEXT;
155121
assert entered == context.getEnv().getContext().isEntered();
@@ -220,8 +186,9 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
220186
}
221187

222188
public RubyFiber getReturnFiber(RubyFiber currentFiber, Node currentNode, BranchProfile errorProfile) {
223-
assert currentFiber == this.currentFiber;
189+
assert currentFiber == currentFiber.rubyThread.getCurrentFiber();
224190

191+
final RubyFiber rootFiber = currentFiber.rubyThread.getRootFiber();
225192
if (currentFiber == rootFiber) {
226193
errorProfile.enter();
227194
throw new RaiseException(context, context.getCoreExceptions().yieldFromRootFiberError(currentNode));
@@ -270,7 +237,7 @@ private void assertNotEntered(String reason) {
270237

271238
@TruffleBoundary
272239
private Object[] handleMessage(RubyFiber fiber, FiberMessage message, Node currentNode) {
273-
setCurrentFiber(fiber);
240+
fiber.rubyThread.setCurrentFiber(fiber);
274241

275242
if (message instanceof FiberShutdownMessage) {
276243
throw new FiberShutdownException(currentNode);
@@ -329,7 +296,7 @@ public void start(RubyFiber fiber, Thread javaThread) {
329296

330297
// share RubyFiber as its fiberLocals might be accessed by other threads with Thread#[]
331298
SharedObjects.propagate(language, rubyThread, fiber);
332-
runningFibers.add(fiber);
299+
rubyThread.runningFibers.add(fiber);
333300
}
334301

335302
public void cleanup(RubyFiber fiber, Thread javaThread) {
@@ -339,7 +306,7 @@ public void cleanup(RubyFiber fiber, Thread javaThread) {
339306

340307
threadManager.cleanupValuesForJavaThread(javaThread);
341308

342-
runningFibers.remove(fiber);
309+
fiber.rubyThread.runningFibers.remove(fiber);
343310

344311
fiber.thread = null;
345312

@@ -352,7 +319,7 @@ public void cleanup(RubyFiber fiber, Thread javaThread) {
352319
}
353320

354321
@TruffleBoundary
355-
public void killOtherFibers() {
322+
public void killOtherFibers(RubyThread thread) {
356323
// All Fibers except the current one are in waitForResume(),
357324
// so sending a FiberShutdownMessage is enough to finish them.
358325
// This also avoids the performance cost of a safepoint.
@@ -366,17 +333,17 @@ public void killOtherFibers() {
366333
try {
367334
final TruffleContext truffleContext = context.getEnv().getContext();
368335
context.getThreadManager().leaveAndEnter(truffleContext, DummyNode.INSTANCE, () -> {
369-
doKillOtherFibers();
336+
doKillOtherFibers(thread);
370337
return BlockingAction.SUCCESS;
371338
});
372339
} finally {
373340
safepoint.setAllowSideEffects(allowSideEffects);
374341
}
375342
}
376343

377-
private void doKillOtherFibers() {
378-
for (RubyFiber fiber : runningFibers) {
379-
if (fiber != rootFiber) {
344+
private void doKillOtherFibers(RubyThread thread) {
345+
for (RubyFiber fiber : thread.runningFibers) {
346+
if (!fiber.isRootFiber()) {
380347
addToMessageQueue(fiber, new FiberShutdownMessage());
381348

382349
// Wait for the Fiber to finish so we only run one Fiber at a time
@@ -394,16 +361,10 @@ private void doKillOtherFibers() {
394361
}
395362
}
396363

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() {
364+
public String getFiberDebugInfo(RubyThread rubyThread) {
404365
final StringBuilder builder = new StringBuilder();
405366

406-
for (RubyFiber fiber : runningFibers) {
367+
for (RubyFiber fiber : rubyThread.runningFibers) {
407368
builder.append(" fiber @");
408369
builder.append(ObjectIDNode.getUncached().execute(fiber));
409370

@@ -414,11 +375,11 @@ public String getFiberDebugInfo() {
414375
builder.append(" #").append(thread.getId()).append(' ').append(thread);
415376
}
416377

417-
if (fiber == rootFiber) {
378+
if (fiber.isRootFiber()) {
418379
builder.append(" (root)");
419380
}
420381

421-
if (fiber == currentFiber) {
382+
if (fiber == fiber.rubyThread.getCurrentFiber()) {
422383
builder.append(" (current)");
423384
}
424385

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 = FiberManager.createFiber(
98+
getLanguage(),
99+
getContext(),
100+
thread,
101+
rubyClass,
102+
getLanguage().fiberShape,
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
}

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
2525
import com.oracle.truffle.api.object.Shape;
2626

27-
public class RubyFiber extends RubyDynamicObject implements ObjectGraphNode {
27+
public final class RubyFiber extends RubyDynamicObject implements ObjectGraphNode {
2828

2929
public final RubyBasicObject fiberLocals;
3030
public final RubyArray catchTags;
@@ -55,22 +55,15 @@ public RubyFiber(
5555
this.sourceLocation = sourceLocation;
5656
}
5757

58-
@TruffleBoundary
59-
private static LinkedBlockingQueue<FiberManager.FiberMessage> newMessageQueue() {
60-
return new LinkedBlockingQueue<>();
61-
}
62-
63-
@Override
64-
public void getAdjacentObjects(Set<Object> reachable) {
65-
reachable.add(fiberLocals);
66-
reachable.add(rubyThread);
58+
public boolean isRootFiber() {
59+
return rubyThread.getRootFiber() == this;
6760
}
6861

6962
public String getStatus() {
7063
if (!resumed) {
7164
return "created";
7265
} else if (alive) {
73-
if (rubyThread.fiberManager.getCurrentFiberRacy() == this) {
66+
if (rubyThread.getCurrentFiberRacy() == this) {
7467
return "resumed";
7568
} else {
7669
return "suspended";
@@ -80,4 +73,15 @@ public String getStatus() {
8073
}
8174
}
8275

76+
@TruffleBoundary
77+
private static LinkedBlockingQueue<FiberManager.FiberMessage> newMessageQueue() {
78+
return new LinkedBlockingQueue<>();
79+
}
80+
81+
@Override
82+
public void getAdjacentObjects(Set<Object> reachable) {
83+
reachable.add(fiberLocals);
84+
reachable.add(rubyThread);
85+
}
86+
8387
}

0 commit comments

Comments
 (0)