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 ;
@@ -101,10 +102,15 @@ public RubyFiber createFiber(RubyLanguage language, RubyContext context, RubyThr
101
102
}
102
103
103
104
public void initialize (RubyFiber fiber , RubyProc block , Node currentNode ) {
105
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
106
+
104
107
ThreadManager .FIBER_BEING_SPAWNED .set (fiber );
105
108
try {
106
- context .getThreadManager ().spawnFiber (() -> fiberMain (context , fiber , block , currentNode ));
107
- waitForInitialization (context , fiber , currentNode );
109
+ context .getThreadManager ().leaveAndEnter (truffleContext , currentNode , () -> {
110
+ context .getThreadManager ().spawnFiber (fiber , () -> fiberMain (context , fiber , block , currentNode ));
111
+ waitForInitialization (context , fiber , currentNode );
112
+ return BlockingAction .SUCCESS ;
113
+ }, context .getThreadManager ().isRubyManagedThread (Thread .currentThread ()));
108
114
} finally {
109
115
ThreadManager .FIBER_BEING_SPAWNED .remove ();
110
116
}
@@ -114,18 +120,24 @@ public void initialize(RubyFiber fiber, RubyProc block, Node currentNode) {
114
120
public static void waitForInitialization (RubyContext context , RubyFiber fiber , Node currentNode ) {
115
121
final CountDownLatch initializedLatch = fiber .initializedLatch ;
116
122
117
- context . getThreadManager (). runUntilResultKeepStatus ( currentNode , () -> {
123
+ final BlockingAction < Boolean > blockingAction = () -> {
118
124
initializedLatch .await ();
119
125
return BlockingAction .SUCCESS ;
120
- });
126
+ };
127
+
128
+ if (context .getEnv ().getContext ().isEntered ()) {
129
+ context .getThreadManager ().runUntilResultKeepStatus (currentNode , blockingAction );
130
+ } else {
131
+ context .getThreadManager ().retryWhileInterrupted (blockingAction );
132
+ }
121
133
122
134
final Throwable uncaughtException = fiber .uncaughtException ;
123
135
if (uncaughtException != null ) {
124
136
ExceptionOperations .rethrow (uncaughtException );
125
137
}
126
138
}
127
139
128
- private static final BranchProfile UNPROFILED = BranchProfile .create ();
140
+ private static final BranchProfile UNPROFILED = BranchProfile .getUncached ();
129
141
130
142
private void fiberMain (RubyContext context , RubyFiber fiber , RubyProc block , Node currentNode ) {
131
143
assert fiber != rootFiber : "Root Fibers execute threadMain() and not fiberMain()" ;
@@ -135,16 +147,30 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
135
147
final String oldName = thread .getName ();
136
148
thread .setName (NAME_PREFIX + " id=" + thread .getId () + " from " + RubyContext .fileLine (sourceSection ));
137
149
138
- start (fiber , thread );
139
- try {
150
+ start (fiber , thread , false );
151
+
152
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
153
+ assert !truffleContext .isEntered ();
140
154
141
- final Object [] args = waitForResume (fiber );
155
+ final Object prev = truffleContext .enter (currentNode ); // enter and leave now to workaround GR-29773
156
+ context .getSafepointManager ().enterThread (); // not done in start() above because the context was not entered
157
+ final FiberMessage message = context .getThreadManager ().leaveAndEnter (truffleContext , currentNode , () -> {
158
+ // fully initialized
159
+ fiber .initializedLatch .countDown ();
160
+ return waitMessage (fiber );
161
+ }, true );
162
+
163
+ try {
142
164
final Object result ;
143
165
try {
166
+ final Object [] args = handleMessage (fiber , message );
144
167
result = ProcOperations .rootCall (block , args );
145
168
} finally {
146
169
// Make sure that other fibers notice we are dead before they gain control back
147
170
fiber .alive = false ;
171
+ // Leave before resume/sendExceptionToParentFiber -> addToMessageQueue() -> parent Fiber starts executing
172
+ context .getSafepointManager ().leaveThread ();
173
+ truffleContext .leave (currentNode , prev );
148
174
}
149
175
resume (fiber , getReturnFiber (fiber , currentNode , UNPROFILED ), FiberOperation .YIELD , result );
150
176
@@ -164,8 +190,11 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
164
190
fiber ,
165
191
new RaiseException (context , context .getCoreExceptions ().unexpectedReturn (currentNode )),
166
192
currentNode );
193
+ } catch (Throwable e ) {
194
+ final RuntimeException exception = ThreadManager .printInternalError (e );
195
+ sendExceptionToParentFiber (fiber , exception , currentNode );
167
196
} finally {
168
- cleanup (fiber , thread );
197
+ cleanup (fiber , thread , false );
169
198
thread .setName (oldName );
170
199
}
171
200
}
@@ -193,16 +222,19 @@ public RubyFiber getReturnFiber(RubyFiber currentFiber, Node currentNode, Branch
193
222
194
223
@ TruffleBoundary
195
224
private void addToMessageQueue (RubyFiber fiber , FiberMessage message ) {
225
+ assert !context .getEnv ().getContext ().isEntered () : "should have left context when sending message to fiber" ;
196
226
fiber .messageQueue .add (message );
197
227
}
198
228
199
- /** Send the Java thread that represents this fiber to sleep until it receives a resume or exit message. */
229
+ /** Send the Java thread that represents this fiber to sleep until it receives a message. */
200
230
@ TruffleBoundary
201
- private Object [] waitForResume (RubyFiber fiber ) {
202
- final FiberMessage message = context .getThreadManager ().runUntilResultKeepStatus (
203
- null ,
204
- () -> fiber . messageQueue . take ());
231
+ private FiberMessage waitMessage (RubyFiber fiber ) {
232
+ assert ! context .getEnv ().getContext (). isEntered () : "should have left context while waiting fiber message" ;
233
+ return context . getThreadManager (). retryWhileInterrupted ( fiber . messageQueue :: take );
234
+ }
205
235
236
+ @ TruffleBoundary
237
+ private Object [] handleMessage (RubyFiber fiber , FiberMessage message ) {
206
238
setCurrentFiber (fiber );
207
239
208
240
if (message instanceof FiberShutdownMessage ) {
@@ -227,12 +259,21 @@ private void resume(RubyFiber fromFiber, RubyFiber fiber, FiberOperation operati
227
259
addToMessageQueue (fiber , new FiberResumeMessage (operation , fromFiber , args ));
228
260
}
229
261
262
+ @ TruffleBoundary
230
263
public Object [] transferControlTo (RubyFiber fromFiber , RubyFiber fiber , FiberOperation operation , Object [] args ) {
231
- resume (fromFiber , fiber , operation , args );
232
- return waitForResume (fromFiber );
264
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
265
+ final boolean isRubyManagedThread = context .getThreadManager ().isRubyManagedThread (Thread .currentThread ());
266
+
267
+ final FiberMessage message = context .getThreadManager ().leaveAndEnter (truffleContext , null , () -> {
268
+ resume (fromFiber , fiber , operation , args );
269
+ return waitMessage (fromFiber );
270
+ }, isRubyManagedThread );
271
+
272
+ return handleMessage (fromFiber , message );
233
273
}
234
274
235
- public void start (RubyFiber fiber , Thread javaThread ) {
275
+ public void start (RubyFiber fiber , Thread javaThread , boolean entered ) {
276
+ assert entered == context .getEnv ().getContext ().isEntered ();
236
277
final ThreadManager threadManager = context .getThreadManager ();
237
278
238
279
if (Thread .currentThread () == javaThread ) {
@@ -249,31 +290,31 @@ public void start(RubyFiber fiber, Thread javaThread) {
249
290
250
291
runningFibers .add (fiber );
251
292
252
- if (threadManager .isRubyManagedThread (javaThread )) {
293
+ if (threadManager .isRubyManagedThread (javaThread ) && Thread . currentThread () == javaThread && entered ) {
253
294
context .getSafepointManager ().enterThread ();
254
295
}
255
-
256
- // fully initialized
257
- fiber .initializedLatch .countDown ();
258
296
}
259
297
260
- public void cleanup (RubyFiber fiber , Thread javaThread ) {
298
+ public void cleanup (RubyFiber fiber , Thread javaThread , boolean entered ) {
299
+ assert entered == context .getEnv ().getContext ().isEntered ();
300
+ final ThreadManager threadManager = context .getThreadManager ();
301
+
261
302
fiber .alive = false ;
262
303
263
- if (context . getThreadManager (). isRubyManagedThread ( javaThread ) ) {
304
+ if (threadManager . isRubyManagedThread ( javaThread ) && Thread . currentThread () == javaThread && entered ) {
264
305
context .getSafepointManager ().leaveThread ();
265
306
}
266
307
267
- context . getThreadManager () .cleanupValuesForJavaThread (javaThread );
308
+ threadManager .cleanupValuesForJavaThread (javaThread );
268
309
269
310
runningFibers .remove (fiber );
270
311
271
312
fiber .thread = null ;
272
313
273
314
if (Thread .currentThread () == javaThread ) {
274
- context . getThreadManager () .rubyFiber .remove ();
315
+ threadManager .rubyFiber .remove ();
275
316
}
276
- context . getThreadManager () .rubyFiberForeignMap .remove (javaThread );
317
+ threadManager .rubyFiberForeignMap .remove (javaThread );
277
318
278
319
fiber .finishedLatch .countDown ();
279
320
}
@@ -283,24 +324,40 @@ public void killOtherFibers() {
283
324
// All Fibers except the current one are in waitForResume(),
284
325
// so sending a FiberShutdownMessage is enough to finish them.
285
326
// This also avoids the performance cost of a safepoint.
327
+
328
+ // This method might not be executed on the rootFiber Java Thread but possibly on another Java Thread.
329
+
330
+ final TruffleContext truffleContext = context .getEnv ().getContext ();
331
+ context .getThreadManager ().leaveAndEnter (truffleContext , null , () -> {
332
+ doKillOtherFibers ();
333
+ return BlockingAction .SUCCESS ;
334
+ }, true );
335
+ }
336
+
337
+ private void doKillOtherFibers () {
286
338
for (RubyFiber fiber : runningFibers ) {
287
339
if (fiber != rootFiber ) {
288
340
addToMessageQueue (fiber , new FiberShutdownMessage ());
289
341
290
342
// Wait for the Fiber to finish so we only run one Fiber at a time
291
343
final CountDownLatch finishedLatch = fiber .finishedLatch ;
292
- context .getThreadManager ().runUntilResultKeepStatus ( null , () -> {
344
+ context .getThreadManager ().retryWhileInterrupted ( () -> {
293
345
finishedLatch .await ();
294
346
return BlockingAction .SUCCESS ;
295
347
});
348
+
349
+ final Throwable uncaughtException = fiber .uncaughtException ;
350
+ if (uncaughtException != null ) {
351
+ ExceptionOperations .rethrow (uncaughtException );
352
+ }
296
353
}
297
354
}
298
355
}
299
356
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