Skip to content

Commit 98a6ed7

Browse files
committed
[GR-38286] Avoid native pointer sharing for PrimitiveNativeWrappers.
PullRequest: graalpython/2234
2 parents b449c80 + 456b083 commit 98a6ed7

File tree

4 files changed

+100
-10
lines changed

4 files changed

+100
-10
lines changed

graalpython/com.oracle.graal.python.cext/src/capi.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,11 @@ int PyTruffle_Debug(void *arg) {
876876
return 0;
877877
}
878878

879+
int PyTruffle_ToNative(void *arg) {
880+
polyglot_invoke(PY_TRUFFLE_CEXT, "PyTruffle_ToNative", arg);
881+
return 0;
882+
}
883+
879884
int truffle_ptr_compare(void* x, void* y, int op) {
880885
switch (op) {
881886
case Py_LT:

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_misc.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -228,6 +228,53 @@ def compile_module(self, name):
228228
cmpfunc=unhandled_error_compare
229229
)
230230

231+
# Tests if wrapped Java primitive values do not share the same
232+
# native pointer.
233+
test_primitive_sharing = CPyExtFunction(
234+
lambda args: True,
235+
lambda: (
236+
(123.0, ),
237+
),
238+
code="""
239+
#ifdef GRAALVM_PYTHON
240+
// internal function defined in 'capi.c'
241+
int PyTruffle_ToNative(void *);
242+
#else
243+
// nothing to do on CPython
244+
static inline int PyTruffle_ToNative(void *arg) {
245+
return 0;
246+
}
247+
#endif
248+
249+
PyObject* primitive_sharing(PyObject* val) {
250+
Py_ssize_t val_refcnt = Py_REFCNT(val);
251+
// assume val's refcnt is X > 0
252+
Py_INCREF(val);
253+
// val's refcnt should now be X+1
254+
255+
double dval = PyFloat_AsDouble(val);
256+
257+
PyTruffle_ToNative(val);
258+
259+
// a fresh object with the same value
260+
PyObject *val1 = PyFloat_FromDouble(dval);
261+
PyTruffle_ToNative(val1);
262+
263+
// now, kill it
264+
Py_DECREF(val1);
265+
266+
// reset val's refcnt to X
267+
Py_DECREF(val);
268+
269+
return val_refcnt == Py_REFCNT(val) ? Py_True : Py_False;
270+
}
271+
""",
272+
resultspec="O",
273+
argspec="O",
274+
arguments=["PyObject* val"],
275+
cmpfunc=unhandled_error_compare
276+
)
277+
231278
test_PyOS_double_to_string = CPyExtFunction(
232279
_reference_format_float,
233280
lambda: (
@@ -256,7 +303,7 @@ def compile_module(self, name):
256303
tuple(),
257304
),
258305
code="""
259-
PyObject* wrap_PyEval_GetBuiltins() {
306+
PyObject* wrap_PyEval_GetBuiltins(void) {
260307
return (PyObject *) Py_TYPE(PyEval_GetBuiltins());
261308
}
262309
""",

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@
212212
import com.oracle.truffle.api.CompilerDirectives;
213213
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
214214
import com.oracle.truffle.api.RootCallTarget;
215+
import com.oracle.truffle.api.TruffleLanguage.Env;
215216
import com.oracle.truffle.api.TruffleLogger;
216217
import com.oracle.truffle.api.dsl.Cached;
217218
import com.oracle.truffle.api.dsl.Cached.Exclusive;
@@ -232,6 +233,7 @@
232233
import com.oracle.truffle.api.interop.UnsupportedMessageException;
233234
import com.oracle.truffle.api.interop.UnsupportedTypeException;
234235
import com.oracle.truffle.api.library.CachedLibrary;
236+
import com.oracle.truffle.api.nodes.LanguageInfo;
235237
import com.oracle.truffle.api.nodes.Node;
236238
import com.oracle.truffle.api.nodes.RootNode;
237239
import com.oracle.truffle.api.object.DynamicObjectLibrary;
@@ -241,6 +243,7 @@
241243
import com.oracle.truffle.api.profiles.LoopConditionProfile;
242244
import com.oracle.truffle.api.profiles.ValueProfile;
243245
import com.oracle.truffle.api.utilities.CyclicAssumption;
246+
import com.oracle.truffle.llvm.api.Toolchain;
244247

245248
@CoreFunctions(defineModule = PythonCextBuiltins.PYTHON_CEXT)
246249
@GenerateNodeFactory
@@ -269,6 +272,15 @@ public void initialize(Python3Core core) {
269272
builtinConstants.put("PyGILState_Release", new PyGILStateRelease());
270273
}
271274

275+
@Override
276+
public void postInitialize(Python3Core core) {
277+
super.postInitialize(core);
278+
if (!core.getContext().getOption(PythonOptions.EnableDebuggingBuiltins)) {
279+
PythonModule mod = core.lookupBuiltinModule(PYTHON_CEXT);
280+
mod.setAttribute("PyTruffle_ToNative", PNone.NO_VALUE);
281+
}
282+
}
283+
272284
@FunctionalInterface
273285
public interface TernaryFunction<T1, T2, T3, R> {
274286
R apply(T1 arg0, T2 arg1, T3 arg2);
@@ -2375,15 +2387,33 @@ Object tssDelete(Object key,
23752387
}
23762388
}
23772389

2390+
// directly called without landing function
23782391
@Builtin(name = "PyTruffle_Debug", takesVarArgs = true)
23792392
@GenerateNodeFactory
23802393
public abstract static class PyTruffleDebugNode extends PythonBuiltinNode {
23812394
@Specialization
23822395
@TruffleBoundary
2383-
public Object doIt(Object[] args,
2396+
static Object doIt(Object[] args,
23842397
@Cached DebugNode debugNode) {
23852398
debugNode.execute(args);
23862399
return PNone.NONE;
23872400
}
23882401
}
2402+
2403+
// directly called without landing function
2404+
@Builtin(name = "PyTruffle_ToNative", minNumOfPositionalArgs = 1)
2405+
@GenerateNodeFactory
2406+
public abstract static class PyTruffleToNativeNode extends PythonUnaryBuiltinNode {
2407+
@Specialization
2408+
@TruffleBoundary
2409+
Object doIt(Object object) {
2410+
Env env = getContext().getEnv();
2411+
LanguageInfo llvmInfo = env.getInternalLanguages().get(PythonLanguage.LLVM_LANGUAGE);
2412+
Toolchain toolchain = env.lookup(llvmInfo, Toolchain.class);
2413+
if ("native".equals(toolchain.getIdentifier())) {
2414+
InteropLibrary.getUncached().toNative(object);
2415+
}
2416+
return PNone.NONE;
2417+
}
2418+
}
23892419
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/DynamicObjectNativeWrapper.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,14 +2137,22 @@ int identityHashCode() {
21372137
@ExportMessage
21382138
TriState isIdenticalOrUndefined(Object obj) {
21392139
if (obj instanceof PrimitiveNativeWrapper) {
2140-
// This basically emulates singletons for boxed values. However, we need to do
2141-
// so to
2142-
// preserve the invariant that storing an object into a list and getting it out
2143-
// (in
2144-
// the same critical region) returns the same object.
2140+
/*
2141+
* This basically emulates singletons for boxed values. However, we need to do so to
2142+
* preserve the invariant that storing an object into a list and getting it out (in
2143+
* the same critical region) returns the same object.
2144+
*/
21452145
PrimitiveNativeWrapper other = (PrimitiveNativeWrapper) obj;
2146-
return TriState.valueOf(other.state == state && other.value == value &&
2147-
(other.dvalue == dvalue || Double.isNaN(dvalue) && Double.isNaN(other.dvalue)));
2146+
if (other.state == state && other.value == value && (other.dvalue == dvalue || Double.isNaN(dvalue) && Double.isNaN(other.dvalue))) {
2147+
/*
2148+
* n.b.: in the equals, we also require the native pointer to be the same. The
2149+
* reason for this is to avoid native pointer sharing. Handles are shared if the
2150+
* objects are equal but in this case we must not share because otherwise we
2151+
* would mess up the reference counts.
2152+
*/
2153+
return TriState.valueOf(GetNativePointer.getGenericPtr(this) == GetNativePointer.getGenericPtr(other));
2154+
}
2155+
return TriState.FALSE;
21482156
} else {
21492157
return TriState.UNDEFINED;
21502158
}

0 commit comments

Comments
 (0)