24
24
import org .truffleruby .core .proc .RubyProc ;
25
25
import org .truffleruby .core .thread .ThreadManager ;
26
26
import org .truffleruby .core .thread .ThreadManager .BlockingAction ;
27
+ import org .truffleruby .language .SafepointAction ;
27
28
import org .truffleruby .language .control .BreakException ;
28
29
import org .truffleruby .language .control .DynamicReturnException ;
29
30
import org .truffleruby .language .control .ExitException ;
42
43
public class FiberManager {
43
44
44
45
public static final String NAME_PREFIX = "Ruby Fiber" ;
46
+ public static final Object [] SAFEPOINT_ARGS = new Object []{ FiberSafepointMessage .class };
45
47
46
48
private final RubyLanguage language ;
47
49
private final RubyContext context ;
@@ -106,6 +108,7 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
106
108
107
109
final Object prev = truffleContext .enter (currentNode );
108
110
language .setupCurrentThread (thread , fiber .rubyThread );
111
+ fiber .rubyThread .setCurrentFiber (fiber );
109
112
110
113
FiberMessage lastMessage = null ;
111
114
try {
@@ -201,7 +204,13 @@ private void assertNotEntered(String reason) {
201
204
202
205
@ TruffleBoundary
203
206
private Object [] handleMessage (RubyFiber fiber , FiberMessage message , Node currentNode ) {
204
- fiber .rubyThread .setCurrentFiber (fiber );
207
+ // Written as a loop to not grow the stack when processing guest safepoints
208
+ while (message instanceof FiberSafepointMessage ) {
209
+ final FiberSafepointMessage safepointMessage = (FiberSafepointMessage ) message ;
210
+ safepointMessage .action .run (fiber .rubyThread , currentNode );
211
+ final RubyFiber sendingFiber = safepointMessage .sendingFiber ;
212
+ message = resumeAndWait (fiber , sendingFiber , FiberOperation .TRANSFER , SAFEPOINT_ARGS , currentNode );
213
+ }
205
214
206
215
if (message instanceof FiberShutdownMessage ) {
207
216
throw new FiberShutdownException (currentNode );
@@ -210,13 +219,16 @@ private Object[] handleMessage(RubyFiber fiber, FiberMessage message, Node curre
210
219
} else if (message instanceof FiberResumeMessage ) {
211
220
final FiberResumeMessage resumeMessage = (FiberResumeMessage ) message ;
212
221
assert language .getCurrentThread () == resumeMessage .getSendingFiber ().rubyThread ;
213
- if (resumeMessage .getOperation () == FiberOperation .RESUME ||
214
- resumeMessage .getOperation () == FiberOperation .RAISE ) {
222
+ final FiberOperation operation = resumeMessage .getOperation ();
223
+
224
+ if (operation == FiberOperation .RESUME || operation == FiberOperation .RAISE ) {
215
225
fiber .lastResumedByFiber = resumeMessage .getSendingFiber ();
216
226
}
217
- if (resumeMessage .getOperation () == FiberOperation .RAISE ) {
227
+
228
+ if (operation == FiberOperation .RAISE ) {
218
229
throw new RaiseException (context , (RubyException ) resumeMessage .getArgs ()[0 ]);
219
230
}
231
+
220
232
return resumeMessage .getArgs ();
221
233
} else {
222
234
throw CompilerDirectives .shouldNotReachHere ();
@@ -232,16 +244,38 @@ private void resume(RubyFiber fromFiber, RubyFiber fiber, FiberOperation operati
232
244
@ TruffleBoundary
233
245
public Object [] transferControlTo (RubyFiber fromFiber , RubyFiber fiber , FiberOperation operation , Object [] args ,
234
246
Node currentNode ) {
235
- final TruffleContext truffleContext = context .getEnv ().getContext ();
247
+ final FiberMessage message = resumeAndWait (fromFiber , fiber , operation , args , currentNode );
248
+ return handleMessage (fromFiber , message , currentNode );
249
+ }
236
250
251
+ @ TruffleBoundary
252
+ private FiberMessage resumeAndWait (RubyFiber fromFiber , RubyFiber fiber , FiberOperation operation , Object [] args ,
253
+ Node currentNode ) {
254
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
237
255
final FiberMessage message = context
238
256
.getThreadManager ()
239
257
.leaveAndEnter (truffleContext , currentNode , () -> {
240
258
resume (fromFiber , fiber , operation , args );
241
259
return waitMessage (fromFiber , currentNode );
242
260
});
261
+ fromFiber .rubyThread .setCurrentFiber (fromFiber );
262
+ return message ;
263
+ }
243
264
244
- return handleMessage (fromFiber , message , currentNode );
265
+ @ TruffleBoundary
266
+ public void safepoint (RubyFiber fromFiber , RubyFiber fiber , SafepointAction action , Node currentNode ) {
267
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
268
+ final FiberResumeMessage returnMessage = (FiberResumeMessage ) context
269
+ .getThreadManager ()
270
+ .leaveAndEnter (truffleContext , currentNode , () -> {
271
+ addToMessageQueue (fiber , new FiberSafepointMessage (fromFiber , action ));
272
+ return waitMessage (fromFiber , currentNode );
273
+ });
274
+ fromFiber .rubyThread .setCurrentFiber (fromFiber );
275
+
276
+ if (returnMessage .getArgs () != SAFEPOINT_ARGS ) {
277
+ throw CompilerDirectives .shouldNotReachHere ();
278
+ }
245
279
}
246
280
247
281
public void start (RubyFiber fiber , Thread javaThread ) {
@@ -385,6 +419,16 @@ public Object[] getArgs() {
385
419
386
420
}
387
421
422
+ private static class FiberSafepointMessage implements FiberMessage {
423
+ private final RubyFiber sendingFiber ;
424
+ private final SafepointAction action ;
425
+
426
+ private FiberSafepointMessage (RubyFiber sendingFiber , SafepointAction action ) {
427
+ this .sendingFiber = sendingFiber ;
428
+ this .action = action ;
429
+ }
430
+ }
431
+
388
432
/** Used to cleanup and terminate Fibers when the parent Thread dies. */
389
433
private static class FiberShutdownException extends TerminationException {
390
434
private static final long serialVersionUID = 1522270454305076317L ;
0 commit comments