// Forward declarations
static PyObject* frozendict_new(PyTypeObject *type, PyObject *args,
PyObject *kwds);
+static PyObject* frozendict_new_untracked(PyTypeObject *type);
static PyObject* dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static int dict_merge(PyObject *a, PyObject *b, int override, PyObject **dupkey);
static int dict_contains(PyObject *op, PyObject *key);
set_keys(mp, keys);
STORE_USED(mp, other->ma_used);
ASSERT_CONSISTENT(mp);
-
- if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) {
- /* Maintain tracking. */
- _PyObject_GC_TRACK(mp);
+ if (PyDict_Check(mp)) {
+ assert(_PyObject_GC_IS_TRACKED(mp));
}
-
return 0;
}
}
};
static PyObject *
-dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+dict_new_untracked(PyTypeObject *type)
{
assert(type != NULL);
- assert(type->tp_alloc != NULL);
// dict subclasses must implement the GC protocol
assert(_PyType_IS_GC(type));
- PyObject *self = type->tp_alloc(type, 0);
+ PyObject *self = _PyType_AllocNoTrack(type, 0);
if (self == NULL) {
return NULL;
}
d->ma_keys = Py_EMPTY_KEYS;
d->ma_values = NULL;
ASSERT_CONSISTENT(d);
- if (!_PyObject_GC_IS_TRACKED(d)) {
- _PyObject_GC_TRACK(d);
+ return self;
+}
+
+static PyObject *
+dict_new(PyTypeObject *type, PyObject *Py_UNUSED(args), PyObject *Py_UNUSED(kwds))
+{
+ /* tp_new ignores args/kwds; args/kwds are consumed by dict_init (tp_init). */
+ PyObject *self = dict_new_untracked(type);
+ if (self == NULL) {
+ return NULL;
}
+ assert(!_PyObject_GC_IS_TRACKED(self));
+ _PyObject_GC_TRACK(self);
return self;
}
return Py_NewRef(args[0]);
}
- PyObject *self = frozendict_new(_PyType_CAST(type), NULL, NULL);
+ /* gh-151722: Keep the frozendict untracked until it is fully built,
+ so a half-built object is never reachable from another thread (using the gc module). */
+ PyObject *self = frozendict_new_untracked(_PyType_CAST(type));
if (self == NULL) {
return NULL;
}
}
}
}
+ assert(!_PyObject_GC_IS_TRACKED(self));
+ _PyObject_GC_TRACK(self);
return self;
}
}
+/* Allocate an empty, GC-untracked frozendict; the constructor tracks it once
+ fully built. */
static PyObject *
-frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+frozendict_new_untracked(PyTypeObject *type)
{
- PyObject *d = dict_new(type, args, kwds);
+ PyObject *d = dict_new_untracked(type);
if (d == NULL) {
return NULL;
}
assert(can_modify_dict(_PyAnyDict_CAST(d)));
+ _PyFrozenDictObject_CAST(d)->ma_hash = -1;
+ return d;
+}
- PyFrozenDictObject *self = _PyFrozenDictObject_CAST(d);
- self->ma_hash = -1;
+static PyObject *
+frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyObject *d = frozendict_new_untracked(type);
+ if (d == NULL) {
+ return NULL;
+ }
if (args != NULL) {
if (dict_update_common(d, args, kwds, "frozendict") < 0) {
assert(kwds == NULL);
}
+ assert(!_PyObject_GC_IS_TRACKED(d));
+ _PyObject_GC_TRACK(d);
return d;
}