summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Objects/weakrefobject.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tools/python3/Objects/weakrefobject.c')
-rw-r--r--contrib/tools/python3/Objects/weakrefobject.c802
1 files changed, 442 insertions, 360 deletions
diff --git a/contrib/tools/python3/Objects/weakrefobject.c b/contrib/tools/python3/Objects/weakrefobject.c
index aee79fc1410..61f05514a48 100644
--- a/contrib/tools/python3/Objects/weakrefobject.c
+++ b/contrib/tools/python3/Objects/weakrefobject.c
@@ -1,25 +1,62 @@
#include "Python.h"
+#include "pycore_critical_section.h"
+#include "pycore_lock.h"
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
#include "pycore_object.h" // _PyObject_GET_WEAKREFS_LISTPTR()
-#include "structmember.h" // PyMemberDef
+#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
+#include "pycore_pystate.h"
+#include "pycore_weakref.h" // _PyWeakref_GET_REF()
+#ifdef Py_GIL_DISABLED
+/*
+ * Thread-safety for free-threaded builds
+ * ======================================
+ *
+ * In free-threaded builds we need to protect mutable state of:
+ *
+ * - The weakref (wr_object, hash, wr_callback)
+ * - The referenced object (its head-of-list pointer)
+ * - The linked list of weakrefs
+ *
+ * For now we've chosen to address this in a straightforward way:
+ *
+ * - The weakref's hash is protected using the weakref's per-object lock.
+ * - The other mutable is protected by a striped lock keyed on the referenced
+ * object's address.
+ * - The striped lock must be locked using `_Py_LOCK_DONT_DETACH` in order to
+ * support atomic deletion from WeakValueDictionaries. As a result, we must
+ * be careful not to perform any operations that could suspend while the
+ * lock is held.
+ *
+ * Since the world is stopped when the GC runs, it is free to clear weakrefs
+ * without acquiring any locks.
+ */
+
+#endif
#define GET_WEAKREFS_LISTPTR(o) \
((PyWeakReference **) _PyObject_GET_WEAKREFS_LISTPTR(o))
Py_ssize_t
-_PyWeakref_GetWeakrefCount(PyWeakReference *head)
+_PyWeakref_GetWeakrefCount(PyObject *obj)
{
- Py_ssize_t count = 0;
+ if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
+ return 0;
+ }
+ LOCK_WEAKREFS(obj);
+ Py_ssize_t count = 0;
+ PyWeakReference *head = *GET_WEAKREFS_LISTPTR(obj);
while (head != NULL) {
++count;
head = head->wr_next;
}
+ UNLOCK_WEAKREFS(obj);
return count;
}
-static PyObject *weakref_vectorcall(PyWeakReference *self, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject *weakref_vectorcall(PyObject *self, PyObject *const *args, size_t nargsf, PyObject *kwnames);
static void
init_weakref(PyWeakReference *self, PyObject *ob, PyObject *callback)
@@ -29,55 +66,56 @@ init_weakref(PyWeakReference *self, PyObject *ob, PyObject *callback)
self->wr_prev = NULL;
self->wr_next = NULL;
self->wr_callback = Py_XNewRef(callback);
- self->vectorcall = (vectorcallfunc)weakref_vectorcall;
+ self->vectorcall = weakref_vectorcall;
+#ifdef Py_GIL_DISABLED
+ self->weakrefs_lock = &WEAKREF_LIST_LOCK(ob);
+ _PyObject_SetMaybeWeakref(ob);
+ _PyObject_SetMaybeWeakref((PyObject *)self);
+#endif
}
-static PyWeakReference *
-new_weakref(PyObject *ob, PyObject *callback)
-{
- PyWeakReference *result;
-
- result = PyObject_GC_New(PyWeakReference, &_PyWeakref_RefType);
- if (result) {
- init_weakref(result, ob, callback);
- PyObject_GC_Track(result);
- }
- return result;
-}
-
-
-/* This function clears the passed-in reference and removes it from the
- * list of weak references for the referent. This is the only code that
- * removes an item from the doubly-linked list of weak references for an
- * object; it is also responsible for clearing the callback slot.
- */
+// Clear the weakref and steal its callback into `callback`, if provided.
static void
-clear_weakref(PyWeakReference *self)
+clear_weakref_lock_held(PyWeakReference *self, PyObject **callback)
{
- PyObject *callback = self->wr_callback;
-
if (self->wr_object != Py_None) {
PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
-
- if (*list == self)
- /* If 'self' is the end of the list (and thus self->wr_next == NULL)
- then the weakref list itself (and thus the value of *list) will
- end up being set to NULL. */
- *list = self->wr_next;
- self->wr_object = Py_None;
- if (self->wr_prev != NULL)
+ if (*list == self) {
+ /* If 'self' is the end of the list (and thus self->wr_next ==
+ NULL) then the weakref list itself (and thus the value of *list)
+ will end up being set to NULL. */
+ FT_ATOMIC_STORE_PTR(*list, self->wr_next);
+ }
+ FT_ATOMIC_STORE_PTR(self->wr_object, Py_None);
+ if (self->wr_prev != NULL) {
self->wr_prev->wr_next = self->wr_next;
- if (self->wr_next != NULL)
+ }
+ if (self->wr_next != NULL) {
self->wr_next->wr_prev = self->wr_prev;
+ }
self->wr_prev = NULL;
self->wr_next = NULL;
}
if (callback != NULL) {
- Py_DECREF(callback);
+ *callback = self->wr_callback;
self->wr_callback = NULL;
}
}
+// Clear the weakref and its callback
+static void
+clear_weakref(PyWeakReference *self)
+{
+ PyObject *callback = NULL;
+ // self->wr_object may be Py_None if the GC cleared the weakref, so lock
+ // using the pointer in the weakref.
+ LOCK_WEAKREFS_FOR_WR(self);
+ clear_weakref_lock_held(self, &callback);
+ UNLOCK_WEAKREFS_FOR_WR(self);
+ Py_XDECREF(callback);
+}
+
+
/* Cyclic gc uses this to *just* clear the passed-in reference, leaving
* the callback intact and uncalled. It must be possible to call self's
* tp_dealloc() after calling this, so self has to be left in a sane enough
@@ -92,15 +130,9 @@ clear_weakref(PyWeakReference *self)
void
_PyWeakref_ClearRef(PyWeakReference *self)
{
- PyObject *callback;
-
assert(self != NULL);
assert(PyWeakref_Check(self));
- /* Preserve and restore the callback around clear_weakref. */
- callback = self->wr_callback;
- self->wr_callback = NULL;
- clear_weakref(self);
- self->wr_callback = callback;
+ clear_weakref_lock_held(self, NULL);
}
static void
@@ -123,13 +155,17 @@ gc_traverse(PyWeakReference *self, visitproc visit, void *arg)
static int
gc_clear(PyWeakReference *self)
{
- clear_weakref(self);
+ PyObject *callback;
+ // The world is stopped during GC in free-threaded builds. It's safe to
+ // call this without holding the lock.
+ clear_weakref_lock_held(self, &callback);
+ Py_XDECREF(callback);
return 0;
}
static PyObject *
-weakref_vectorcall(PyWeakReference *self, PyObject *const *args,
+weakref_vectorcall(PyObject *self, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
if (!_PyArg_NoKwnames("weakref", kwnames)) {
@@ -139,52 +175,57 @@ weakref_vectorcall(PyWeakReference *self, PyObject *const *args,
if (!_PyArg_CheckPositional("weakref", nargs, 0, 0)) {
return NULL;
}
- return Py_NewRef(PyWeakref_GET_OBJECT(self));
+ PyObject *obj = _PyWeakref_GET_REF(self);
+ if (obj == NULL) {
+ Py_RETURN_NONE;
+ }
+ return obj;
}
static Py_hash_t
-weakref_hash(PyWeakReference *self)
+weakref_hash_lock_held(PyWeakReference *self)
{
if (self->hash != -1)
return self->hash;
- PyObject* obj = PyWeakref_GET_OBJECT(self);
- if (obj == Py_None) {
+ PyObject* obj = _PyWeakref_GET_REF((PyObject*)self);
+ if (obj == NULL) {
PyErr_SetString(PyExc_TypeError, "weak object has gone away");
return -1;
}
- Py_INCREF(obj);
self->hash = PyObject_Hash(obj);
Py_DECREF(obj);
return self->hash;
}
+static Py_hash_t
+weakref_hash(PyWeakReference *self)
+{
+ Py_hash_t hash;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ hash = weakref_hash_lock_held(self);
+ Py_END_CRITICAL_SECTION();
+ return hash;
+}
static PyObject *
-weakref_repr(PyWeakReference *self)
+weakref_repr(PyObject *self)
{
- PyObject *name, *repr;
- PyObject* obj = PyWeakref_GET_OBJECT(self);
-
- if (obj == Py_None) {
+ PyObject* obj = _PyWeakref_GET_REF(self);
+ if (obj == NULL) {
return PyUnicode_FromFormat("<weakref at %p; dead>", self);
}
- Py_INCREF(obj);
- name = _PyObject_LookupSpecial(obj, &_Py_ID(__name__));
+ PyObject *name = _PyObject_LookupSpecial(obj, &_Py_ID(__name__));
+ PyObject *repr;
if (name == NULL || !PyUnicode_Check(name)) {
repr = PyUnicode_FromFormat(
- "<weakref at %p; to '%s' at %p>",
- self,
- Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name,
- obj);
+ "<weakref at %p; to '%T' at %p>",
+ self, obj, obj);
}
else {
repr = PyUnicode_FromFormat(
- "<weakref at %p; to '%s' at %p (%U)>",
- self,
- Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name,
- obj,
- name);
+ "<weakref at %p; to '%T' at %p (%U)>",
+ self, obj, obj, name);
}
Py_DECREF(obj);
Py_XDECREF(name);
@@ -196,15 +237,18 @@ weakref_repr(PyWeakReference *self)
gone away, they are equal if they are identical. */
static PyObject *
-weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
+weakref_richcompare(PyObject* self, PyObject* other, int op)
{
if ((op != Py_EQ && op != Py_NE) ||
!PyWeakref_Check(self) ||
!PyWeakref_Check(other)) {
Py_RETURN_NOTIMPLEMENTED;
}
- if (PyWeakref_GET_OBJECT(self) == Py_None
- || PyWeakref_GET_OBJECT(other) == Py_None) {
+ PyObject* obj = _PyWeakref_GET_REF(self);
+ PyObject* other_obj = _PyWeakref_GET_REF(other);
+ if (obj == NULL || other_obj == NULL) {
+ Py_XDECREF(obj);
+ Py_XDECREF(other_obj);
int res = (self == other);
if (op == Py_NE)
res = !res;
@@ -213,10 +257,6 @@ weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
else
Py_RETURN_FALSE;
}
- PyObject* obj = PyWeakref_GET_OBJECT(self);
- PyObject* other_obj = PyWeakref_GET_OBJECT(other);
- Py_INCREF(obj);
- Py_INCREF(other_obj);
PyObject* res = PyObject_RichCompare(obj, other_obj, op);
Py_DECREF(obj);
Py_DECREF(other_obj);
@@ -278,6 +318,135 @@ insert_head(PyWeakReference *newref, PyWeakReference **list)
*list = newref;
}
+/* See if we can reuse either the basic ref or proxy in list instead of
+ * creating a new weakref
+ */
+static PyWeakReference *
+try_reuse_basic_ref(PyWeakReference *list, PyTypeObject *type,
+ PyObject *callback)
+{
+ if (callback != NULL) {
+ return NULL;
+ }
+
+ PyWeakReference *ref, *proxy;
+ get_basic_refs(list, &ref, &proxy);
+
+ PyWeakReference *cand = NULL;
+ if (type == &_PyWeakref_RefType) {
+ cand = ref;
+ }
+ if ((type == &_PyWeakref_ProxyType) ||
+ (type == &_PyWeakref_CallableProxyType)) {
+ cand = proxy;
+ }
+
+ if (cand != NULL && _Py_TryIncref((PyObject *) cand)) {
+ return cand;
+ }
+ return NULL;
+}
+
+static int
+is_basic_ref(PyWeakReference *ref)
+{
+ return (ref->wr_callback == NULL) && PyWeakref_CheckRefExact(ref);
+}
+
+static int
+is_basic_proxy(PyWeakReference *proxy)
+{
+ return (proxy->wr_callback == NULL) && PyWeakref_CheckProxy(proxy);
+}
+
+static int
+is_basic_ref_or_proxy(PyWeakReference *wr)
+{
+ return is_basic_ref(wr) || is_basic_proxy(wr);
+}
+
+/* Insert `newref` in the appropriate position in `list` */
+static void
+insert_weakref(PyWeakReference *newref, PyWeakReference **list)
+{
+ PyWeakReference *ref, *proxy;
+ get_basic_refs(*list, &ref, &proxy);
+
+ PyWeakReference *prev;
+ if (is_basic_ref(newref)) {
+ prev = NULL;
+ }
+ else if (is_basic_proxy(newref)) {
+ prev = ref;
+ }
+ else {
+ prev = (proxy == NULL) ? ref : proxy;
+ }
+
+ if (prev == NULL) {
+ insert_head(newref, list);
+ }
+ else {
+ insert_after(newref, prev);
+ }
+}
+
+static PyWeakReference *
+allocate_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback)
+{
+ PyWeakReference *newref = (PyWeakReference *) type->tp_alloc(type, 0);
+ if (newref == NULL) {
+ return NULL;
+ }
+ init_weakref(newref, obj, callback);
+ return newref;
+}
+
+static PyWeakReference *
+get_or_create_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback)
+{
+ if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
+ PyErr_Format(PyExc_TypeError,
+ "cannot create weak reference to '%s' object",
+ Py_TYPE(obj)->tp_name);
+ return NULL;
+ }
+ if (callback == Py_None)
+ callback = NULL;
+
+ PyWeakReference **list = GET_WEAKREFS_LISTPTR(obj);
+ if ((type == &_PyWeakref_RefType) ||
+ (type == &_PyWeakref_ProxyType) ||
+ (type == &_PyWeakref_CallableProxyType))
+ {
+ LOCK_WEAKREFS(obj);
+ PyWeakReference *basic_ref = try_reuse_basic_ref(*list, type, callback);
+ if (basic_ref != NULL) {
+ UNLOCK_WEAKREFS(obj);
+ return basic_ref;
+ }
+ PyWeakReference *newref = allocate_weakref(type, obj, callback);
+ if (newref == NULL) {
+ UNLOCK_WEAKREFS(obj);
+ return NULL;
+ }
+ insert_weakref(newref, list);
+ UNLOCK_WEAKREFS(obj);
+ return newref;
+ }
+ else {
+ // We may not be able to safely allocate inside the lock
+ PyWeakReference *newref = allocate_weakref(type, obj, callback);
+ if (newref == NULL) {
+ return NULL;
+ }
+ LOCK_WEAKREFS(obj);
+ insert_weakref(newref, list);
+ UNLOCK_WEAKREFS(obj);
+ return newref;
+ }
+}
+
static int
parse_weakref_init_args(const char *funcname, PyObject *args, PyObject *kwargs,
PyObject **obp, PyObject **callbackp)
@@ -288,54 +457,11 @@ parse_weakref_init_args(const char *funcname, PyObject *args, PyObject *kwargs,
static PyObject *
weakref___new__(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
- PyWeakReference *self = NULL;
PyObject *ob, *callback = NULL;
-
if (parse_weakref_init_args("__new__", args, kwargs, &ob, &callback)) {
- PyWeakReference *ref, *proxy;
- PyWeakReference **list;
-
- if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
- PyErr_Format(PyExc_TypeError,
- "cannot create weak reference to '%s' object",
- Py_TYPE(ob)->tp_name);
- return NULL;
- }
- if (callback == Py_None)
- callback = NULL;
- list = GET_WEAKREFS_LISTPTR(ob);
- get_basic_refs(*list, &ref, &proxy);
- if (callback == NULL && type == &_PyWeakref_RefType) {
- if (ref != NULL) {
- /* We can re-use an existing reference. */
- return Py_NewRef(ref);
- }
- }
- /* We have to create a new reference. */
- /* Note: the tp_alloc() can trigger cyclic GC, so the weakref
- list on ob can be mutated. This means that the ref and
- proxy pointers we got back earlier may have been collected,
- so we need to compute these values again before we use
- them. */
- self = (PyWeakReference *) (type->tp_alloc(type, 0));
- if (self != NULL) {
- init_weakref(self, ob, callback);
- if (callback == NULL && type == &_PyWeakref_RefType) {
- insert_head(self, list);
- }
- else {
- PyWeakReference *prev;
-
- get_basic_refs(*list, &ref, &proxy);
- prev = (proxy == NULL) ? ref : proxy;
- if (prev == NULL)
- insert_head(self, list);
- else
- insert_after(self, prev);
- }
- }
+ return (PyObject *)get_or_create_weakref(type, ob, callback);
}
- return (PyObject *)self;
+ return NULL;
}
static int
@@ -354,7 +480,7 @@ weakref___init__(PyObject *self, PyObject *args, PyObject *kwargs)
static PyMemberDef weakref_members[] = {
- {"__callback__", T_OBJECT, offsetof(PyWeakReference, wr_callback), READONLY},
+ {"__callback__", _Py_T_OBJECT, offsetof(PyWeakReference, wr_callback), Py_READONLY},
{NULL} /* Sentinel */
};
@@ -372,13 +498,13 @@ _PyWeakref_RefType = {
.tp_dealloc = weakref_dealloc,
.tp_vectorcall_offset = offsetof(PyWeakReference, vectorcall),
.tp_call = PyVectorcall_Call,
- .tp_repr = (reprfunc)weakref_repr,
+ .tp_repr = weakref_repr,
.tp_hash = (hashfunc)weakref_hash,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_BASETYPE,
.tp_traverse = (traverseproc)gc_traverse,
.tp_clear = (inquiry)gc_clear,
- .tp_richcompare = (richcmpfunc)weakref_richcompare,
+ .tp_richcompare = weakref_richcompare,
.tp_methods = weakref_methods,
.tp_members = weakref_members,
.tp_init = weakref___init__,
@@ -388,15 +514,15 @@ _PyWeakref_RefType = {
};
-static int
-proxy_checkref(PyWeakReference *proxy)
+static bool
+proxy_check_ref(PyObject *obj)
{
- if (PyWeakref_GET_OBJECT(proxy) == Py_None) {
+ if (obj == NULL) {
PyErr_SetString(PyExc_ReferenceError,
"weakly-referenced object no longer exists");
- return 0;
+ return false;
}
- return 1;
+ return true;
}
@@ -406,16 +532,19 @@ proxy_checkref(PyWeakReference *proxy)
*/
#define UNWRAP(o) \
if (PyWeakref_CheckProxy(o)) { \
- if (!proxy_checkref((PyWeakReference *)o)) \
+ o = _PyWeakref_GET_REF(o); \
+ if (!proxy_check_ref(o)) { \
return NULL; \
- o = PyWeakref_GET_OBJECT(o); \
+ } \
+ } \
+ else { \
+ Py_INCREF(o); \
}
#define WRAP_UNARY(method, generic) \
static PyObject * \
method(PyObject *proxy) { \
UNWRAP(proxy); \
- Py_INCREF(proxy); \
PyObject* res = generic(proxy); \
Py_DECREF(proxy); \
return res; \
@@ -426,8 +555,6 @@ proxy_checkref(PyWeakReference *proxy)
method(PyObject *x, PyObject *y) { \
UNWRAP(x); \
UNWRAP(y); \
- Py_INCREF(x); \
- Py_INCREF(y); \
PyObject* res = generic(x, y); \
Py_DECREF(x); \
Py_DECREF(y); \
@@ -442,11 +569,9 @@ proxy_checkref(PyWeakReference *proxy)
method(PyObject *proxy, PyObject *v, PyObject *w) { \
UNWRAP(proxy); \
UNWRAP(v); \
- if (w != NULL) \
+ if (w != NULL) { \
UNWRAP(w); \
- Py_INCREF(proxy); \
- Py_INCREF(v); \
- Py_XINCREF(w); \
+ } \
PyObject* res = generic(proxy, v, w); \
Py_DECREF(proxy); \
Py_DECREF(v); \
@@ -458,7 +583,6 @@ proxy_checkref(PyWeakReference *proxy)
static PyObject * \
method(PyObject *proxy, PyObject *Py_UNUSED(ignored)) { \
UNWRAP(proxy); \
- Py_INCREF(proxy); \
PyObject* res = PyObject_CallMethodNoArgs(proxy, &_Py_ID(SPECIAL)); \
Py_DECREF(proxy); \
return res; \
@@ -472,23 +596,32 @@ WRAP_UNARY(proxy_str, PyObject_Str)
WRAP_TERNARY(proxy_call, PyObject_Call)
static PyObject *
-proxy_repr(PyWeakReference *proxy)
+proxy_repr(PyObject *proxy)
{
- return PyUnicode_FromFormat(
- "<weakproxy at %p to %s at %p>",
- proxy,
- Py_TYPE(PyWeakref_GET_OBJECT(proxy))->tp_name,
- PyWeakref_GET_OBJECT(proxy));
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ PyObject *repr;
+ if (obj != NULL) {
+ repr = PyUnicode_FromFormat(
+ "<weakproxy at %p; to '%T' at %p>",
+ proxy, obj, obj);
+ Py_DECREF(obj);
+ }
+ else {
+ repr = PyUnicode_FromFormat(
+ "<weakproxy at %p; dead>",
+ proxy);
+ }
+ return repr;
}
static int
-proxy_setattr(PyWeakReference *proxy, PyObject *name, PyObject *value)
+proxy_setattr(PyObject *proxy, PyObject *name, PyObject *value)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return -1;
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
int res = PyObject_SetAttr(obj, name, value);
Py_DECREF(obj);
return res;
@@ -499,7 +632,10 @@ proxy_richcompare(PyObject *proxy, PyObject *v, int op)
{
UNWRAP(proxy);
UNWRAP(v);
- return PyObject_RichCompare(proxy, v, op);
+ PyObject* res = PyObject_RichCompare(proxy, v, op);
+ Py_DECREF(proxy);
+ Py_DECREF(v);
+ return res;
}
/* number slots */
@@ -539,13 +675,12 @@ WRAP_BINARY(proxy_matmul, PyNumber_MatrixMultiply)
WRAP_BINARY(proxy_imatmul, PyNumber_InPlaceMatrixMultiply)
static int
-proxy_bool(PyWeakReference *proxy)
+proxy_bool(PyObject *proxy)
{
- PyObject *o = PyWeakref_GET_OBJECT(proxy);
- if (!proxy_checkref(proxy)) {
+ PyObject *o = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(o)) {
return -1;
}
- Py_INCREF(o);
int res = PyObject_IsTrue(o);
Py_DECREF(o);
return res;
@@ -555,8 +690,6 @@ static void
proxy_dealloc(PyWeakReference *self)
{
PyObject_GC_UnTrack(self);
- if (self->wr_callback != NULL)
- PyObject_GC_UnTrack((PyObject *)self);
clear_weakref(self);
PyObject_GC_Del(self);
}
@@ -564,13 +697,12 @@ proxy_dealloc(PyWeakReference *self)
/* sequence slots */
static int
-proxy_contains(PyWeakReference *proxy, PyObject *value)
+proxy_contains(PyObject *proxy, PyObject *value)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return -1;
-
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
int res = PySequence_Contains(obj, value);
Py_DECREF(obj);
return res;
@@ -579,13 +711,12 @@ proxy_contains(PyWeakReference *proxy, PyObject *value)
/* mapping slots */
static Py_ssize_t
-proxy_length(PyWeakReference *proxy)
+proxy_length(PyObject *proxy)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return -1;
-
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
Py_ssize_t res = PyObject_Length(obj);
Py_DECREF(obj);
return res;
@@ -594,13 +725,12 @@ proxy_length(PyWeakReference *proxy)
WRAP_BINARY(proxy_getitem, PyObject_GetItem)
static int
-proxy_setitem(PyWeakReference *proxy, PyObject *key, PyObject *value)
+proxy_setitem(PyObject *proxy, PyObject *key, PyObject *value)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return -1;
-
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
int res;
if (value == NULL) {
res = PyObject_DelItem(obj, key);
@@ -614,31 +744,31 @@ proxy_setitem(PyWeakReference *proxy, PyObject *key, PyObject *value)
/* iterator slots */
static PyObject *
-proxy_iter(PyWeakReference *proxy)
+proxy_iter(PyObject *proxy)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return NULL;
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
PyObject* res = PyObject_GetIter(obj);
Py_DECREF(obj);
return res;
}
static PyObject *
-proxy_iternext(PyWeakReference *proxy)
+proxy_iternext(PyObject *proxy)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return NULL;
-
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
+ }
if (!PyIter_Check(obj)) {
PyErr_Format(PyExc_TypeError,
"Weakref proxy referenced a non-iterator '%.200s' object",
Py_TYPE(obj)->tp_name);
+ Py_DECREF(obj);
return NULL;
}
- Py_INCREF(obj);
PyObject* res = PyIter_Next(obj);
Py_DECREF(obj);
return res;
@@ -666,7 +796,7 @@ static PyNumberMethods proxy_as_number = {
proxy_neg, /*nb_negative*/
proxy_pos, /*nb_positive*/
proxy_abs, /*nb_absolute*/
- (inquiry)proxy_bool, /*nb_bool*/
+ proxy_bool, /*nb_bool*/
proxy_invert, /*nb_invert*/
proxy_lshift, /*nb_lshift*/
proxy_rshift, /*nb_rshift*/
@@ -696,20 +826,20 @@ static PyNumberMethods proxy_as_number = {
};
static PySequenceMethods proxy_as_sequence = {
- (lenfunc)proxy_length, /*sq_length*/
+ proxy_length, /*sq_length*/
0, /*sq_concat*/
0, /*sq_repeat*/
0, /*sq_item*/
0, /*sq_slice*/
0, /*sq_ass_item*/
- 0, /*sq_ass_slice*/
- (objobjproc)proxy_contains, /* sq_contains */
+ 0, /*sq_ass_slice*/
+ proxy_contains, /* sq_contains */
};
static PyMappingMethods proxy_as_mapping = {
- (lenfunc)proxy_length, /*mp_length*/
+ proxy_length, /*mp_length*/
proxy_getitem, /*mp_subscript*/
- (objobjargproc)proxy_setitem, /*mp_ass_subscript*/
+ proxy_setitem, /*mp_ass_subscript*/
};
@@ -725,7 +855,7 @@ _PyWeakref_ProxyType = {
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)proxy_repr, /* tp_repr */
+ proxy_repr, /* tp_repr */
&proxy_as_number, /* tp_as_number */
&proxy_as_sequence, /* tp_as_sequence */
&proxy_as_mapping, /* tp_as_mapping */
@@ -734,7 +864,7 @@ _PyWeakref_ProxyType = {
0, /* tp_call */
proxy_str, /* tp_str */
proxy_getattr, /* tp_getattro */
- (setattrofunc)proxy_setattr, /* tp_setattro */
+ proxy_setattr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
@@ -742,9 +872,9 @@ _PyWeakref_ProxyType = {
(inquiry)gc_clear, /* tp_clear */
proxy_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)proxy_iter, /* tp_iter */
- (iternextfunc)proxy_iternext, /* tp_iternext */
- proxy_methods, /* tp_methods */
+ proxy_iter, /* tp_iter */
+ proxy_iternext, /* tp_iternext */
+ proxy_methods, /* tp_methods */
};
@@ -760,7 +890,7 @@ _PyWeakref_CallableProxyType = {
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (unaryfunc)proxy_repr, /* tp_repr */
+ proxy_repr, /* tp_repr */
&proxy_as_number, /* tp_as_number */
&proxy_as_sequence, /* tp_as_sequence */
&proxy_as_mapping, /* tp_as_mapping */
@@ -768,7 +898,7 @@ _PyWeakref_CallableProxyType = {
proxy_call, /* tp_call */
proxy_str, /* tp_str */
proxy_getattr, /* tp_getattro */
- (setattrofunc)proxy_setattr, /* tp_setattro */
+ proxy_setattr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
@@ -776,131 +906,43 @@ _PyWeakref_CallableProxyType = {
(inquiry)gc_clear, /* tp_clear */
proxy_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)proxy_iter, /* tp_iter */
- (iternextfunc)proxy_iternext, /* tp_iternext */
+ proxy_iter, /* tp_iter */
+ proxy_iternext, /* tp_iternext */
};
-
-
PyObject *
PyWeakref_NewRef(PyObject *ob, PyObject *callback)
{
- PyWeakReference *result = NULL;
- PyWeakReference **list;
- PyWeakReference *ref, *proxy;
-
- if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
- PyErr_Format(PyExc_TypeError,
- "cannot create weak reference to '%s' object",
- Py_TYPE(ob)->tp_name);
- return NULL;
- }
- list = GET_WEAKREFS_LISTPTR(ob);
- get_basic_refs(*list, &ref, &proxy);
- if (callback == Py_None)
- callback = NULL;
- if (callback == NULL)
- /* return existing weak reference if it exists */
- result = ref;
- if (result != NULL)
- Py_INCREF(result);
- else {
- /* Note: new_weakref() can trigger cyclic GC, so the weakref
- list on ob can be mutated. This means that the ref and
- proxy pointers we got back earlier may have been collected,
- so we need to compute these values again before we use
- them. */
- result = new_weakref(ob, callback);
- if (result != NULL) {
- get_basic_refs(*list, &ref, &proxy);
- if (callback == NULL) {
- if (ref == NULL)
- insert_head(result, list);
- else {
- /* Someone else added a ref without a callback
- during GC. Return that one instead of this one
- to avoid violating the invariants of the list
- of weakrefs for ob. */
- Py_SETREF(result, (PyWeakReference*)Py_NewRef(ref));
- }
- }
- else {
- PyWeakReference *prev;
-
- prev = (proxy == NULL) ? ref : proxy;
- if (prev == NULL)
- insert_head(result, list);
- else
- insert_after(result, prev);
- }
- }
- }
- return (PyObject *) result;
+ return (PyObject *)get_or_create_weakref(&_PyWeakref_RefType, ob,
+ callback);
}
-
PyObject *
PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
{
- PyWeakReference *result = NULL;
- PyWeakReference **list;
- PyWeakReference *ref, *proxy;
-
- if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
- PyErr_Format(PyExc_TypeError,
- "cannot create weak reference to '%s' object",
- Py_TYPE(ob)->tp_name);
- return NULL;
+ PyTypeObject *type = &_PyWeakref_ProxyType;
+ if (PyCallable_Check(ob)) {
+ type = &_PyWeakref_CallableProxyType;
}
- list = GET_WEAKREFS_LISTPTR(ob);
- get_basic_refs(*list, &ref, &proxy);
- if (callback == Py_None)
- callback = NULL;
- if (callback == NULL)
- /* attempt to return an existing weak reference if it exists */
- result = proxy;
- if (result != NULL)
- Py_INCREF(result);
- else {
- /* Note: new_weakref() can trigger cyclic GC, so the weakref
- list on ob can be mutated. This means that the ref and
- proxy pointers we got back earlier may have been collected,
- so we need to compute these values again before we use
- them. */
- result = new_weakref(ob, callback);
- if (result != NULL) {
- PyWeakReference *prev;
+ return (PyObject *)get_or_create_weakref(type, ob, callback);
+}
- if (PyCallable_Check(ob)) {
- Py_SET_TYPE(result, &_PyWeakref_CallableProxyType);
- }
- else {
- Py_SET_TYPE(result, &_PyWeakref_ProxyType);
- }
- get_basic_refs(*list, &ref, &proxy);
- if (callback == NULL) {
- if (proxy != NULL) {
- /* Someone else added a proxy without a callback
- during GC. Return that one instead of this one
- to avoid violating the invariants of the list
- of weakrefs for ob. */
- Py_SETREF(result, (PyWeakReference*)Py_NewRef(proxy));
- goto skip_insert;
- }
- prev = ref;
- }
- else
- prev = (proxy == NULL) ? ref : proxy;
- if (prev == NULL)
- insert_head(result, list);
- else
- insert_after(result, prev);
- skip_insert:
- ;
- }
+int
+PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
+{
+ if (ref == NULL) {
+ *pobj = NULL;
+ PyErr_BadInternalCall();
+ return -1;
}
- return (PyObject *) result;
+ if (!PyWeakref_Check(ref)) {
+ *pobj = NULL;
+ PyErr_SetString(PyExc_TypeError, "expected a weakref");
+ return -1;
+ }
+ *pobj = _PyWeakref_GET_REF(ref);
+ return (*pobj != NULL);
}
@@ -911,7 +953,12 @@ PyWeakref_GetObject(PyObject *ref)
PyErr_BadInternalCall();
return NULL;
}
- return PyWeakref_GET_OBJECT(ref);
+ PyObject *obj = _PyWeakref_GET_REF(ref);
+ if (obj == NULL) {
+ return Py_None;
+ }
+ Py_DECREF(obj);
+ return obj; // borrowed reference
}
/* Note that there's an inlined copy-paste of handle_callback() in gcmodule.c's
@@ -946,67 +993,82 @@ PyObject_ClearWeakRefs(PyObject *object)
PyErr_BadInternalCall();
return;
}
+
list = GET_WEAKREFS_LISTPTR(object);
- /* Remove the callback-less basic and proxy references */
- if (*list != NULL && (*list)->wr_callback == NULL) {
- clear_weakref(*list);
- if (*list != NULL && (*list)->wr_callback == NULL)
- clear_weakref(*list);
+ if (FT_ATOMIC_LOAD_PTR(*list) == NULL) {
+ // Fast path for the common case
+ return;
}
- if (*list != NULL) {
- PyWeakReference *current = *list;
- Py_ssize_t count = _PyWeakref_GetWeakrefCount(current);
- PyObject *exc = PyErr_GetRaisedException();
-
- if (count == 1) {
- PyObject *callback = current->wr_callback;
- current->wr_callback = NULL;
- clear_weakref(current);
- if (callback != NULL) {
- if (Py_REFCNT((PyObject *)current) > 0) {
- handle_callback(current, callback);
- }
- Py_DECREF(callback);
- }
+ /* Remove the callback-less basic and proxy references, which always appear
+ at the head of the list.
+ */
+ for (int done = 0; !done;) {
+ LOCK_WEAKREFS(object);
+ if (*list != NULL && is_basic_ref_or_proxy(*list)) {
+ PyObject *callback;
+ clear_weakref_lock_held(*list, &callback);
+ assert(callback == NULL);
}
- else {
- PyObject *tuple;
- Py_ssize_t i = 0;
+ done = (*list == NULL) || !is_basic_ref_or_proxy(*list);
+ UNLOCK_WEAKREFS(object);
+ }
- tuple = PyTuple_New(count * 2);
- if (tuple == NULL) {
- _PyErr_ChainExceptions1(exc);
- return;
- }
+ /* Deal with non-canonical (subtypes or refs with callbacks) references. */
+ Py_ssize_t num_weakrefs = _PyWeakref_GetWeakrefCount(object);
+ if (num_weakrefs == 0) {
+ return;
+ }
- for (i = 0; i < count; ++i) {
- PyWeakReference *next = current->wr_next;
+ PyObject *exc = PyErr_GetRaisedException();
+ PyObject *tuple = PyTuple_New(num_weakrefs * 2);
+ if (tuple == NULL) {
+ _PyWeakref_ClearWeakRefsNoCallbacks(object);
+ PyErr_WriteUnraisable(NULL);
+ PyErr_SetRaisedException(exc);
+ return;
+ }
- if (Py_REFCNT((PyObject *)current) > 0) {
- PyTuple_SET_ITEM(tuple, i * 2, Py_NewRef(current));
- PyTuple_SET_ITEM(tuple, i * 2 + 1, current->wr_callback);
- }
- else {
- Py_DECREF(current->wr_callback);
- }
- current->wr_callback = NULL;
- clear_weakref(current);
- current = next;
+ Py_ssize_t num_items = 0;
+ for (int done = 0; !done;) {
+ PyObject *callback = NULL;
+ LOCK_WEAKREFS(object);
+ PyWeakReference *cur = *list;
+ if (cur != NULL) {
+ clear_weakref_lock_held(cur, &callback);
+ if (_Py_TryIncref((PyObject *) cur)) {
+ assert(num_items / 2 < num_weakrefs);
+ PyTuple_SET_ITEM(tuple, num_items, (PyObject *) cur);
+ PyTuple_SET_ITEM(tuple, num_items + 1, callback);
+ num_items += 2;
+ callback = NULL;
}
- for (i = 0; i < count; ++i) {
- PyObject *callback = PyTuple_GET_ITEM(tuple, i * 2 + 1);
+ }
+ done = (*list == NULL);
+ UNLOCK_WEAKREFS(object);
- /* The tuple may have slots left to NULL */
- if (callback != NULL) {
- PyObject *item = PyTuple_GET_ITEM(tuple, i * 2);
- handle_callback((PyWeakReference *)item, callback);
- }
- }
- Py_DECREF(tuple);
+ Py_XDECREF(callback);
+ }
+
+ for (Py_ssize_t i = 0; i < num_items; i += 2) {
+ PyObject *callback = PyTuple_GET_ITEM(tuple, i + 1);
+ if (callback != NULL) {
+ PyObject *weakref = PyTuple_GET_ITEM(tuple, i);
+ handle_callback((PyWeakReference *)weakref, callback);
}
- assert(!PyErr_Occurred());
- PyErr_SetRaisedException(exc);
+ }
+
+ Py_DECREF(tuple);
+
+ assert(!PyErr_Occurred());
+ PyErr_SetRaisedException(exc);
+}
+
+void
+PyUnstable_Object_ClearWeakRefsNoCallbacks(PyObject *obj)
+{
+ if (_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
+ _PyWeakref_ClearWeakRefsNoCallbacks(obj);
}
}
@@ -1019,12 +1081,32 @@ PyObject_ClearWeakRefs(PyObject *object)
void
_PyStaticType_ClearWeakRefs(PyInterpreterState *interp, PyTypeObject *type)
{
- static_builtin_state *state = _PyStaticType_GetState(interp, type);
+ managed_static_type_state *state = _PyStaticType_GetState(interp, type);
PyObject **list = _PyStaticType_GET_WEAKREFS_LISTPTR(state);
- while (*list != NULL) {
- /* Note that clear_weakref() pops the first ref off the type's
- weaklist before clearing its wr_object and wr_callback.
- That is how we're able to loop over the list. */
- clear_weakref((PyWeakReference *)*list);
+ // This is safe to do without holding the lock in free-threaded builds;
+ // there is only one thread running and no new threads can be created.
+ while (*list) {
+ _PyWeakref_ClearRef((PyWeakReference *)*list);
}
}
+
+void
+_PyWeakref_ClearWeakRefsNoCallbacks(PyObject *obj)
+{
+ /* Modeled after GET_WEAKREFS_LISTPTR().
+
+ This is never triggered for static types so we can avoid the
+ (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR(). */
+ PyWeakReference **list = _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(obj);
+ LOCK_WEAKREFS(obj);
+ while (*list) {
+ _PyWeakref_ClearRef(*list);
+ }
+ UNLOCK_WEAKREFS(obj);
+}
+
+int
+_PyWeakref_IsDead(PyObject *weakref)
+{
+ return _PyWeakref_IS_DEAD(weakref);
+}