|
40 | 40 | */ |
41 | 41 | package com.oracle.graal.python.nodes.arrow.capsule; |
42 | 42 |
|
| 43 | +import java.lang.invoke.MethodHandle; |
| 44 | +import java.lang.invoke.MethodHandles; |
| 45 | +import java.lang.invoke.MethodType; |
| 46 | + |
| 47 | +import com.oracle.graal.python.annotations.CApiUpcallTarget; |
43 | 48 | import com.oracle.graal.python.builtins.modules.cext.PythonCextCapsuleBuiltins.PyCapsuleGetPointerNode; |
44 | | -import com.oracle.graal.python.builtins.objects.PNone; |
45 | 49 | import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode; |
46 | | -import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; |
47 | 50 | import com.oracle.graal.python.nodes.arrow.ArrowArray; |
48 | | -import com.oracle.graal.python.nodes.arrow.InvokeArrowReleaseCallbackNode; |
| 51 | +import com.oracle.graal.python.nodes.arrow.ArrowReleaseCallback; |
49 | 52 | import com.oracle.graal.python.runtime.PythonContext; |
50 | | -import com.oracle.truffle.api.CompilerDirectives; |
51 | | -import com.oracle.truffle.api.dsl.Bind; |
52 | | -import com.oracle.truffle.api.dsl.Cached; |
53 | | -import com.oracle.truffle.api.interop.InteropLibrary; |
54 | | -import com.oracle.truffle.api.interop.TruffleObject; |
55 | | -import com.oracle.truffle.api.library.CachedLibrary; |
56 | | -import com.oracle.truffle.api.library.ExportLibrary; |
57 | | -import com.oracle.truffle.api.library.ExportMessage; |
58 | | -import com.oracle.truffle.api.nodes.Node; |
| 53 | +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; |
| 54 | +import com.oracle.truffle.api.CompilerAsserts; |
| 55 | + |
| 56 | +public final class ArrowArrayCapsuleDestructor { |
| 57 | + private static final MethodHandle HANDLE_EXECUTE; |
59 | 58 |
|
60 | | -@ExportLibrary(InteropLibrary.class) |
61 | | -public class ArrowArrayCapsuleDestructor implements TruffleObject { |
| 59 | + static { |
| 60 | + try { |
| 61 | + HANDLE_EXECUTE = MethodHandles.lookup().findStatic(ArrowArrayCapsuleDestructor.class, "execute", |
| 62 | + MethodType.methodType(void.class, long.class)); |
| 63 | + } catch (NoSuchMethodException | IllegalAccessException e) { |
| 64 | + throw new RuntimeException(e); |
| 65 | + } |
| 66 | + } |
62 | 67 |
|
63 | | - @ExportMessage |
64 | | - boolean isExecutable() { |
65 | | - return true; |
| 68 | + private ArrowArrayCapsuleDestructor() { |
66 | 69 | } |
67 | 70 |
|
68 | | - @ExportMessage |
69 | | - Object execute(Object[] args, |
70 | | - @Bind Node inliningTarget, |
71 | | - @CachedLibrary(limit = "1") InteropLibrary lib, |
72 | | - @Cached NativeToPythonNode nativeToPythonNode, |
73 | | - @Cached PyCapsuleGetPointerNode capsuleGetPointerNode, |
74 | | - @Cached InvokeArrowReleaseCallbackNode.Lazy invokeReleaseCallbackNode) { |
75 | | - if (args.length != 1 || !lib.isPointer(args[0])) { |
76 | | - throw CompilerDirectives.shouldNotReachHere(); |
77 | | - } |
| 71 | + public static MethodHandle getMethodHandle() { |
| 72 | + return HANDLE_EXECUTE; |
| 73 | + } |
78 | 74 |
|
79 | | - Object capsule = nativeToPythonNode.execute(args[0]); |
80 | | - PythonContext ctx = PythonContext.get(inliningTarget); |
| 75 | + @CApiUpcallTarget |
| 76 | + private static void execute(long capsulePointer) { |
| 77 | + CompilerAsserts.neverPartOfCompilation(); |
| 78 | + PythonContext ctx = PythonContext.get(null); |
81 | 79 | ctx.ensureNativeAccess(); |
82 | | - long capsuleNamePointer = ctx.stringToNativeUtf8Bytes(ArrowArray.CAPSULE_NAME, true); |
83 | | - var arrowArray = ArrowArray.wrap(capsuleGetPointerNode.execute(inliningTarget, capsule, capsuleNamePointer)); |
84 | | - /* |
85 | | - * The exported PyCapsules should have a destructor that calls the release callback of the |
86 | | - * Arrow struct, if it is not already null. This prevents a memory leak in case the capsule |
87 | | - * was never passed to another consumer. |
88 | | - * |
89 | | - * For more information see: |
90 | | - * https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html#lifetime- |
91 | | - * semantics |
92 | | - */ |
93 | | - if (!arrowArray.isReleased()) { |
94 | | - invokeReleaseCallbackNode.get(inliningTarget).executeCached(arrowArray.releaseCallback(), arrowArray.memoryAddress()); |
| 80 | + Object capsule = NativeToPythonNode.executeRawUncached(capsulePointer); |
| 81 | + long capsuleNamePointer = ctx.stringToNativeUtf8Bytes(ArrowArray.CAPSULE_NAME, false); |
| 82 | + try { |
| 83 | + var arrowArray = ArrowArray.wrap(PyCapsuleGetPointerNode.executeUncached(capsule, capsuleNamePointer)); |
| 84 | + /* |
| 85 | + * The exported PyCapsules should have a destructor that calls the release callback of |
| 86 | + * the Arrow struct, if it is not already null. This prevents a memory leak in case the |
| 87 | + * capsule was never passed to another consumer. |
| 88 | + * |
| 89 | + * For more information see: |
| 90 | + * https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html#lifetime- |
| 91 | + * semantics |
| 92 | + */ |
| 93 | + if (!arrowArray.isReleased()) { |
| 94 | + ArrowReleaseCallback.execute(arrowArray.releaseCallback(), arrowArray.memoryAddress()); |
| 95 | + } |
| 96 | + NativeMemory.free(arrowArray.memoryAddress()); |
| 97 | + } finally { |
| 98 | + NativeMemory.free(capsuleNamePointer); |
95 | 99 | } |
96 | | - NativeMemory.free(arrowArray.memoryAddress()); |
97 | | - return PNone.NO_VALUE; |
98 | 100 | } |
99 | 101 | } |
0 commit comments