* Returns 0 on success, -1 on error.
*
* Note that this currently doesn't work for protocol 0.
+
+ * gh-146452: Wrap the dict iteration in a critical sections to prevent
+ * concurrent mutation from invalidating PyDict_Next() iteration state.
*/
static int
-batch_dict_exact_impl(PickleState *state, PicklerObject *self, PyObject *obj)
+batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
{
PyObject *key = NULL, *value = NULL;
int i;
/* Special-case len(d) == 1 to save space. */
if (dict_size == 1) {
- PyDict_Next(obj, &ppos, &key, &value);
- Py_INCREF(key);
- Py_INCREF(value);
+ int next;
+ Py_BEGIN_CRITICAL_SECTION(obj);
+ next = PyDict_Next(obj, &ppos, &key, &value);
+ if (next) {
+ Py_INCREF(key);
+ Py_INCREF(value);
+ }
+ Py_END_CRITICAL_SECTION();
+ if (!next) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "dictionary changed size during iteration");
+ goto error;
+ }
if (save(state, self, key, 0) < 0) {
goto error;
}
i = 0;
if (_Pickler_Write(self, &mark_op, 1) < 0)
return -1;
- while (PyDict_Next(obj, &ppos, &key, &value)) {
- Py_INCREF(key);
- Py_INCREF(value);
+ int next;
+ while (1) {
+ Py_BEGIN_CRITICAL_SECTION(obj);
+ next = PyDict_Next(obj, &ppos, &key, &value);
+ if (next) {
+ Py_INCREF(key);
+ Py_INCREF(value);
+ }
+ Py_END_CRITICAL_SECTION();
+ if (!next) {
+ break;
+ }
if (save(state, self, key, 0) < 0) {
goto error;
}
return -1;
}
-/* gh-146452: Wrap the dict iteration in a critical section to prevent
- concurrent mutation from invalidating PyDict_Next() iteration state. */
-static int
-batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
-{
- int ret;
- Py_BEGIN_CRITICAL_SECTION(obj);
- ret = batch_dict_exact_impl(state, self, obj);
- Py_END_CRITICAL_SECTION();
- return ret;
-}
-
static int
save_dict(PickleState *state, PicklerObject *self, PyObject *obj)
{