8000 Implement _PyWeakref_ClearRef · oracle/graalpython@5bfa841 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5bfa841

Browse files
committed
Implement _PyWeakref_ClearRef
1 parent 56ea39a commit 5bfa841

File tree

5 files changed

+92
-11
lines changed

5 files changed

+92
-11
lines changed

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

Lines changed: 48 additions & 2 deletions
8000
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2025, 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
@@ -37,10 +37,12 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40+
import gc
41+
import time
4042
import unittest
4143
import weakref
4244

43-
from . import CPyExtType, assert_raises
45+
from . import CPyExtFunction, CPyExtType, assert_raises
4446

4547
TestWeakRefHelper = CPyExtType(
4648
'TestWeakRefHelper',
@@ -95,3 +97,47 @@ class Bar(clazz):
9597
x = Bar()
9698
y = weakref.ref(x)
9799
assert type(y) == weakref.ReferenceType
100+
101+
def clear_ref_arguments():
102+
class Foo():
103+
pass
104+
f = Foo()
105+
log = []
106+
wr = weakref.ref(f, lambda wr: log.append("cb called"))
107+
return ((f, wr, log),)
108+
109+
def clear_ref_equivalent(args):
110+
# Ignore args. There is no clearref in python, so
111+
# the equivalent is a weakref that never had a callback
112+
# in the first place
113+
class Foo():
114+
pass
115+
f = Foo()
116+
return weakref.ref(f), []
117+
118+
def compare_cleared_weakref(cresult, presult):
119+
cwr, clog = cresult
120+
pwr, plog = presult
121+
for i in range(3):
122+
gc.collect()
123+
time.sleep(1)
124+
assert cwr() is None
125+
assert pwr() is None
126+
assert not clog
127+
assert not plog
128+
return True
129+
130+
test_PyWeakref_ClearRef = CPyExtFunction(
131+
clear_ref_equivalent,
132+
clear_ref_arguments,
133+
code="""
134+
PyObject* wrap_PyWeakref_ClearRef(PyObject *o, PyObject* weakref, PyObject* log) {
135+
_PyWeakref_ClearRef((PyWeakReference *)weakref);
136+
return Py_BuildValue("OO", weakref, log);
137+
}
138+
""",
139+
argspec='OOO',
140+
arguments=["PyObject* o", "PyObject* weakref", "PyObject* log"],
141+
callfunction="wrap_PyWeakref_ClearRef",
142+
cmpfunc=compare_cleared_weakref,
143+
)

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
4545
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectBorrowed;
4646
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
47+
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PYWEAKREFERENCE_PTR;
4748
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void;
4849

4950
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
@@ -89,4 +90,15 @@ static Object call(Object reference) {
8990
return PNone.NONE;
9091
}
9192
}
93+
94+
@CApiBuiltin(name = "_PyWeakref_ClearRef", ret = Void, args = {PYWEAKREFERENCE_PTR}, call = Direct)
95+
abstract static class PyWeakref_ClearRef extends CApiUnaryBuiltinNode {
96+
@Specialization
97+
static Object call(Object reference) {
98+
if (reference instanceof PReferenceType ref) {
99+
ref.clearRef();
100+
}
101+
return PNone.NONE;
102+
}
103+
}
92104
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1214,7 +1214,6 @@ public final class CApiFunction {
12141214
@CApiBuiltin(name = "_PyUnicode_WideCharString_Converter", ret = Int, args = {PyObject, Pointer}, call = NotImplemented)
12151215
@CApiBuiltin(name = "_PyUnicode_WideCharString_Opt_Converter", ret = Int, args = {PyObject, Pointer}, call = NotImplemented)
12161216
@CApiBuiltin(name = "_PyUnicode_XStrip", ret = PyObject, args = {PyObject, Int, PyObject}, call = NotImplemented)
1217-
@CApiBuiltin(name = "_PyWeakref_ClearRef", ret = Void, args = {PYWEAKREFERENCE_PTR}, call = NotImplemented)
12181217
@CApiBuiltin(name = "_PyWeakref_GetWeakrefCount", ret = Py_ssize_t, args = {PYWEAKREFERENCE_PTR}, call = NotImplemented)
12191218
@CApiBuiltin(name = "_Py_CoerceLegacyLocale", ret = Int, args = {Int}, call = NotImplemented)
12201219
@CApiBuiltin(name = "_Py_DisplaySourceLine", ret = Int, args = {PyObject, PyObject, Int, Int, INT_LIST, PyObjectPtr}, call = NotImplemented)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ public enum ArgDescriptor {
279279
PYPRECONFIG_PTR("PyPreConfig*"),
280280
PYSTATUS("PyStatus"),
281281
PYUNICODE_KIND("enum PyUnicode_Kind"),
282-
PYWEAKREFERENCE_PTR("PyWeakReference*"),
282+
PYWEAKREFERENCE_PTR(ArgBehavior.PyObject, "PyWeakReference*"),
283283
PYWIDESTRINGLIST_PTR("PyWideStringList*"),
284284
SIZE_T(ArgBehavior.Int64, "size_t"),
285285
SIZE_T_PTR("size_t*"),

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/PReferenceType.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -51,7 +51,7 @@
5151

5252
public class PReferenceType extends PythonBuiltinObject {
5353
public static class WeakRefStorage extends WeakReference<Object> {
54-
private final Object callback;
54+
private Object callback;
5555
private final PReferenceType ref;
5656
private final long pointer;
5757

@@ -72,15 +72,15 @@ public PReferenceType getRef() {
7272

7373
/**
7474
* get the native pointer of the referent.
75-
*
75+
*
7676
* @return the native pointer if referent is a native reference, otherwise, 0.
7777
*/
7878
public long getPointer() {
7979
return pointer;
8080
}
8181
}
8282

83-
private final WeakRefStorage store;
83+
private WeakRefStorage store;
8484
private long hash = -1;
8585

8686
@TruffleBoundary
@@ -89,16 +89,40 @@ public PReferenceType(Object cls, Shape instanceShape, Object pythonObject, Obje
8989
this.store = new WeakRefStorage(this, pythonObject, callback, queue);
9090
}
9191

92+
/**
93+
* In CPython, this functions clears the reference without calling the callback. This is not
94+
* exactly what we can do here since the WeakRefStorage is already enqueued. We clear the
95+
* callback so that when the Java WeakReference is enqueued because the Object was collected,
96+
* there is no callback to run anymore, and we also drop the store reference here entirely so
97+
* that the object is not longer reachable through this weakref.
98+
*/
99+
public void clearRef() {
100+
WeakRefStorage s = this.store;
101+
if (s != null) {
102+
s.callback = null;
103+
this.store = null;
104+
}
105+
}
106+
92107
public Object getCallback() {
93-
if (this.store.callback == null) {
108+
Object callback = null;
109+
WeakRefStorage s = this.store;
110+
if (s != null) {
111+
callback = s.callback;
112+
}
113+
if (callback == null) {
94114
return PNone.NONE;
95115
}
96-
return this.store.callback;
116+
return callback;
97117
}
98118

99119
@TruffleBoundary
100120
public Object getObject() {
101-
return this.store.get();
121+
WeakRefStorage s = this.store;
122+
if (s != null) {
123+
return s.get();
124+
}
125+
return null;
102126
}
103127

104128
public Object getPyObject() {

0 commit comments

Comments
 (0)
0