• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef Py_INTERNAL_WEAKREF_H
2 #define Py_INTERNAL_WEAKREF_H
3 #ifdef __cplusplus
4 extern "C" {
5 #endif
6 
7 #ifndef Py_BUILD_CORE
8 #  error "this header requires Py_BUILD_CORE define"
9 #endif
10 
11 #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
12 #include "pycore_lock.h"
13 #include "pycore_object.h"           // _Py_REF_IS_MERGED()
14 #include "pycore_pyatomic_ft_wrappers.h"
15 
16 #ifdef Py_GIL_DISABLED
17 
18 #define WEAKREF_LIST_LOCK(obj) \
19     _PyInterpreterState_GET()  \
20         ->weakref_locks[((uintptr_t)obj) % NUM_WEAKREF_LIST_LOCKS]
21 
22 // Lock using the referenced object
23 #define LOCK_WEAKREFS(obj) \
24     PyMutex_LockFlags(&WEAKREF_LIST_LOCK(obj), _Py_LOCK_DONT_DETACH)
25 #define UNLOCK_WEAKREFS(obj) PyMutex_Unlock(&WEAKREF_LIST_LOCK(obj))
26 
27 // Lock using a weakref
28 #define LOCK_WEAKREFS_FOR_WR(wr) \
29     PyMutex_LockFlags(wr->weakrefs_lock, _Py_LOCK_DONT_DETACH)
30 #define UNLOCK_WEAKREFS_FOR_WR(wr) PyMutex_Unlock(wr->weakrefs_lock)
31 
32 #else
33 
34 #define LOCK_WEAKREFS(obj)
35 #define UNLOCK_WEAKREFS(obj)
36 
37 #define LOCK_WEAKREFS_FOR_WR(wr)
38 #define UNLOCK_WEAKREFS_FOR_WR(wr)
39 
40 #endif
41 
_is_dead(PyObject * obj)42 static inline int _is_dead(PyObject *obj)
43 {
44     // Explanation for the Py_REFCNT() check: when a weakref's target is part
45     // of a long chain of deallocations which triggers the trashcan mechanism,
46     // clearing the weakrefs can be delayed long after the target's refcount
47     // has dropped to zero.  In the meantime, code accessing the weakref will
48     // be able to "see" the target object even though it is supposed to be
49     // unreachable.  See issue gh-60806.
50 #if defined(Py_GIL_DISABLED)
51     Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared);
52     return shared == _Py_REF_SHARED(0, _Py_REF_MERGED);
53 #else
54     return (Py_REFCNT(obj) == 0);
55 #endif
56 }
57 
_PyWeakref_GET_REF(PyObject * ref_obj)58 static inline PyObject* _PyWeakref_GET_REF(PyObject *ref_obj)
59 {
60     assert(PyWeakref_Check(ref_obj));
61     PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj);
62 
63     PyObject *obj = FT_ATOMIC_LOAD_PTR(ref->wr_object);
64     if (obj == Py_None) {
65         // clear_weakref() was called
66         return NULL;
67     }
68 
69     LOCK_WEAKREFS(obj);
70 #ifdef Py_GIL_DISABLED
71     if (ref->wr_object == Py_None) {
72         // clear_weakref() was called
73         UNLOCK_WEAKREFS(obj);
74         return NULL;
75     }
76 #endif
77     if (_Py_TryIncref(obj)) {
78         UNLOCK_WEAKREFS(obj);
79         return obj;
80     }
81     UNLOCK_WEAKREFS(obj);
82     return NULL;
83 }
84 
_PyWeakref_IS_DEAD(PyObject * ref_obj)85 static inline int _PyWeakref_IS_DEAD(PyObject *ref_obj)
86 {
87     assert(PyWeakref_Check(ref_obj));
88     int ret = 0;
89     PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj);
90     PyObject *obj = FT_ATOMIC_LOAD_PTR(ref->wr_object);
91     if (obj == Py_None) {
92         // clear_weakref() was called
93         ret = 1;
94     }
95     else {
96         LOCK_WEAKREFS(obj);
97         // See _PyWeakref_GET_REF() for the rationale of this test
98 #ifdef Py_GIL_DISABLED
99         ret = (ref->wr_object == Py_None) || _is_dead(obj);
100 #else
101         ret = _is_dead(obj);
102 #endif
103         UNLOCK_WEAKREFS(obj);
104     }
105     return ret;
106 }
107 
108 extern Py_ssize_t _PyWeakref_GetWeakrefCount(PyObject *obj);
109 
110 // Clear all the weak references to obj but leave their callbacks uncalled and
111 // intact.
112 extern void _PyWeakref_ClearWeakRefsNoCallbacks(PyObject *obj);
113 
114 PyAPI_FUNC(int) _PyWeakref_IsDead(PyObject *weakref);
115 
116 #ifdef __cplusplus
117 }
118 #endif
119 #endif /* !Py_INTERNAL_WEAKREF_H */
120