4040 */
4141package com .oracle .graal .python .nodes .frame ;
4242
43+ import static com .oracle .graal .python .util .PythonUtils .tsLiteral ;
44+
4345import java .util .Objects ;
46+ import java .util .concurrent .CancellationException ;
47+ import java .util .concurrent .ExecutionException ;
48+ import java .util .concurrent .Future ;
49+ import java .util .concurrent .TimeUnit ;
50+ import java .util .concurrent .TimeoutException ;
4451
52+ import com .oracle .graal .python .PythonLanguage ;
53+ import com .oracle .graal .python .builtins .PythonBuiltinClassType ;
4554import com .oracle .graal .python .builtins .objects .frame .PFrame ;
4655import com .oracle .graal .python .builtins .objects .function .PArguments ;
4756import com .oracle .graal .python .builtins .objects .generator .PGenerator ;
57+ import com .oracle .graal .python .nodes .PRaiseNode ;
4858import com .oracle .graal .python .nodes .PRootNode ;
4959import com .oracle .graal .python .nodes .bytecode .PBytecodeGeneratorRootNode ;
5060import com .oracle .graal .python .nodes .bytecode .PBytecodeRootNode ;
5161import com .oracle .graal .python .nodes .bytecode_dsl .PBytecodeDSLRootNode ;
5262import com .oracle .graal .python .runtime .CallerFlags ;
63+ import com .oracle .graal .python .runtime .GilNode ;
5364import com .oracle .graal .python .runtime .IndirectCallData ;
5465import com .oracle .graal .python .runtime .IndirectCallData .BoundaryCallData ;
5566import com .oracle .graal .python .runtime .PythonContext ;
5667import com .oracle .graal .python .runtime .PythonOptions ;
5768import com .oracle .truffle .api .CompilerDirectives ;
5869import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
5970import com .oracle .truffle .api .RootCallTarget ;
71+ import com .oracle .truffle .api .ThreadLocalAction ;
6072import com .oracle .truffle .api .Truffle ;
73+ import com .oracle .truffle .api .TruffleSafepoint ;
6174import com .oracle .truffle .api .bytecode .ContinuationRootNode ;
6275import com .oracle .truffle .api .dsl .Bind ;
6376import com .oracle .truffle .api .dsl .Cached ;
@@ -144,7 +157,7 @@ public final PFrame refreshFrame(VirtualFrame frame, PFrame.Reference reference,
144157 }
145158
146159 public final PFrame ensureFresh (VirtualFrame frame , PFrame pFrame , int callerFlags ) {
147- if (pFrame .isStale (frame , callerFlags )) {
160+ if (pFrame .needsRefresh (frame , callerFlags )) {
148161 return refreshFrame (frame , pFrame .getRef (), callerFlags );
149162 }
150163 return pFrame ;
@@ -166,9 +179,11 @@ PFrame read(VirtualFrame frame, PFrame.Reference startFrameInfo, FrameInstance.F
166179 @ Cached MaterializeFrameNode materializeFrameNode ,
167180 @ Cached InlinedBranchProfile stackWalkProfile1 ,
168181 @ Cached InlinedBranchProfile stackWalkProfile2 ) {
182+ PFrame .Reference executingFrameInfo = frame != null ? PArguments .getCurrentFrameInfo (frame ) : null ;
169183 if (startFrameInfo == null ) {
170184 PythonContext context = PythonContext .get (this );
171185 startFrameInfo = context .peekTopFrameInfo (context .getLanguage (this ));
186+ executingFrameInfo = startFrameInfo ;
172187 }
173188 int i = 0 ;
174189 PFrame .Reference curFrameInfo = startFrameInfo ;
@@ -182,10 +197,10 @@ PFrame read(VirtualFrame frame, PFrame.Reference startFrameInfo, FrameInstance.F
182197 if (i == level ) {
183198 // We found the right reference
184199 // Maybe it's for the frame we're in?
185- if (frame != null && PArguments . getCurrentFrameInfo ( frame ) == curFrameInfo ) {
200+ if (curFrameInfo == executingFrameInfo ) {
186201 return materializeFrameNode .execute (this , false , CallerFlags .needsLocals (callerFlags ), frame );
187202 }
188- if (curFrameInfo .getPyFrame () != null && !curFrameInfo .getPyFrame ().isStale (null , callerFlags )) {
203+ if (curFrameInfo .getPyFrame () != null && !curFrameInfo .getPyFrame ().needsRefresh (null , callerFlags )) {
189204 return curFrameInfo .getPyFrame ();
190205 }
191206 // We don't have the frame for the reference, fall back to the stack walk
@@ -215,7 +230,7 @@ PFrame read(VirtualFrame frame, PFrame.Reference startFrameInfo, FrameInstance.F
215230 * It is necessary to continue from where we stopped with the backref walk because the
216231 * original starting frame might not be on stack anymore
217232 */
218- return readFromStackWalk (curFrameInfo , frameAccess , selector , level - i , callerFlags , materializeFrameNode );
233+ return readSlowPath (curFrameInfo , frameAccess , selector , level - i , callerFlags , materializeFrameNode );
219234 }
220235
221236 private static PFrame .Reference getBackref (PFrame .Reference reference ) {
@@ -226,9 +241,49 @@ private static PFrame.Reference getBackref(PFrame.Reference reference) {
226241 }
227242
228243 @ TruffleBoundary
229- private PFrame readFromStackWalk (PFrame .Reference startFrameInfo , FrameInstance .FrameAccess frameAccess , FrameSelector selector , int level , int callerFlags ,
244+ @ SuppressWarnings ("try" )
245+ private PFrame readSlowPath (PFrame .Reference startFrameInfo , FrameInstance .FrameAccess frameAccess , FrameSelector selector , int level , int callerFlags ,
230246 MaterializeFrameNode materializeFrameNode ) {
247+ if (level == 0 && startFrameInfo != null && startFrameInfo .getPyFrame () != null && !selector .skip (startFrameInfo .getRootNode ()) && startFrameInfo .getPyFrame ().getThread () != null &&
248+ startFrameInfo .getPyFrame ().getThread () != Thread .currentThread ()) {
249+ // We have the frame we're looking for, but it's on another thread
250+ Thread thread = startFrameInfo .getPyFrame ().getThread ();
251+ if (thread .isAlive ()) {
252+ try (var gil = GilNode .uncachedRelease ()) {
253+ // Schedule a safepoint action on that thread
254+ Future <Void > future = PythonContext .get (null ).getEnv ().submitThreadLocal (new Thread []{thread }, new ThreadLocalAction (true , false ) {
255+ @ Override
256+ protected void perform (Access access ) {
257+ Node location = access .getLocation ();
258+ if (location instanceof PBytecodeDSLRootNode ) {
259+ // See AsyncPythonAction#execute for explanation
260+ location = PythonLanguage .get (null ).unavailableSafepointLocation ;
261+ }
262+ StackWalkResult result = ReadFrameNode .getFrame (location , startFrameInfo , frameAccess , selector , 0 , callerFlags );
263+ processStackWalkResult (materializeFrameNode , callerFlags , result );
264+ }
265+ });
266+ TruffleSafepoint .setBlockedThreadInterruptible (this , voidFuture -> {
267+ try {
268+ voidFuture .get (10 , TimeUnit .SECONDS );
269+ } catch (TimeoutException e ) {
270+ throw PRaiseNode .raiseStatic (this , PythonBuiltinClassType .RuntimeError , tsLiteral ("Failed to interrupt thread " + Thread .currentThread () + " within 10 seconds" ));
271+ } catch (CancellationException e ) {
272+ // Ignore
273+ } catch (ExecutionException e ) {
274+ throw new RuntimeException (e );
275+ }
276+ }, future );
277+ }
278+ }
279+ assert !startFrameInfo .getPyFrame ().outdatedCallerFlags (callerFlags );
280+ return startFrameInfo .getPyFrame ();
281+ }
231282 StackWalkResult callerFrameResult = getFrame (this , startFrameInfo , frameAccess , selector , level , callerFlags );
283+ return processStackWalkResult (materializeFrameNode , callerFlags , callerFrameResult );
284+ }
285+
286+ private static PFrame processStackWalkResult (MaterializeFrameNode materializeFrameNode , int callerFlags , StackWalkResult callerFrameResult ) {
232287 if (callerFrameResult != null ) {
233288 Node location = callerFrameResult .callNode ;
234289 if (!(callerFrameResult .rootNode instanceof PBytecodeDSLRootNode ) && location == null ) {
0 commit comments