@@ -9,48 +9,66 @@ extern "C" {
99#endif
1010
1111#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
12+ #include "pycore_object.h" // _Py_REF_IS_MERGED()
1213
13- static inline PyObject * _PyWeakref_GET_REF (PyObject * ref_obj ) {
14+ static inline int _is_dead (PyObject * obj )
15+ {
16+ // Explanation for the Py_REFCNT() check: when a weakref's target is part
17+ // of a long chain of deallocations which triggers the trashcan mechanism,
18+ // clearing the weakrefs can be delayed long after the target's refcount
19+ // has dropped to zero. In the meantime, code accessing the weakref will
20+ // be able to "see" the target object even though it is supposed to be
21+ // unreachable. See issue gh-60806.
22+ #if defined(Py_GIL_DISABLED )
23+ Py_ssize_t shared = _Py_atomic_load_ssize_relaxed (& obj -> ob_ref_shared );
24+ return shared == _Py_REF_SHARED (0 , _Py_REF_MERGED );
25+ #else
26+ return (Py_REFCNT (obj ) == 0 );
27+ #endif
28+ }
29+
30+ static inline PyObject * _PyWeakref_GET_REF (PyObject * ref_obj )
31+ {
1432 assert (PyWeakref_Check (ref_obj ));
33+ PyObject * ret = NULL ;
34+ Py_BEGIN_CRITICAL_SECTION (ref_obj );
1535 PyWeakReference * ref = _Py_CAST (PyWeakReference * , ref_obj );
1636 PyObject * obj = ref -> wr_object ;
1737
1838 if (obj == Py_None ) {
1939 // clear_weakref() was called
20- return NULL ;
40+ goto end ;
2141 }
2242
23- // Explanation for the Py_REFCNT() check: when a weakref's target is part
24- // of a long chain of deallocations which triggers the trashcan mechanism,
25- // clearing the weakrefs can be delayed long after the target's refcount
26- // has dropped to zero. In the meantime, code accessing the weakref will
27- // be able to "see" the target object even though it is supposed to be
28- // unreachable. See issue gh-60806.
29- Py_ssize_t refcnt = Py_REFCNT (obj );
30- if (refcnt == 0 ) {
31- return NULL ;
43+ if (_is_dead (obj )) {
44+ goto end ;
3245 }
33-
34- assert (refcnt > 0 );
35- return Py_NewRef (obj );
46+ #if !defined(Py_GIL_DISABLED )
47+ assert (Py_REFCNT (obj ) > 0 );
48+ #endif
49+ ret = Py_NewRef (obj );
50+ end :
51+ Py_END_CRITICAL_SECTION ();
52+ return ret ;
3653}
3754
38- static inline int _PyWeakref_IS_DEAD (PyObject * ref_obj ) {
55+ static inline int _PyWeakref_IS_DEAD (PyObject * ref_obj )
56+ {
3957 assert (PyWeakref_Check (ref_obj ));
40- int is_dead ;
58+ int ret = 0 ;
4159 Py_BEGIN_CRITICAL_SECTION (ref_obj );
4260 PyWeakReference * ref = _Py_CAST (PyWeakReference * , ref_obj );
4361 PyObject * obj = ref -> wr_object ;
4462 if (obj == Py_None ) {
4563 // clear_weakref() was called
46- is_dead = 1 ;
64+ ret = 1 ;
4765 }
4866 else {
4967 // See _PyWeakref_GET_REF() for the rationale of this test
50- is_dead = ( Py_REFCNT ( obj ) == 0 );
68+ ret = _is_dead ( obj );
5169 }
5270 Py_END_CRITICAL_SECTION ();
53- return is_dead ;
71+ return ret ;
5472}
5573
5674extern Py_ssize_t _PyWeakref_GetWeakrefCount (PyWeakReference * head );
0 commit comments