Skip to content

Commit cd1b1a4

Browse files
committed
gh-151722: Defer GC tracking of frozendict to end of construction
1 parent 23793ac commit cd1b1a4

2 files changed

Lines changed: 42 additions & 8 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Defer GC tracking of :class:`frozendict` to end of construction. Patch by
2+
Donghee Na.

Objects/dictobject.c

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ As a consequence of this, split keys have a maximum size of 16.
138138
// Forward declarations
139139
static PyObject* frozendict_new(PyTypeObject *type, PyObject *args,
140140
PyObject *kwds);
141+
static PyObject* frozendict_new_untracked(PyTypeObject *type);
141142
static PyObject* dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
142143
static int dict_merge(PyObject *a, PyObject *b, int override, PyObject **dupkey);
143144
static int dict_contains(PyObject *op, PyObject *key);
@@ -5242,7 +5243,7 @@ static PyNumberMethods dict_as_number = {
52425243
};
52435244

52445245
static PyObject *
5245-
dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
5246+
dict_new_untracked(PyTypeObject *type)
52465247
{
52475248
assert(type != NULL);
52485249
assert(type->tp_alloc != NULL);
@@ -5262,8 +5263,18 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
52625263
d->ma_keys = Py_EMPTY_KEYS;
52635264
d->ma_values = NULL;
52645265
ASSERT_CONSISTENT(d);
5265-
if (!_PyObject_GC_IS_TRACKED(d)) {
5266-
_PyObject_GC_TRACK(d);
5266+
return self;
5267+
}
5268+
5269+
static PyObject *
5270+
dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
5271+
{
5272+
PyObject *self = dict_new_untracked(type);
5273+
if (self == NULL) {
5274+
return NULL;
5275+
}
5276+
if (!_PyObject_GC_IS_TRACKED(self)) {
5277+
_PyObject_GC_TRACK(self);
52675278
}
52685279
return self;
52695280
}
@@ -5323,7 +5334,9 @@ frozendict_vectorcall(PyObject *type, PyObject * const*args,
53235334
return Py_NewRef(args[0]);
53245335
}
53255336

5326-
PyObject *self = frozendict_new(_PyType_CAST(type), NULL, NULL);
5337+
/* Keep the frozendict untracked until it is fully built, so a half-built
5338+
object is never reachable from another thread. */
5339+
PyObject *self = frozendict_new_untracked(_PyType_CAST(type));
53275340
if (self == NULL) {
53285341
return NULL;
53295342
}
@@ -5343,6 +5356,10 @@ frozendict_vectorcall(PyObject *type, PyObject * const*args,
53435356
}
53445357
}
53455358
}
5359+
/* Track only once fully built. */
5360+
if (!_PyObject_GC_IS_TRACKED(self)) {
5361+
_PyObject_GC_TRACK(self);
5362+
}
53465363
return self;
53475364
}
53485365

@@ -8361,17 +8378,28 @@ frozendict_hash(PyObject *op)
83618378
}
83628379

83638380

8381+
/* Allocate an empty, GC-untracked frozendict. Staying untracked while it is
8382+
filled keeps a half-built frozendict so another thread can't observe it
8383+
changing. Callers must GC-track it once fully built. */
83648384
static PyObject *
8365-
frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
8385+
frozendict_new_untracked(PyTypeObject *type)
83668386
{
8367-
PyObject *d = dict_new(type, args, kwds);
8387+
PyObject *d = dict_new_untracked(type);
83688388
if (d == NULL) {
83698389
return NULL;
83708390
}
83718391
assert(can_modify_dict(_PyAnyDict_CAST(d)));
8392+
_PyFrozenDictObject_CAST(d)->ma_hash = -1;
8393+
return d;
8394+
}
83728395

8373-
PyFrozenDictObject *self = _PyFrozenDictObject_CAST(d);
8374-
self->ma_hash = -1;
8396+
static PyObject *
8397+
frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
8398+
{
8399+
PyObject *d = frozendict_new_untracked(type);
8400+
if (d == NULL) {
8401+
return NULL;
8402+
}
83758403

83768404
if (args != NULL) {
83778405
if (dict_update_common(d, args, kwds, "frozendict") < 0) {
@@ -8383,6 +8411,10 @@ frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
83838411
assert(kwds == NULL);
83848412
}
83858413

8414+
/* Track only once fully built. */
8415+
if (!_PyObject_GC_IS_TRACKED(d)) {
8416+
_PyObject_GC_TRACK(d);
8417+
}
83868418
return d;
83878419
}
83888420

0 commit comments

Comments
 (0)