]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111926: Set up basic sementics of weakref API for freethreading (gh-113621)
authorDonghee Na <donghee.na@python.org>
Wed, 3 Jan 2024 13:25:27 +0000 (22:25 +0900)
committerGitHub <noreply@github.com>
Wed, 3 Jan 2024 13:25:27 +0000 (13:25 +0000)
---------

Co-authored-by: Sam Gross <colesbury@gmail.com>
Include/internal/pycore_weakref.h

index eacbe14c903289017ee49f36cb062365a0838d48..dea267b49039e72e27c50d68c82397234d99efe8 100644 (file)
@@ -9,48 +9,66 @@ extern "C" {
 #endif
 
 #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_object.h"           // _Py_REF_IS_MERGED()
 
-static inline PyObject* _PyWeakref_GET_REF(PyObject *ref_obj) {
+static inline int _is_dead(PyObject *obj)
+{
+    // Explanation for the Py_REFCNT() check: when a weakref's target is part
+    // of a long chain of deallocations which triggers the trashcan mechanism,
+    // clearing the weakrefs can be delayed long after the target's refcount
+    // has dropped to zero.  In the meantime, code accessing the weakref will
+    // be able to "see" the target object even though it is supposed to be
+    // unreachable.  See issue gh-60806.
+#if defined(Py_GIL_DISABLED)
+    Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared);
+    return shared == _Py_REF_SHARED(0, _Py_REF_MERGED);
+#else
+    return (Py_REFCNT(obj) == 0);
+#endif
+}
+
+static inline PyObject* _PyWeakref_GET_REF(PyObject *ref_obj)
+{
     assert(PyWeakref_Check(ref_obj));
+    PyObject *ret = NULL;
+    Py_BEGIN_CRITICAL_SECTION(ref_obj);
     PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj);
     PyObject *obj = ref->wr_object;
 
     if (obj == Py_None) {
         // clear_weakref() was called
-        return NULL;
+        goto end;
     }
 
-    // Explanation for the Py_REFCNT() check: when a weakref's target is part
-    // of a long chain of deallocations which triggers the trashcan mechanism,
-    // clearing the weakrefs can be delayed long after the target's refcount
-    // has dropped to zero.  In the meantime, code accessing the weakref will
-    // be able to "see" the target object even though it is supposed to be
-    // unreachable.  See issue gh-60806.
-    Py_ssize_t refcnt = Py_REFCNT(obj);
-    if (refcnt == 0) {
-        return NULL;
+    if (_is_dead(obj)) {
+        goto end;
     }
-
-    assert(refcnt > 0);
-    return Py_NewRef(obj);
+#if !defined(Py_GIL_DISABLED)
+    assert(Py_REFCNT(obj) > 0);
+#endif
+    ret = Py_NewRef(obj);
+end:
+    Py_END_CRITICAL_SECTION();
+    return ret;
 }
 
-static inline int _PyWeakref_IS_DEAD(PyObject *ref_obj) {
+static inline int _PyWeakref_IS_DEAD(PyObject *ref_obj)
+{
     assert(PyWeakref_Check(ref_obj));
-    int is_dead;
+    int ret = 0;
     Py_BEGIN_CRITICAL_SECTION(ref_obj);
     PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj);
     PyObject *obj = ref->wr_object;
     if (obj == Py_None) {
         // clear_weakref() was called
-        is_dead = 1;
+        ret = 1;
     }
     else {
         // See _PyWeakref_GET_REF() for the rationale of this test
-        is_dead = (Py_REFCNT(obj) == 0);
+        ret = _is_dead(obj);
     }
     Py_END_CRITICAL_SECTION();
-    return is_dead;
+    return ret;
 }
 
 extern Py_ssize_t _PyWeakref_GetWeakrefCount(PyWeakReference *head);