108108import com .oracle .truffle .api .TruffleLogger ;
109109import com .oracle .truffle .api .frame .VirtualFrame ;
110110import com .oracle .truffle .api .interop .ArityException ;
111- import com .oracle .truffle .api .interop .InteropException ;
112111import com .oracle .truffle .api .interop .InteropLibrary ;
113112import com .oracle .truffle .api .interop .TruffleObject ;
114113import com .oracle .truffle .api .interop .UnknownIdentifierException ;
125124import com .oracle .truffle .api .strings .TruffleString ;
126125import com .oracle .truffle .nfi .api .SignatureLibrary ;
127126
127+ import sun .misc .Unsafe ;
128+
128129public final class CApiContext extends CExtContext {
129130
130131 public static final String LOGGER_CAPI_NAME = "capi" ;
@@ -593,10 +594,11 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
593594 * is terminated by a signal, the context exit is skipped. For that case we set
594595 * up the shutdown hook.
595596 */
596- Object finalizeFunction = U .readMember (capiLibrary , "finalizeCAPI" );
597- Object finalizeSignature = env .parseInternal (Source .newBuilder (J_NFI_LANGUAGE , "():VOID" , "exec" ).build ()).call ();
597+ Object finalizeFunction = U .readMember (capiLibrary , "GraalPy_get_finalize_capi_pointer_array" );
598+ Object finalizeSignature = env .parseInternal (Source .newBuilder (J_NFI_LANGUAGE , "():POINTER" , "exec" ).build ()).call ();
599+ Object resetFunctionPointerArray = SignatureLibrary .getUncached ().call (finalizeSignature , finalizeFunction );
598600 try {
599- cApiContext .addNativeFinalizer (env , finalizeFunction , finalizeSignature );
601+ cApiContext .addNativeFinalizer (env , resetFunctionPointerArray );
600602 } catch (RuntimeException e ) {
601603 // This can happen when other languages restrict multithreading
602604 LOGGER .warning (() -> "didn't register a native finalizer due to: " + e .getMessage ());
@@ -622,16 +624,40 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
622624 return context .getCApiContext ();
623625 }
624626
625- private void addNativeFinalizer (Env env , Object finalizeFunction , Object finalizeSignature ) {
626- nativeFinalizerRunnable = () -> {
627+ /**
628+ * Registers a VM shutdown hook, that resets certain C API function to NOP functions to avoid
629+ * segfaults due to improper Python context shutdown. In particular, the shutdown hook reads
630+ * pairs of {@code variable address} and {@code reset value} provided in a native array and
631+ * writes the {@code reset value} to the {@code variable address}. Currently, this is used to
632+ * change the incref and decref upcall functions which would crash if they are called after the
633+ * Python context was closed. The format of the array is:
634+ *
635+ * <pre>
636+ * [ var_addr, reset_val, var_addr1, reset_val1, ..., NULL ]
637+ * </pre>
638+ */
639+ private void addNativeFinalizer (Env env , Object resetFunctionPointerArray ) {
640+ final Unsafe unsafe = getContext ().getUnsafe ();
641+ InteropLibrary lib = InteropLibrary .getUncached (resetFunctionPointerArray );
642+ if (!lib .isNull (resetFunctionPointerArray ) && lib .isPointer (resetFunctionPointerArray )) {
627643 try {
628- SignatureLibrary .getUncached ().call (finalizeSignature , finalizeFunction );
629- } catch (InteropException e ) {
630- throw CompilerDirectives .shouldNotReachHere (e );
644+ long lresetFunctionPointerArray = lib .asPointer (resetFunctionPointerArray );
645+ nativeFinalizerRunnable = () -> {
646+ long resetFunctionPointerLocation ;
647+ long curElemAddr = lresetFunctionPointerArray ;
648+ while ((resetFunctionPointerLocation = unsafe .getLong (curElemAddr )) != 0 ) {
649+ curElemAddr += Long .BYTES ;
650+ long replacingFunctionPointer = unsafe .getLong (curElemAddr );
651+ unsafe .putAddress (resetFunctionPointerLocation , replacingFunctionPointer );
652+ curElemAddr += Long .BYTES ;
653+ }
654+ };
655+ nativeFinalizerShutdownHook = env .newTruffleThreadBuilder (nativeFinalizerRunnable ).build ();
656+ Runtime .getRuntime ().addShutdownHook (nativeFinalizerShutdownHook );
657+ } catch (UnsupportedMessageException e ) {
658+ throw new RuntimeException (e );
631659 }
632- };
633- nativeFinalizerShutdownHook = env .newTruffleThreadBuilder (nativeFinalizerRunnable ).build ();
634- Runtime .getRuntime ().addShutdownHook (nativeFinalizerShutdownHook );
660+ }
635661 }
636662
637663 @ TruffleBoundary
0 commit comments