@@ -79,7 +79,7 @@ def test_sigterm():
7979
8080@skip_if_sandboxed ("Needs native extension support in sandboxed runs" )
8181@skipIf (not GRAALPY , "GraalPy-only native weakref shutdown test" )
82- def test_native_weakref_shutdown_with_c_retained_object ():
82+ def test_native_weakref_shutdown_skips_c_retained_object ():
8383 module = compile_module_from_string (textwrap .dedent ("""
8484 #include "Python.h"
8585 #include "structmember.h"
@@ -90,23 +90,34 @@ def test_native_weakref_shutdown_with_c_retained_object():
9090 typedef struct {
9191 PyObject_HEAD
9292 PyObject *weakreflist;
93+ int id;
9394 } NativeWeakRefObject;
9495
9596 static PyObject *kept_alive;
97+ static PyTypeObject NativeWeakRefType;
9698
9799 static void write_marker(const char *contents) {
98100 const char *marker_path = getenv("GR50212_DEALLOC_MARKER");
99101 if (marker_path != NULL) {
100- FILE *marker = fopen(marker_path, "w ");
102+ FILE *marker = fopen(marker_path, "a ");
101103 if (marker != NULL) {
102104 fputs(contents, marker);
103105 fclose(marker);
104106 }
105107 }
106108 }
107109
110+ static PyObject *new_native_weakref(int id) {
111+ NativeWeakRefObject *obj = (NativeWeakRefObject *)PyObject_CallNoArgs((PyObject *)&NativeWeakRefType);
112+ if (obj == NULL) {
113+ return NULL;
114+ }
115+ obj->id = id;
116+ return (PyObject *)obj;
117+ }
118+
108119 static void NativeWeakRef_dealloc(NativeWeakRefObject *self) {
109- write_marker("deallocated\\ n");
120+ write_marker(self->id == 1 ? "deallocated:held \\ n" : "deallocated:free \\ n");
110121 if (self->weakreflist != NULL) {
111122 PyObject_ClearWeakRefs((PyObject *)self);
112123 }
@@ -125,16 +136,26 @@ def test_native_weakref_shutdown_with_c_retained_object():
125136
126137 static PyObject *hold(PyObject *self, PyObject *Py_UNUSED(ignored)) {
127138 Py_CLEAR(kept_alive);
128- kept_alive = PyObject_CallNoArgs((PyObject *)&NativeWeakRefType );
139+ kept_alive = new_native_weakref(1 );
129140 if (kept_alive == NULL) {
130141 return NULL;
131142 }
132- write_marker("held\\ n");
143+ write_marker("created: held\\ n");
133144 return Py_NewRef(kept_alive);
134145 }
135146
147+ static PyObject *make(PyObject *self, PyObject *Py_UNUSED(ignored)) {
148+ PyObject *obj = new_native_weakref(2);
149+ if (obj == NULL) {
150+ return NULL;
151+ }
152+ write_marker("created:free\\ n");
153+ return obj;
154+ }
155+
136156 static PyMethodDef methods[] = {
137157 {"hold", hold, METH_NOARGS, ""},
158+ {"make", make, METH_NOARGS, ""},
138159 {NULL, NULL, 0, NULL},
139160 };
140161
@@ -173,19 +194,26 @@ def test_native_weakref_shutdown_with_c_retained_object():
173194 if not __graalpython__ .is_native :
174195 args += [f'--vm.Dpython.EnableBytecodeDSLInterpreter={ str (__graalpython__ .is_bytecode_dsl_interpreter ).lower ()} ' ]
175196 code = textwrap .dedent ("""
197+ import gc
176198 import weakref
177199 import native_weakref_shutdown_reproducer_gr50212
178200
179- obj = native_weakref_shutdown_reproducer_gr50212.hold()
180- wr = weakref.ref(obj)
201+ held = native_weakref_shutdown_reproducer_gr50212.hold()
202+ free = native_weakref_shutdown_reproducer_gr50212.make()
203+ held_wr = weakref.ref(held)
204+ free_wr = weakref.ref(free)
181205 type_wr = weakref.ref(native_weakref_shutdown_reproducer_gr50212.NativeWeakRef)
182- print(type(obj).__name__, flush=True)
206+ print(type(held).__name__, type(free).__name__, flush=True)
207+ del free
208+ for _ in range(3):
209+ gc.collect()
210+ print(held_wr() is not None, free_wr() is None, flush=True)
183211 """ )
184212 env = dict (ENV )
185213 env ["PYTHONPATH" ] = os .pathsep .join ([module_dir , env ["PYTHONPATH" ]])
186214 with tempfile .TemporaryDirectory () as tmpdir :
187215 marker = Path (tmpdir ) / "deallocated"
188216 env ["GR50212_DEALLOC_MARKER" ] = str (marker )
189217 proc = subprocess .run ([* args , '-c' , code ], env = env , capture_output = True , text = True , check = True )
190- assert proc .stdout .strip () == "NativeWeakRef"
191- assert marker .read_text () == " deallocated\n "
218+ assert proc .stdout .splitlines () == [ "NativeWeakRef NativeWeakRef" , "True True" ]
219+ assert marker .read_text (). splitlines () == [ "created:held" , "created:free" , " deallocated:free" ]
0 commit comments