Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Defer GC tracking of a :class:`frozendict` created by
:meth:`!frozendict.fromkeys` until the end of construction as possible.
32 changes: 22 additions & 10 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ 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 PyObject* dict_new_untracked(PyTypeObject *type);
static int dict_merge(PyObject *a, PyObject *b, int override, PyObject **dupkey);
static int dict_contains(PyObject *op, PyObject *key);
static int dict_merge_from_seq2(PyObject *d, PyObject *seq2, int override);
Expand Down Expand Up @@ -3419,22 +3420,25 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
PyObject *d;
int status;

PyTypeObject *cls_type = _PyType_CAST(cls);
d = _PyObject_CallNoArgs(cls);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a good way to obtain an untracked object here, that would be great, but I think that is a separate topic.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (d == NULL) {
return NULL;
}
// The constructor returns a tracked object; keep it untracked while it is
// filled and GC-track it once complete.
_PyObject_GC_UNTRACK(d);

// If cls is a dict or frozendict subclass with overridden constructor,
// copy the frozendict.
PyTypeObject *cls_type = _PyType_CAST(cls);
if (PyFrozenDict_Check(d) && cls_type->tp_new != frozendict_new) {
// Subclass-friendly copy
PyObject *copy;
if (PyObject_IsSubclass(cls, (PyObject*)&PyFrozenDict_Type)) {
copy = frozendict_new(cls_type, NULL, NULL);
copy = frozendict_new_untracked(cls_type);
}
else {
copy = dict_new(cls_type, NULL, NULL);
copy = dict_new_untracked(cls_type);
}
if (copy == NULL) {
Py_DECREF(d);
Expand All @@ -3456,23 +3460,23 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
Py_BEGIN_CRITICAL_SECTION2(d, iterable);
d = (PyObject *)dict_dict_fromkeys(mp, iterable, value);
Py_END_CRITICAL_SECTION2();
return d;
goto done;
}
else if (PyFrozenDict_CheckExact(iterable)) {
PyDictObject *mp = (PyDictObject *)d;

Py_BEGIN_CRITICAL_SECTION(d);
d = (PyObject *)dict_dict_fromkeys(mp, iterable, value);
Py_END_CRITICAL_SECTION();
return d;
goto done;
}
else if (PyAnySet_CheckExact(iterable)) {
PyDictObject *mp = (PyDictObject *)d;

Py_BEGIN_CRITICAL_SECTION2(d, iterable);
d = (PyObject *)dict_set_fromkeys(mp, iterable, value);
Py_END_CRITICAL_SECTION2();
return d;
goto done;
}
}
else if (PyFrozenDict_CheckExact(d)) {
Expand All @@ -3482,20 +3486,20 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
Py_BEGIN_CRITICAL_SECTION(iterable);
d = (PyObject *)dict_dict_fromkeys(mp, iterable, value);
Py_END_CRITICAL_SECTION();
return d;
goto done;
}
else if (PyFrozenDict_CheckExact(iterable)) {
PyDictObject *mp = (PyDictObject *)d;
d = (PyObject *)dict_dict_fromkeys(mp, iterable, value);
return d;
goto done;
}
else if (PyAnySet_CheckExact(iterable)) {
PyDictObject *mp = (PyDictObject *)d;

Py_BEGIN_CRITICAL_SECTION(iterable);
d = (PyObject *)dict_set_fromkeys(mp, iterable, value);
Py_END_CRITICAL_SECTION();
return d;
goto done;
}
}

Expand Down Expand Up @@ -3541,12 +3545,20 @@ dict_iter_exit:;
if (PyErr_Occurred())
goto Fail;
Py_DECREF(it);
return d;
goto done;

Fail:
Py_DECREF(it);
Py_DECREF(d);
return NULL;

done:
// Built untracked above; GC-track now that it is complete.
if (d != NULL) {
assert(!_PyObject_GC_IS_TRACKED(d));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tonghuaroot
By this, we don't need additional test code.

_PyObject_GC_TRACK(d);
}
return d;
}

/* Methods */
Expand Down
Loading