|
3 | 3 | #include "cinderx/python.h" |
4 | 4 |
|
5 | 5 | #include "internal/pycore_call.h" |
| 6 | +#include "internal/pycore_object.h" |
6 | 7 | #include "internal/pycore_pystate.h" |
7 | 8 |
|
8 | 9 | #if PY_VERSION_HEX >= 0x030D0000 |
9 | 10 | #include "internal/pycore_modsupport.h" |
10 | 11 | #endif |
11 | 12 |
|
| 13 | +#if PY_VERSION_HEX >= 0x030E0000 |
| 14 | +#include "internal/pycore_object_deferred.h" |
| 15 | +#include "internal/pycore_uniqueid.h" |
| 16 | +#endif |
| 17 | + |
12 | 18 | #include "cinderx/CachedProperties/cached_properties.h" |
13 | 19 | #include "cinderx/Common/audit.h" |
14 | 20 | #include "cinderx/Common/extra-py-flags.h" |
|
26 | 32 | #include "cinderx/StaticPython/vtable_builder.h" |
27 | 33 | #include "cinderx/UpstreamBorrow/borrowed.h" |
28 | 34 |
|
| 35 | +static int type_refcount_indicates_escape( |
| 36 | + PyObject* type, |
| 37 | + Py_ssize_t expected_refcount) { |
| 38 | + assert(PyType_Check(type)); |
| 39 | + assert(PyType_HasFeature((PyTypeObject*)type, Py_TPFLAGS_HEAPTYPE)); |
| 40 | + Py_ssize_t refcount = Py_REFCNT(type); |
| 41 | + |
| 42 | +#ifdef Py_GIL_DISABLED |
| 43 | + // TODO: Pending references owned by other threads cannot be read safely |
| 44 | + // here. If a metaclass or base publishes the type, this check can miss |
| 45 | + // instances created by another thread. Closing that race requires |
| 46 | + // synchronizing all threads across both this check and the layout update, or |
| 47 | + // finalizing the layout before user callbacks can publish the type. |
| 48 | + PyHeapTypeObject* heap_type = (PyHeapTypeObject*)type; |
| 49 | + Py_ssize_t unique_id = heap_type->unique_id; |
| 50 | + |
| 51 | + if (unique_id != _Py_INVALID_UNIQUE_ID) { |
| 52 | + Py_ssize_t index = unique_id - 1; |
| 53 | + _PyThreadStateImpl* tstate = (_PyThreadStateImpl*)_PyThreadState_GET(); |
| 54 | + |
| 55 | + if (index < tstate->refcounts.size) { |
| 56 | + refcount += tstate->refcounts.values[index]; |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + if (_PyObject_HasDeferredRefcount(type)) { |
| 61 | + refcount -= _Py_REF_DEFERRED; |
| 62 | + } |
| 63 | +#endif |
| 64 | + |
| 65 | + return refcount != expected_refcount; |
| 66 | +} |
| 67 | + |
29 | 68 | PyDoc_STRVAR( |
30 | 69 | _static__doc__, |
31 | 70 | "_static contains types related to static Python\n"); |
@@ -1595,9 +1634,10 @@ static PyObject* _static___build_cinder_class__( |
1595 | 1634 | slot_count++; |
1596 | 1635 | } |
1597 | 1636 | #endif |
| 1637 | + |
1598 | 1638 | // Type by default has 2 references, the one which we'll return, and one |
1599 | 1639 | // which is a circular reference between the type and its MRO |
1600 | | - if (Py_REFCNT(type) != 2 + slot_count) { |
| 1640 | + if (type_refcount_indicates_escape(type, 2 + slot_count)) { |
1601 | 1641 | leaked_type = 1; |
1602 | 1642 | } |
1603 | 1643 | } |
|
0 commit comments