Skip to content

Commit b770b23

Browse files
authored
Revert "gh-146452: Improve locking granularity in pickle's batch_dict_exact and fix race condition (#150025)" (#150261)
This reverts commit 57a0e57.
1 parent de7c3bf commit b770b23

2 files changed

Lines changed: 20 additions & 31 deletions

File tree

Misc/NEWS.d/next/Library/2026-05-18-15-30-34.gh-issue-146452.RM0EVJ.rst

Lines changed: 0 additions & 2 deletions
This file was deleted.

Modules/_pickle.c

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3463,12 +3463,9 @@ batch_dict(PickleState *state, PicklerObject *self, PyObject *iter, PyObject *or
34633463
* Returns 0 on success, -1 on error.
34643464
*
34653465
* Note that this currently doesn't work for protocol 0.
3466-
3467-
* gh-146452: Wrap the dict iteration in a critical sections to prevent
3468-
* concurrent mutation from invalidating PyDict_Next() iteration state.
34693466
*/
34703467
static int
3471-
batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
3468+
batch_dict_exact_impl(PickleState *state, PicklerObject *self, PyObject *obj)
34723469
{
34733470
PyObject *key = NULL, *value = NULL;
34743471
int i;
@@ -3482,24 +3479,15 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
34823479
assert(self->proto > 0);
34833480

34843481
dict_size = PyDict_GET_SIZE(obj);
3482+
assert(dict_size);
34853483

34863484
/* Write in batches of BATCHSIZE. */
34873485
Py_ssize_t total = 0;
34883486
do {
34893487
if (dict_size - total == 1) {
3490-
int next;
3491-
Py_BEGIN_CRITICAL_SECTION(obj);
3492-
next = PyDict_Next(obj, &ppos, &key, &value);
3493-
if (next) {
3494-
Py_INCREF(key);
3495-
Py_INCREF(value);
3496-
}
3497-
Py_END_CRITICAL_SECTION();
3498-
if (!next) {
3499-
PyErr_SetString(PyExc_RuntimeError,
3500-
"dictionary changed size during iteration");
3501-
goto error;
3502-
}
3488+
PyDict_Next(obj, &ppos, &key, &value);
3489+
Py_INCREF(key);
3490+
Py_INCREF(value);
35033491
if (save(state, self, key, 0) < 0) {
35043492
goto error;
35053493
}
@@ -3517,18 +3505,9 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
35173505
i = 0;
35183506
if (_Pickler_Write(self, &mark_op, 1) < 0)
35193507
return -1;
3520-
int next;
3521-
while (1) {
3522-
Py_BEGIN_CRITICAL_SECTION(obj);
3523-
next = PyDict_Next(obj, &ppos, &key, &value);
3524-
if (next) {
3525-
Py_INCREF(key);
3526-
Py_INCREF(value);
3527-
}
3528-
Py_END_CRITICAL_SECTION();
3529-
if (!next) {
3530-
break;
3531-
}
3508+
while (PyDict_Next(obj, &ppos, &key, &value)) {
3509+
Py_INCREF(key);
3510+
Py_INCREF(value);
35323511
if (save(state, self, key, 0) < 0) {
35333512
goto error;
35343513
}
@@ -3559,6 +3538,18 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
35593538
return -1;
35603539
}
35613540

3541+
/* gh-146452: Wrap the dict iteration in a critical section to prevent
3542+
concurrent mutation from invalidating PyDict_Next() iteration state. */
3543+
static int
3544+
batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
3545+
{
3546+
int ret;
3547+
Py_BEGIN_CRITICAL_SECTION(obj);
3548+
ret = batch_dict_exact_impl(state, self, obj);
3549+
Py_END_CRITICAL_SECTION();
3550+
return ret;
3551+
}
3552+
35623553
static int
35633554
save_dict(PickleState *state, PicklerObject *self, PyObject *obj)
35643555
{

0 commit comments

Comments
 (0)