@@ -9,48 +9,66 @@ extern "C" {
9
9
#endif
10
10
11
11
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
12
+ #include "pycore_object.h" // _Py_REF_IS_MERGED()
12
13
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
+ {
14
32
assert (PyWeakref_Check (ref_obj ));
33
+ PyObject * ret = NULL ;
34
+ Py_BEGIN_CRITICAL_SECTION (ref_obj );
15
35
PyWeakReference * ref = _Py_CAST (PyWeakReference * , ref_obj );
16
36
PyObject * obj = ref -> wr_object ;
17
37
18
38
if (obj == Py_None ) {
19
39
// clear_weakref() was called
20
- return NULL ;
40
+ goto end ;
21
41
}
22
42
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 ;
32
45
}
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 ;
36
53
}
37
54
38
- static inline int _PyWeakref_IS_DEAD (PyObject * ref_obj ) {
55
+ static inline int _PyWeakref_IS_DEAD (PyObject * ref_obj )
56
+ {
39
57
assert (PyWeakref_Check (ref_obj ));
40
- int is_dead ;
58
+ int ret = 0 ;
41
59
Py_BEGIN_CRITICAL_SECTION (ref_obj );
42
60
PyWeakReference * ref = _Py_CAST (PyWeakReference * , ref_obj );
43
61
PyObject * obj = ref -> wr_object ;
44
62
if (obj == Py_None ) {
45
63
// clear_weakref() was called
46
- is_dead = 1 ;
64
+ ret = 1 ;
47
65
}
48
66
else {
49
67
// See _PyWeakref_GET_REF() for the rationale of this test
50
- is_dead = ( Py_REFCNT ( obj ) == 0 );
68
+ ret = _is_dead ( obj );
51
69
}
52
70
Py_END_CRITICAL_SECTION ();
53
- return is_dead ;
71
+ return ret ;
54
72
}
55
73
56
74
extern Py_ssize_t _PyWeakref_GetWeakrefCount (PyWeakReference * head );
0 commit comments