8000 gh-111926: Set up basic sementics of weakref API for freethreading (g… · python/cpython@0c3455a · GitHub
[go: up one dir, main page]

Skip to content

Commit 0c3455a

Browse files
corona10colesbury
andauthored
gh-111926: Set up basic sementics of weakref API for freethreading (gh-113621)
--------- Co-authored-by: Sam Gross <colesbury@gmail.com>
1 parent fab7ad6 commit 0c3455a

File tree

1 file changed

+37
-19
lines changed

1 file changed

+37
-19
lines changed

Include/internal/pycore_weakref.h

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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

5674
extern Py_ssize_t _PyWeakref_GetWeakrefCount(PyWeakReference *head);

0 commit comments

Comments
 (0)
0