9
9
*/
10
10
package org .truffleruby .core .fiber ;
11
11
12
- import java .util .Set ;
13
- import java .util .concurrent .ConcurrentHashMap ;
14
12
import java .util .concurrent .CountDownLatch ;
15
13
16
14
import com .oracle .truffle .api .TruffleContext ;
17
15
import com .oracle .truffle .api .TruffleSafepoint ;
18
16
import org .truffleruby .RubyContext ;
19
17
import org .truffleruby .RubyLanguage ;
20
18
import org .truffleruby .core .DummyNode ;
21
- import org .truffleruby .core .array .ArrayHelpers ;
22
- import org .truffleruby .core .array .RubyArray ;
23
19
import org .truffleruby .core .basicobject .BasicObjectNodes .ObjectIDNode ;
24
- import org .truffleruby .core .basicobject .RubyBasicObject ;
25
20
import org .truffleruby .core .exception .ExceptionOperations ;
26
21
import org .truffleruby .core .exception .RubyException ;
27
- import org .truffleruby .core .klass .RubyClass ;
28
22
import org .truffleruby .core .proc .ProcOperations ;
29
23
import org .truffleruby .core .thread .RubyThread ;
30
24
import org .truffleruby .core .proc .RubyProc ;
37
31
import org .truffleruby .language .control .RaiseException ;
38
32
import org .truffleruby .language .control .TerminationException ;
39
33
40
- import com .oracle .truffle .api .CompilerAsserts ;
41
34
import com .oracle .truffle .api .CompilerDirectives ;
42
35
import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
43
36
import com .oracle .truffle .api .nodes .Node ;
44
- import com .oracle .truffle .api .object .Shape ;
45
37
import com .oracle .truffle .api .profiles .BranchProfile ;
46
38
import com .oracle .truffle .api .source .SourceSection ;
47
- import org .truffleruby .language .objects .ObjectGraphNode ;
48
39
import org .truffleruby .language .objects .shared .SharedObjects ;
49
40
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 {
52
43
53
44
public static final String NAME_PREFIX = "Ruby Fiber" ;
54
45
55
46
private final RubyLanguage language ;
56
47
private final RubyContext context ;
57
- private final RubyFiber rootFiber ;
58
- private RubyFiber currentFiber ;
59
- private final Set <RubyFiber > runningFibers = newFiberSet ();
60
48
61
- public FiberManager (RubyLanguage language , RubyContext context , RubyThread rubyThread ) {
49
+ public FiberManager (RubyLanguage language , RubyContext context ) {
62
50
this .language = language ;
63
51
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 );
113
52
}
114
53
115
54
public void initialize (RubyFiber fiber , RubyProc block , Node currentNode ) {
@@ -149,7 +88,7 @@ public static void waitForInitialization(RubyContext context, RubyFiber fiber, N
149
88
private static final BranchProfile UNPROFILED = BranchProfile .getUncached ();
150
89
151
90
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()" ;
153
92
154
93
final boolean entered = !context .getOptions ().FIBER_LEAVE_CONTEXT ;
155
94
assert entered == context .getEnv ().getContext ().isEntered ();
@@ -220,8 +159,9 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
220
159
}
221
160
222
161
public RubyFiber getReturnFiber (RubyFiber currentFiber , Node currentNode , BranchProfile errorProfile ) {
223
- assert currentFiber == this . currentFiber ;
162
+ assert currentFiber == currentFiber . rubyThread . getCurrentFiber () ;
224
163
164
+ final RubyFiber rootFiber = currentFiber .rubyThread .getRootFiber ();
225
165
if (currentFiber == rootFiber ) {
226
166
errorProfile .enter ();
227
167
throw new RaiseException (context , context .getCoreExceptions ().yieldFromRootFiberError (currentNode ));
@@ -270,7 +210,7 @@ private void assertNotEntered(String reason) {
270
210
271
211
@ TruffleBoundary
272
212
private Object [] handleMessage (RubyFiber fiber , FiberMessage message , Node currentNode ) {
273
- setCurrentFiber (fiber );
213
+ fiber . rubyThread . setCurrentFiber (fiber );
274
214
275
215
if (message instanceof FiberShutdownMessage ) {
276
216
throw new FiberShutdownException (currentNode );
@@ -318,9 +258,8 @@ public void start(RubyFiber fiber, Thread javaThread) {
318
258
319
259
if (Thread .currentThread () == javaThread ) {
320
260
context .getThreadManager ().rubyFiber .set (fiber );
321
- } else {
322
- context .getThreadManager ().javaThreadToRubyFiber .put (javaThread , fiber );
323
261
}
262
+ context .getThreadManager ().javaThreadToRubyFiber .put (javaThread , fiber );
324
263
325
264
fiber .thread = javaThread ;
326
265
@@ -329,7 +268,7 @@ public void start(RubyFiber fiber, Thread javaThread) {
329
268
330
269
// share RubyFiber as its fiberLocals might be accessed by other threads with Thread#[]
331
270
SharedObjects .propagate (language , rubyThread , fiber );
332
- runningFibers .add (fiber );
271
+ rubyThread . runningFibers .add (fiber );
333
272
}
334
273
335
274
public void cleanup (RubyFiber fiber , Thread javaThread ) {
@@ -339,7 +278,7 @@ public void cleanup(RubyFiber fiber, Thread javaThread) {
339
278
340
279
threadManager .cleanupValuesForJavaThread (javaThread );
341
280
342
- runningFibers .remove (fiber );
281
+ fiber . rubyThread . runningFibers .remove (fiber );
343
282
344
283
fiber .thread = null ;
345
284
@@ -352,7 +291,7 @@ public void cleanup(RubyFiber fiber, Thread javaThread) {
352
291
}
353
292
354
293
@ TruffleBoundary
355
- public void killOtherFibers () {
294
+ public void killOtherFibers (RubyThread thread ) {
356
295
// All Fibers except the current one are in waitForResume(),
357
296
// so sending a FiberShutdownMessage is enough to finish them.
358
297
// This also avoids the performance cost of a safepoint.
@@ -366,17 +305,17 @@ public void killOtherFibers() {
366
305
try {
367
306
final TruffleContext truffleContext = context .getEnv ().getContext ();
368
307
context .getThreadManager ().leaveAndEnter (truffleContext , DummyNode .INSTANCE , () -> {
369
- doKillOtherFibers ();
308
+ doKillOtherFibers (thread );
370
309
return BlockingAction .SUCCESS ;
371
310
});
372
311
} finally {
373
312
safepoint .setAllowSideEffects (allowSideEffects );
374
313
}
375
314
}
376
315
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 () ) {
380
319
addToMessageQueue (fiber , new FiberShutdownMessage ());
381
320
382
321
// Wait for the Fiber to finish so we only run one Fiber at a time
@@ -394,16 +333,10 @@ private void doKillOtherFibers() {
394
333
}
395
334
}
396
335
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 ) {
404
337
final StringBuilder builder = new StringBuilder ();
405
338
406
- for (RubyFiber fiber : runningFibers ) {
339
+ for (RubyFiber fiber : rubyThread . runningFibers ) {
407
340
builder .append (" fiber @" );
408
341
builder .append (ObjectIDNode .getUncached ().execute (fiber ));
409
342
@@ -414,11 +347,11 @@ public String getFiberDebugInfo() {
414
347
builder .append (" #" ).append (thread .getId ()).append (' ' ).append (thread );
415
348
}
416
349
417
- if (fiber == rootFiber ) {
350
+ if (fiber . isRootFiber () ) {
418
351
builder .append (" (root)" );
419
352
}
420
353
421
- if (fiber == currentFiber ) {
354
+ if (fiber == fiber . rubyThread . getCurrentFiber () ) {
422
355
builder .append (" (current)" );
423
356
}
424
357
0 commit comments