Repository navigation
gh-151722: Defer GC tracking of frozendict to end of construction #151740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
cd1b1a4
399d810
286eafd
d74be81
897bdec
69a6316
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Defer GC tracking of :class:`frozendict` to end of construction. Patch by | ||
| Donghee Na. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -138,6 +138,7 @@ As a consequence of this, split keys have a maximum size of 16. | |||||
| // 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); | ||||||
|
|
@@ -5242,7 +5243,7 @@ static PyNumberMethods dict_as_number = { | |||||
| }; | ||||||
|
|
||||||
| static PyObject * | ||||||
| dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
| dict_new_untracked(PyTypeObject *type) | ||||||
| { | ||||||
| assert(type != NULL); | ||||||
| assert(type->tp_alloc != NULL); | ||||||
|
|
@@ -5262,8 +5263,18 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |||||
| 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 *args, PyObject *kwds) | ||||||
| { | ||||||
| PyObject *self = dict_new_untracked(type); | ||||||
| if (self == NULL) { | ||||||
| return NULL; | ||||||
| } | ||||||
| if (!_PyObject_GC_IS_TRACKED(self)) { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we don't need it |
||||||
| _PyObject_GC_TRACK(self); | ||||||
| } | ||||||
| return self; | ||||||
| } | ||||||
|
|
@@ -5323,7 +5334,9 @@ frozendict_vectorcall(PyObject *type, PyObject * const*args, | |||||
| return Py_NewRef(args[0]); | ||||||
| } | ||||||
|
|
||||||
| PyObject *self = frozendict_new(_PyType_CAST(type), NULL, NULL); | ||||||
| /* Keep the frozendict untracked until it is fully built, so a half-built | ||||||
| object is never reachable from another thread. */ | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
You may add a reference to the issue gh-151722. |
||||||
| PyObject *self = frozendict_new_untracked(_PyType_CAST(type)); | ||||||
| if (self == NULL) { | ||||||
| return NULL; | ||||||
| } | ||||||
|
|
@@ -5343,6 +5356,10 @@ frozendict_vectorcall(PyObject *type, PyObject * const*args, | |||||
| } | ||||||
| } | ||||||
| } | ||||||
| /* Track only once fully built. */ | ||||||
| if (!_PyObject_GC_IS_TRACKED(self)) { | ||||||
| _PyObject_GC_TRACK(self); | ||||||
| } | ||||||
| return self; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -8361,17 +8378,28 @@ frozendict_hash(PyObject *op) | |||||
| } | ||||||
|
|
||||||
|
|
||||||
| /* Allocate an empty, GC-untracked frozendict. Staying untracked while it is | ||||||
| filled keeps a half-built frozendict so another thread can't observe it | ||||||
| changing. Callers must GC-track 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) { | ||||||
|
|
@@ -8383,6 +8411,10 @@ frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |||||
| assert(kwds == NULL); | ||||||
| } | ||||||
|
|
||||||
| /* Track only once fully built. */ | ||||||
| if (!_PyObject_GC_IS_TRACKED(d)) { | ||||||
| _PyObject_GC_TRACK(d); | ||||||
| } | ||||||
| return d; | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is strange. Why does it ignore its parameters?
Creating a dict with arguments should create a dict from those arguments.
This comment was marked as outdated.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cpython/Objects/dictobject.c
Lines 5245 to 5269 in aa5b164
The function already ignore those params, and it was done by
dict_initThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed:
but
frozendictis inconsistent:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that than we need to modify frozenset also
Let's handle this at separate issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see what's wrong here. For me, it works as expected:
The fact that arguments are used by
__new__()or__init__()is just an implementation detail. I don't think that it matters for the "constructor" API.