14
14
import java .util .concurrent .ConcurrentHashMap ;
15
15
import java .util .concurrent .CountDownLatch ;
16
16
17
+ import com .oracle .truffle .api .TruffleContext ;
17
18
import org .truffleruby .RubyContext ;
18
19
import org .truffleruby .RubyLanguage ;
19
20
import org .truffleruby .core .array .ArrayHelpers ;
@@ -103,7 +104,7 @@ public RubyFiber createFiber(RubyLanguage language, RubyContext context, RubyThr
103
104
public void initialize (RubyFiber fiber , RubyProc block , Node currentNode ) {
104
105
ThreadManager .FIBER_BEING_SPAWNED .set (fiber );
105
106
try {
106
- context .getThreadManager ().spawnFiber (() -> fiberMain (context , fiber , block , currentNode ));
107
+ context .getThreadManager ().spawnFiber (fiber , () -> fiberMain (context , fiber , block , currentNode ));
107
108
waitForInitialization (context , fiber , currentNode );
108
109
} finally {
109
110
ThreadManager .FIBER_BEING_SPAWNED .remove ();
@@ -125,7 +126,7 @@ public static void waitForInitialization(RubyContext context, RubyFiber fiber, N
125
126
}
126
127
}
127
128
128
- private static final BranchProfile UNPROFILED = BranchProfile .create ();
129
+ private static final BranchProfile UNPROFILED = BranchProfile .getUncached ();
129
130
130
131
private void fiberMain (RubyContext context , RubyFiber fiber , RubyProc block , Node currentNode ) {
131
132
assert fiber != rootFiber : "Root Fibers execute threadMain() and not fiberMain()" ;
@@ -135,16 +136,25 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
135
136
final String oldName = thread .getName ();
136
137
thread .setName (NAME_PREFIX + " id=" + thread .getId () + " from " + RubyContext .fileLine (sourceSection ));
137
138
138
- start (fiber , thread );
139
- try {
139
+ start (fiber , thread , false );
140
+
141
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
142
+ assert !truffleContext .isEntered ();
140
143
141
- final Object [] args = waitForResume (fiber );
144
+ final FiberMessage message = waitMessage (fiber );
145
+ final Object prev = truffleContext .enter (currentNode );
146
+ context .getSafepointManager ().enterThread (); // not done in start() above because the context was not entered
147
+ try {
142
148
final Object result ;
143
149
try {
150
+ final Object [] args = handleMessage (fiber , message );
144
151
result = ProcOperations .rootCall (block , args );
145
152
} finally {
146
153
// Make sure that other fibers notice we are dead before they gain control back
147
154
fiber .alive = false ;
155
+ // Leave before resume/sendExceptionToParentFiber -> addToMessageQueue() -> parent Fiber starts executing
156
+ context .getSafepointManager ().leaveThread ();
157
+ truffleContext .leave (currentNode , prev );
148
158
}
149
159
resume (fiber , getReturnFiber (fiber , currentNode , UNPROFILED ), FiberOperation .YIELD , result );
150
160
@@ -164,8 +174,11 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
164
174
fiber ,
165
175
new RaiseException (context , context .getCoreExceptions ().unexpectedReturn (currentNode )),
166
176
currentNode );
177
+ } catch (Throwable e ) {
178
+ final RuntimeException exception = ThreadManager .printInternalError (e );
179
+ sendExceptionToParentFiber (fiber , exception , currentNode );
167
180
} finally {
168
- cleanup (fiber , thread );
181
+ cleanup (fiber , thread , false );
169
182
thread .setName (oldName );
170
183
}
171
184
}
@@ -193,16 +206,19 @@ public RubyFiber getReturnFiber(RubyFiber currentFiber, Node currentNode, Branch
193
206
194
207
@ TruffleBoundary
195
208
private void addToMessageQueue (RubyFiber fiber , FiberMessage message ) {
209
+ assert !context .getEnv ().getContext ().isEntered () : "should have left context when sending message to fiber" ;
196
210
fiber .messageQueue .add (message );
197
211
}
198
212
199
- /** Send the Java thread that represents this fiber to sleep until it receives a resume or exit message. */
213
+ /** Send the Java thread that represents this fiber to sleep until it receives a message. */
200
214
@ TruffleBoundary
201
- private Object [] waitForResume (RubyFiber fiber ) {
202
- final FiberMessage message = context .getThreadManager ().runUntilResultKeepStatus (
203
- null ,
204
- () -> fiber . messageQueue . take ());
215
+ private FiberMessage waitMessage (RubyFiber fiber ) {
216
+ assert ! context .getEnv ().getContext (). isEntered () : "should have left context while waiting fiber message" ;
217
+ return ThreadManager . retryWhileInterrupted ( fiber . messageQueue :: take );
218
+ }
205
219
220
+ @ TruffleBoundary
221
+ private Object [] handleMessage (RubyFiber fiber , FiberMessage message ) {
206
222
setCurrentFiber (fiber );
207
223
208
224
if (message instanceof FiberShutdownMessage ) {
@@ -227,12 +243,31 @@ private void resume(RubyFiber fromFiber, RubyFiber fiber, FiberOperation operati
227
243
addToMessageQueue (fiber , new FiberResumeMessage (operation , fromFiber , args ));
228
244
}
229
245
246
+ @ TruffleBoundary
230
247
public Object [] transferControlTo (RubyFiber fromFiber , RubyFiber fiber , FiberOperation operation , Object [] args ) {
231
- resume (fromFiber , fiber , operation , args );
232
- return waitForResume (fromFiber );
248
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
249
+
250
+ final FiberMessage message ;
251
+ final boolean isRubyManagedThread = context .getThreadManager ().isRubyManagedThread (Thread .currentThread ());
252
+ if (isRubyManagedThread ) {
253
+ context .getSafepointManager ().leaveThread ();
254
+ }
255
+ try {
256
+ message = truffleContext .leaveAndEnter (null , () -> {
257
+ resume (fromFiber , fiber , operation , args );
258
+ return waitMessage (fromFiber );
259
+ });
260
+ } finally {
261
+ if (isRubyManagedThread ) {
262
+ context .getSafepointManager ().enterThread ();
263
+ }
264
+ }
265
+
266
+ return handleMessage (fromFiber , message );
233
267
}
234
268
235
- public void start (RubyFiber fiber , Thread javaThread ) {
269
+ public void start (RubyFiber fiber , Thread javaThread , boolean entered ) {
270
+ assert entered == context .getEnv ().getContext ().isEntered ();
236
271
final ThreadManager threadManager = context .getThreadManager ();
237
272
238
273
if (Thread .currentThread () == javaThread ) {
@@ -249,31 +284,34 @@ public void start(RubyFiber fiber, Thread javaThread) {
249
284
250
285
runningFibers .add (fiber );
251
286
252
- if (threadManager .isRubyManagedThread (javaThread )) {
287
+ if (threadManager .isRubyManagedThread (javaThread ) && Thread . currentThread () == javaThread && entered ) {
253
288
context .getSafepointManager ().enterThread ();
254
289
}
255
290
256
291
// fully initialized
257
292
fiber .initializedLatch .countDown ();
258
293
}
259
294
260
- public void cleanup (RubyFiber fiber , Thread javaThread ) {
295
+ public void cleanup (RubyFiber fiber , Thread javaThread , boolean entered ) {
296
+ assert entered == context .getEnv ().getContext ().isEntered ();
297
+ final ThreadManager threadManager = context .getThreadManager ();
298
+
261
299
fiber .alive = false ;
262
300
263
- if (context . getThreadManager (). isRubyManagedThread ( javaThread ) ) {
301
+ if (threadManager . isRubyManagedThread ( javaThread ) && Thread . currentThread () == javaThread && entered ) {
264
302
context .getSafepointManager ().leaveThread ();
265
303
}
266
304
267
- context . getThreadManager () .cleanupValuesForJavaThread (javaThread );
305
+ threadManager .cleanupValuesForJavaThread (javaThread );
268
306
269
307
runningFibers .remove (fiber );
270
308
271
309
fiber .thread = null ;
272
310
273
311
if (Thread .currentThread () == javaThread ) {
274
- context . getThreadManager () .rubyFiber .remove ();
312
+ threadManager .rubyFiber .remove ();
275
313
}
276
- context . getThreadManager () .rubyFiberForeignMap .remove (javaThread );
314
+ threadManager .rubyFiberForeignMap .remove (javaThread );
277
315
278
316
fiber .finishedLatch .countDown ();
279
317
}
@@ -283,13 +321,32 @@ public void killOtherFibers() {
283
321
// All Fibers except the current one are in waitForResume(),
284
322
// so sending a FiberShutdownMessage is enough to finish them.
285
323
// This also avoids the performance cost of a safepoint.
324
+
325
+ // This method might not be executed on the rootFiber Java Thread but possibly on another Java Thread.
326
+
327
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
328
+ assert truffleContext .isEntered ();
329
+ assert context .getThreadManager ().isRubyManagedThread (Thread .currentThread ());
330
+
331
+ context .getSafepointManager ().leaveThread ();
332
+ try {
333
+ truffleContext .leaveAndEnter (null , () -> {
334
+ doKillOtherFibers ();
335
+ return null ;
336
+ });
337
+ } finally {
338
+ context .getSafepointManager ().enterThread ();
339
+ }
340
+ }
341
+
342
+ private void doKillOtherFibers () {
286
343
for (RubyFiber fiber : runningFibers ) {
287
344
if (fiber != rootFiber ) {
288
345
addToMessageQueue (fiber , new FiberShutdownMessage ());
289
346
290
347
// Wait for the Fiber to finish so we only run one Fiber at a time
291
348
final CountDownLatch finishedLatch = fiber .finishedLatch ;
292
- context . getThreadManager (). runUntilResultKeepStatus ( null , () -> {
349
+ ThreadManager . retryWhileInterrupted ( () -> {
293
350
finishedLatch .await ();
294
351
return BlockingAction .SUCCESS ;
295
352
});
@@ -300,7 +357,7 @@ public void killOtherFibers() {
300
357
@ TruffleBoundary
301
358
public void shutdown (Thread javaThread ) {
302
359
killOtherFibers ();
303
- cleanup (rootFiber , javaThread );
360
+ cleanup (rootFiber , javaThread , true );
304
361
}
305
362
306
363
public String getFiberDebugInfo () {
0 commit comments