From da0e917ed3cf2766c33b13b1edaed770805af529 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 22 May 2024 22:55:54 +0200 Subject: [PATCH 01/24] Make the _json module thread safe --- Modules/_json.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Modules/_json.c b/Modules/_json.c index e33ef1f5eea92f..8ef69420e305c4 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -10,6 +10,7 @@ #include "Python.h" #include "pycore_ceval.h" // _Py_EnterRecursiveCall() +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() #include "pycore_runtime.h" // _PyRuntime #include "pycore_global_strings.h" // _Py_ID() @@ -1615,12 +1616,14 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer, } else { Py_ssize_t pos = 0; + Py_BEGIN_CRITICAL_SECTION(dct); while (PyDict_Next(dct, &pos, &key, &value)) { if (encoder_encode_key_value(s, writer, &first, key, value, new_newline_indent, current_item_separator) < 0) goto bail; } + Py_END_CRITICAL_SECTION(dct); } if (ident != NULL) { @@ -1661,6 +1664,8 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, ident = NULL; s_fast = PySequence_Fast(seq, "_iterencode_list needs a sequence"); + Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(seq); + if (s_fast == NULL) return -1; if (PySequence_Fast_GET_SIZE(s_fast) == 0) { @@ -1713,6 +1718,7 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, if (encoder_listencode_obj(s, writer, obj, new_newline_indent)) goto bail; } + Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); if (ident != NULL) { if (PyDict_DelItem(s->markers, ident)) goto bail; From 3797dfac3cf046267b05df9e022e5946ebe6c914 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 23 May 2024 00:10:44 +0200 Subject: [PATCH 02/24] Update Modules/_json.c --- Modules/_json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_json.c b/Modules/_json.c index 8ef69420e305c4..16ab12c80384d1 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1623,7 +1623,7 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer, current_item_separator) < 0) goto bail; } - Py_END_CRITICAL_SECTION(dct); + Py_END_CRITICAL_SECTION(); } if (ident != NULL) { From 366654c196d928596023ffd72a7d6497ea7c83f0 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 24 May 2024 22:13:23 +0200 Subject: [PATCH 03/24] handle goto and return statements --- Include/internal/pycore_critical_section.h | 7 +++++++ Modules/_json.c | 14 ++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 7bebcdb67c7169..ce1640bd7a5a65 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -128,6 +128,12 @@ extern "C" { } \ } +# define Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(value) \ + if (_should_lock_cs) { \ + _PyCriticalSection_End(&_cs); \ + } \ + return value; + // Asserts that the mutex is locked. The mutex must be held by the // top-most critical section otherwise there's the possibility // that the mutex would be swalled out in some code paths. @@ -159,6 +165,7 @@ extern "C" { # define Py_END_CRITICAL_SECTION2() # define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) # define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() +# define Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(value) # define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex) # define _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op) #endif /* !Py_GIL_DISABLED */ diff --git a/Modules/_json.c b/Modules/_json.c index 16ab12c80384d1..f53756be775129 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1664,13 +1664,15 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, ident = NULL; s_fast = PySequence_Fast(seq, "_iterencode_list needs a sequence"); - Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(seq); - if (s_fast == NULL) return -1; + + Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(seq); + if (PySequence_Fast_GET_SIZE(s_fast) == 0) { Py_DECREF(s_fast); - return _PyUnicodeWriter_WriteASCIIString(writer, "[]", 2); + int r = _PyUnicodeWriter_WriteASCIIString(writer, "[]", 2); + Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(r); } if (s->markers != Py_None) { @@ -1718,13 +1720,11 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, if (encoder_listencode_obj(s, writer, obj, new_newline_indent)) goto bail; } - Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); if (ident != NULL) { if (PyDict_DelItem(s->markers, ident)) goto bail; Py_CLEAR(ident); } - if (s->indent != Py_None) { Py_CLEAR(new_newline_indent); Py_CLEAR(separator_indent); @@ -1736,14 +1736,16 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, if (_PyUnicodeWriter_WriteChar(writer, ']')) goto bail; Py_DECREF(s_fast); - return 0; + Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(0); bail: + Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); Py_XDECREF(ident); Py_DECREF(s_fast); Py_XDECREF(separator_indent); Py_XDECREF(new_newline_indent); return -1; + } static void From 5b72cdf3214357cd8908b30a21d59e4fa408da35 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 25 May 2024 12:45:41 +0200 Subject: [PATCH 04/24] Apply suggestions from code review Co-authored-by: Nice Zombies --- Modules/_json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_json.c b/Modules/_json.c index f53756be775129..a1f5c45df77f33 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1725,6 +1725,7 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, goto bail; Py_CLEAR(ident); } + if (s->indent != Py_None) { Py_CLEAR(new_newline_indent); Py_CLEAR(separator_indent); @@ -1745,7 +1746,6 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, Py_XDECREF(separator_indent); Py_XDECREF(new_newline_indent); return -1; - } static void From c4c24c38a3eabafe929b3dfdb3c602bd10dd11a0 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 25 May 2024 12:47:17 +0200 Subject: [PATCH 05/24] Update Include/internal/pycore_critical_section.h --- Include/internal/pycore_critical_section.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index ce1640bd7a5a65..91e0d6a15ee38f 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -165,7 +165,7 @@ extern "C" { # define Py_END_CRITICAL_SECTION2() # define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) # define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() -# define Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(value) +# define Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(value) return value; # define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex) # define _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op) #endif /* !Py_GIL_DISABLED */ From 370191b9335a93a618a491de3d89e56ae2512e4d Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 31 May 2024 23:31:09 +0200 Subject: [PATCH 06/24] rename macro --- Include/internal/pycore_critical_section.h | 7 +++---- Modules/_json.c | 7 ++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 91e0d6a15ee38f..8cb75a019fa13b 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -128,11 +128,10 @@ extern "C" { } \ } -# define Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(value) \ +# define Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST \ if (_should_lock_cs) { \ _PyCriticalSection_End(&_cs); \ - } \ - return value; + } // Asserts that the mutex is locked. The mutex must be held by the // top-most critical section otherwise there's the possibility @@ -165,7 +164,7 @@ extern "C" { # define Py_END_CRITICAL_SECTION2() # define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) # define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() -# define Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(value) return value; +# define Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST() # define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex) # define _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op) #endif /* !Py_GIL_DISABLED */ diff --git a/Modules/_json.c b/Modules/_json.c index a1f5c45df77f33..46adffa5a1e60d 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1671,8 +1671,8 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, if (PySequence_Fast_GET_SIZE(s_fast) == 0) { Py_DECREF(s_fast); - int r = _PyUnicodeWriter_WriteASCIIString(writer, "[]", 2); - Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(r); + Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST(); + return _PyUnicodeWriter_WriteASCIIString(writer, "[]", 2); } if (s->markers != Py_None) { @@ -1737,7 +1737,8 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, if (_PyUnicodeWriter_WriteChar(writer, ']')) goto bail; Py_DECREF(s_fast); - Py_RETURN_CRITICAL_SECTION_SEQUENCE_FAST(0); + Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST(); + return 0; bail: Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); From eafd3c1cd92b4e5abfd8e013584de6e9303a47b1 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 31 May 2024 23:34:34 +0200 Subject: [PATCH 07/24] fix typo --- Include/internal/pycore_critical_section.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 8cb75a019fa13b..a11af1bf0c0d8f 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -128,7 +128,7 @@ extern "C" { } \ } -# define Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST \ +# define Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST() \ if (_should_lock_cs) { \ _PyCriticalSection_End(&_cs); \ } From d54baf2049a71956b26c45e67fe31bf4601517e8 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 4 Jun 2024 15:26:06 +0200 Subject: [PATCH 08/24] fix missing to exit critical section --- Include/internal/pycore_critical_section.h | 4 ++++ Lib/test/test_json/test_dump.py | 4 +++- Modules/_json.c | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index a11af1bf0c0d8f..adebcae7e24076 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -99,6 +99,9 @@ extern "C" { _PyCriticalSection_End(&_cs); \ } +# define Py_EXIT_CRITICAL_SECTION() \ + _PyCriticalSection_End(&_cs); + # define Py_BEGIN_CRITICAL_SECTION2(a, b) \ { \ _PyCriticalSection2 _cs2; \ @@ -160,6 +163,7 @@ extern "C" { # define Py_BEGIN_CRITICAL_SECTION_MUT(mut) # define Py_BEGIN_CRITICAL_SECTION(op) # define Py_END_CRITICAL_SECTION() +# define Py_EXIT_CRITICAL_SECTION() # define Py_BEGIN_CRITICAL_SECTION2(a, b) # define Py_END_CRITICAL_SECTION2() # define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) diff --git a/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py index 13b40020781bae..fc052dfa37afb9 100644 --- a/Lib/test/test_json/test_dump.py +++ b/Lib/test/test_json/test_dump.py @@ -12,11 +12,13 @@ def test_dump(self): def test_dumps(self): self.assertEqual(self.dumps({}), '{}') - def test_dump_skipkeys(self): + def test_dump_skipkeys_invalid(self): v = {b'invalid_key': False, 'valid_key': True} with self.assertRaises(TypeError): self.json.dumps(v) + def test_dump_skipkeys(self): + v = {b'invalid_key': False, 'valid_key': True} s = self.json.dumps(v, skipkeys=True) o = self.json.loads(s) self.assertIn('valid_key', o) diff --git a/Modules/_json.c b/Modules/_json.c index d2b308eca36d3f..be8224e042d18a 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1620,8 +1620,10 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer, while (PyDict_Next(dct, &pos, &key, &value)) { if (encoder_encode_key_value(s, writer, &first, key, value, new_newline_indent, - current_item_separator) < 0) + current_item_separator) < 0) { + Py_EXIT_CRITICAL_SECTION(); goto bail; + } } Py_END_CRITICAL_SECTION(); } From e5fa305ad7842d71a552b9d97431e7b56a2604fb Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 4 Jun 2024 22:25:59 +0200 Subject: [PATCH 09/24] revert changes to tests --- Lib/test/test_json/test_dump.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py index fc052dfa37afb9..13b40020781bae 100644 --- a/Lib/test/test_json/test_dump.py +++ b/Lib/test/test_json/test_dump.py @@ -12,13 +12,11 @@ def test_dump(self): def test_dumps(self): self.assertEqual(self.dumps({}), '{}') - def test_dump_skipkeys_invalid(self): + def test_dump_skipkeys(self): v = {b'invalid_key': False, 'valid_key': True} with self.assertRaises(TypeError): self.json.dumps(v) - def test_dump_skipkeys(self): - v = {b'invalid_key': False, 'valid_key': True} s = self.json.dumps(v, skipkeys=True) o = self.json.loads(s) self.assertIn('valid_key', o) From d4ddf5dcab9f8f0b9bc5eede9f9d96f0b07d1df9 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:26:25 +0000 Subject: [PATCH 10/24] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst new file mode 100644 index 00000000000000..d7d64b8a8edc53 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst @@ -0,0 +1 @@ +Make the module :mod:`json` thread-safe. From 384ca59bf6ed251f8681c8ba9e0a6607a614c678 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 14 Aug 2024 22:37:12 +0200 Subject: [PATCH 11/24] sync with main --- Include/cpython/critical_section.h | 4 ++++ Include/internal/pycore_critical_section.h | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Include/cpython/critical_section.h b/Include/cpython/critical_section.h index 35db3fb6a59ce6..40862e90f22144 100644 --- a/Include/cpython/critical_section.h +++ b/Include/cpython/critical_section.h @@ -118,6 +118,10 @@ struct PyCriticalSection2 { PyCriticalSection _py_cs; \ PyCriticalSection_Begin(&_py_cs, _PyObject_CAST(op)) +# define Py_EXIT_CRITICAL_SECTION() \ + _PyCriticalSection_End(&_py_cs); + + # define Py_END_CRITICAL_SECTION() \ PyCriticalSection_End(&_py_cs); \ } diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 1e8440469dd404..76b18f4519c653 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -26,9 +26,6 @@ extern "C" { PyCriticalSection _py_cs; \ _PyCriticalSection_BeginMutex(&_py_cs, mutex) -# define Py_EXIT_CRITICAL_SECTION() \ - _PyCriticalSection_End(&_cs); - # define Py_BEGIN_CRITICAL_SECTION2_MUT(m1, m2) \ { \ PyCriticalSection2 _py_cs2; \ From 64e20aa80bffb98437775f8c21ec2299adf2b806 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 14 Aug 2024 22:41:51 +0200 Subject: [PATCH 12/24] sync with main --- Include/cpython/critical_section.h | 1 + Include/internal/pycore_critical_section.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/cpython/critical_section.h b/Include/cpython/critical_section.h index 40862e90f22144..d4c3ec9c67e504 100644 --- a/Include/cpython/critical_section.h +++ b/Include/cpython/critical_section.h @@ -85,6 +85,7 @@ PyCriticalSection2_End(PyCriticalSection2 *c); #ifndef Py_GIL_DISABLED # define Py_BEGIN_CRITICAL_SECTION(op) \ { +# define Py_EXIT_CRITICAL_SECTION() # define Py_END_CRITICAL_SECTION() \ } # define Py_BEGIN_CRITICAL_SECTION2(a, b) \ diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 76b18f4519c653..f19780754edcc1 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -83,7 +83,6 @@ extern "C" { # define Py_BEGIN_CRITICAL_SECTION_MUT(mut) { # define Py_BEGIN_CRITICAL_SECTION2_MUT(m1, m2) { # define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) { -# define Py_EXIT_CRITICAL_SECTION() # define Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST() # define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() } # define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex) From e6ce9c9936e71fefe351a6a2f0e8544216cfca9d Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 14 Aug 2024 22:43:32 +0200 Subject: [PATCH 13/24] update news entry --- .../2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst index d7d64b8a8edc53..8ae38e757ff925 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst @@ -1 +1 @@ -Make the module :mod:`json` thread-safe. +Make the module :mod:`json` safe to use under the free-theading build. From 34885a02f5dad38b7e3f98393f6cd382ef6b29dd Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 14 Aug 2024 22:50:17 +0200 Subject: [PATCH 14/24] fix normal build --- Modules/_json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_json.c b/Modules/_json.c index 5d6c635cc40f51..9fa5d1182c3073 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1748,8 +1748,8 @@ encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer, return 0; bail: - Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); Py_XDECREF(ident); + Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); Py_DECREF(s_fast); Py_XDECREF(separator_indent); Py_XDECREF(new_newline_indent); From eebccac0f005a83ed459af42a5a3c0f87d70ef02 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 15 Aug 2024 17:42:13 +0200 Subject: [PATCH 15/24] add lock around result of PyMapping_Items --- Modules/_json.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Modules/_json.c b/Modules/_json.c index 9fa5d1182c3073..93c207eac1406e 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1600,11 +1600,13 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer, if (items == NULL || (s->sort_keys && PyList_Sort(items) < 0)) goto bail; + Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(items); for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) { PyObject *item = PyList_GET_ITEM(items, i); if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) { PyErr_SetString(PyExc_ValueError, "items must return 2-tuples"); + Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST(); goto bail; } @@ -1612,9 +1614,12 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer, value = PyTuple_GET_ITEM(item, 1); if (encoder_encode_key_value(s, writer, &first, dct, key, value, new_newline_indent, - current_item_separator) < 0) + current_item_separator) < 0) { + Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST(); goto bail; + } } + Py_END_CRITICAL_SECTION_SEQUENCE_FAST(items); Py_CLEAR(items); } else { From db8947cef5acf1fd5751ac5f5e932e43a464e872 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 15 Aug 2024 20:44:03 +0200 Subject: [PATCH 16/24] add tests --- Lib/test/test_json/test_threading.py | 80 ++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Lib/test/test_json/test_threading.py diff --git a/Lib/test/test_json/test_threading.py b/Lib/test/test_json/test_threading.py new file mode 100644 index 00000000000000..ab07cceceda289 --- /dev/null +++ b/Lib/test/test_json/test_threading.py @@ -0,0 +1,80 @@ +import unittest +from threading import Thread +from test.test_json import CTest +from test.support import threading_helper + + +def encode_json_helper(json, worker, data, number_of_threads, number_of_json_encodings=100): + worker_threads = [] + for index in range(number_of_threads): + worker_threads.append(Thread(target=worker, args=[data, index])) + for t in worker_threads: + t.start() + for ii in range(number_of_json_encodings): + json.dumps(data) + data.clear() + for t in worker_threads: + t.join() + + +class MyMapping(dict): + def __init__(self): + self.mapping = [] + + def items(self): + return self.mapping + + +@threading_helper.reap_threads +@threading_helper.requires_working_threading() +class TestJsonEncoding(CTest): + # Test encoding json with multiple threads modifying the data cannot + # corrupt the interpreter + + def test_json_mutating_list(self): + + def worker(data, index): + while data: + for d in data: + if len(d) > 5: + d.clear() + else: + d.append(index) + d.append(index) + d.append(index) + encode_json_helper(self.json, worker, [[], []], number_of_threads=16) + + def test_json_mutating_dict(self): + + def worker(data, index): + while data: + for d in data: + if len(d) > 5: + try: + d.pop(list(d)[0]) + except (KeyError, IndexError): + pass + else: + d[index] = index + encode_json_helper(self.json, worker, [{}, {}], number_of_threads=16) + + def test_json_mutating_mapping(self): + + def worker(data, index): + while data: + for d in data: + if len(d.mapping) > 3: + d.mapping.clear() + else: + d.mapping.append((index, index)) + encode_json_helper(self.json, + worker, [MyMapping(), MyMapping()], number_of_threads=16) + + +if __name__ == "__main__": + import time + + t0 = time.time() + unittest.main() + dt = time.time()-t0 + print(f'Done: {dt:.2f}') From c19ad14886ac198a8329960bd1732d71c6bcd077 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 15 Aug 2024 20:55:38 +0200 Subject: [PATCH 17/24] fix argument of Py_END_CRITICAL_SECTION_SEQUENCE_FAST --- Modules/_json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_json.c b/Modules/_json.c index 93c207eac1406e..49b688d28b5c41 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1619,7 +1619,7 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer, goto bail; } } - Py_END_CRITICAL_SECTION_SEQUENCE_FAST(items); + Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); Py_CLEAR(items); } else { From 78d35951565b47f0c359276a5cbcb46e08170856 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 10 Feb 2025 11:51:04 +0100 Subject: [PATCH 18/24] avoid Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST --- Include/cpython/critical_section.h | 5 - Include/internal/pycore_critical_section.h | 6 - Modules/_json.c | 140 +++++++++++++-------- 3 files changed, 89 insertions(+), 62 deletions(-) diff --git a/Include/cpython/critical_section.h b/Include/cpython/critical_section.h index d4c3ec9c67e504..35db3fb6a59ce6 100644 --- a/Include/cpython/critical_section.h +++ b/Include/cpython/critical_section.h @@ -85,7 +85,6 @@ PyCriticalSection2_End(PyCriticalSection2 *c); #ifndef Py_GIL_DISABLED # define Py_BEGIN_CRITICAL_SECTION(op) \ { -# define Py_EXIT_CRITICAL_SECTION() # define Py_END_CRITICAL_SECTION() \ } # define Py_BEGIN_CRITICAL_SECTION2(a, b) \ @@ -119,10 +118,6 @@ struct PyCriticalSection2 { PyCriticalSection _py_cs; \ PyCriticalSection_Begin(&_py_cs, _PyObject_CAST(op)) -# define Py_EXIT_CRITICAL_SECTION() \ - _PyCriticalSection_End(&_py_cs); - - # define Py_END_CRITICAL_SECTION() \ PyCriticalSection_End(&_py_cs); \ } diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 4e088fe13e198b..e66d6d805c1b3b 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -51,11 +51,6 @@ extern "C" { } \ } -# define Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST() \ - if (_should_lock_cs) { \ - _PyCriticalSection_End(&_cs); \ - } - // Asserts that the mutex is locked. The mutex must be held by the // top-most critical section otherwise there's the possibility // that the mutex would be swalled out in some code paths. @@ -83,7 +78,6 @@ extern "C" { # define Py_BEGIN_CRITICAL_SECTION_MUT(mut) { # define Py_BEGIN_CRITICAL_SECTION2_MUT(m1, m2) { # define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) { -# define Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST() # define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() } # define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex) # define _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op) diff --git a/Modules/_json.c b/Modules/_json.c index 589f0b26698bf7..12be911d09f53f 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1634,6 +1634,52 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs return 0; } +static inline int +_encoder_iterate_mapping_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, + PyObject *dct, PyObject *items, + Py_ssize_t indent_level, PyObject *indent_cache, + PyObject *separator) +{ + PyObject *key, *value; + bool first = true; + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) { + PyObject *item = PyList_GET_ITEM(items, i); + + if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) { + PyErr_SetString(PyExc_ValueError, "items must return 2-tuples"); + return -1; + } + + key = PyTuple_GET_ITEM(item, 0); + value = PyTuple_GET_ITEM(item, 1); + if (encoder_encode_key_value(s, writer, &first, dct, key, value, + indent_level, indent_cache, + separator) < 0) { + return -1; + } + } + + return 0; +} + +static inline int +_encoder_iterate_dict_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, + PyObject *dct, Py_ssize_t indent_level, + PyObject *indent_cache, PyObject *separator) +{ + PyObject *key, *value; + Py_ssize_t pos = 0; + bool first = true; + while (PyDict_Next(dct, &pos, &key, &value)) { + if (encoder_encode_key_value(s, writer, &first, dct, key, value, + indent_level, indent_cache, + separator) < 0) { + return -1; + } + } + return 0; +} + static int encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *dct, @@ -1642,8 +1688,6 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, /* Encode Python dict dct a JSON term */ PyObject *ident = NULL; PyObject *items = NULL; - PyObject *key, *value; - bool first = true; if (PyDict_GET_SIZE(dct) == 0) { /* Fast path */ @@ -1683,46 +1727,30 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, if (s->sort_keys || !PyDict_CheckExact(dct)) { items = PyMapping_Items(dct); - if (items == NULL || (s->sort_keys && PyList_Sort(items) < 0)) + if (items == NULL || (s->sort_keys && PyList_Sort(items) < 0)) { goto bail; + } + int result; Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(items); - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) { - PyObject *item = PyList_GET_ITEM(items, i); - - if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) { - PyErr_SetString(PyExc_ValueError, "items must return 2-tuples"); - Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST(); - goto bail; - } - - key = PyTuple_GET_ITEM(item, 0); - value = PyTuple_GET_ITEM(item, 1); - if (encoder_encode_key_value(s, writer, &first, dct, key, value, - new_newline_indent, - current_item_separator) < 0) { - indent_level, indent_cache, - separator) < 0) - //Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST(); - goto bail; - } - } + result = _encoder_iterate_mapping_lock_held(s, writer, dct, + items, indent_level, indent_cache, separator); Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); Py_CLEAR(items); + if (result < 0) { + Py_XDECREF(items); + goto bail; + } } else { - Py_ssize_t pos = 0; + int result; Py_BEGIN_CRITICAL_SECTION(dct); - while (PyDict_Next(dct, &pos, &key, &value)) { - if (encoder_encode_key_value(s, writer, &first, dct, key, value, - indent_level, indent_cache, - separator) < 0) - //Py_EXIT_CRITICAL_SECTION(); - - goto bail; - } - } + result = _encoder_iterate_dict_lock_held(s, writer, dct, + indent_level, indent_cache, separator); Py_END_CRITICAL_SECTION(); + if (result < 0) { + goto bail; + } } if (ident != NULL) { @@ -1748,6 +1776,26 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, return -1; } +static inline int +_encoder_iterate_fast_seq_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, + PyObject *seq, PyObject *s_fast, + Py_ssize_t indent_level, PyObject *indent_cache, PyObject *separator) +{ + for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(s_fast); i++) { + PyObject *obj = PySequence_Fast_GET_ITEM(s_fast, i); + if (i) { + if (PyUnicodeWriter_WriteStr(writer, separator) < 0) { + return -1; + } + } + if (encoder_listencode_obj(s, writer, obj, indent_level, indent_cache)) { + _PyErr_FormatNote("when serializing %T item %zd", seq, i); + return -1; + } + } + return 0; +} + static int encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *seq, @@ -1755,18 +1803,13 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, { PyObject *ident = NULL; PyObject *s_fast = NULL; - Py_ssize_t i; - ident = NULL; - s_fast = PySequence_Fast(seq, "_iterencode_list needs a sequence"); + s_fast = PySequence_Fast(seq, "encoder_listencode_list needs a sequence"); if (s_fast == NULL) return -1; - Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(seq); - if (PySequence_Fast_GET_SIZE(s_fast) == 0) { Py_DECREF(s_fast); - Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST(); return PyUnicodeWriter_WriteUTF8(writer, "[]", 2); } @@ -1800,16 +1843,13 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, goto bail; } } - for (i = 0; i < PySequence_Fast_GET_SIZE(s_fast); i++) { - PyObject *obj = PySequence_Fast_GET_ITEM(s_fast, i); - if (i) { - if (PyUnicodeWriter_WriteStr(writer, separator) < 0) - goto bail; - } - if (encoder_listencode_obj(s, writer, obj, indent_level, indent_cache)) { - _PyErr_FormatNote("when serializing %T item %zd", seq, i); - goto bail; - } + int result; + Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(seq); + result = _encoder_iterate_fast_seq_lock_held(s, writer, seq, s_fast, + indent_level, indent_cache, separator); + Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); + if (result < -1) { + goto bail; } if (ident != NULL) { if (PyDict_DelItem(s->markers, ident)) @@ -1828,12 +1868,10 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, goto bail; } Py_DECREF(s_fast); - Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST(); return 0; bail: Py_XDECREF(ident); - Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); Py_DECREF(s_fast); return -1; } From 6e8615fc05420c6ade77fdea4a2cfb9f6b198eb5 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 10 Feb 2025 11:59:36 +0100 Subject: [PATCH 19/24] use barriers in test --- .../test_json.py} | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) rename Lib/test/{test_json/test_threading.py => test_free_threading/test_json.py} (85%) diff --git a/Lib/test/test_json/test_threading.py b/Lib/test/test_free_threading/test_json.py similarity index 85% rename from Lib/test/test_json/test_threading.py rename to Lib/test/test_free_threading/test_json.py index ab07cceceda289..192f2da0191501 100644 --- a/Lib/test/test_json/test_threading.py +++ b/Lib/test/test_free_threading/test_json.py @@ -1,13 +1,14 @@ import unittest -from threading import Thread +from threading import Barrier, Thread from test.test_json import CTest from test.support import threading_helper def encode_json_helper(json, worker, data, number_of_threads, number_of_json_encodings=100): worker_threads = [] + barrier = Barrier(number_of_threads) for index in range(number_of_threads): - worker_threads.append(Thread(target=worker, args=[data, index])) + worker_threads.append(Thread(target=worker, args=[barrier, data, index])) for t in worker_threads: t.start() for ii in range(number_of_json_encodings): @@ -33,7 +34,8 @@ class TestJsonEncoding(CTest): def test_json_mutating_list(self): - def worker(data, index): + def worker(barrier, data, index): + barrier.wait() while data: for d in data: if len(d) > 5: @@ -46,7 +48,8 @@ def worker(data, index): def test_json_mutating_dict(self): - def worker(data, index): + def worker(barrier, data, index): + barrier.wait() while data: for d in data: if len(d) > 5: @@ -60,7 +63,8 @@ def worker(data, index): def test_json_mutating_mapping(self): - def worker(data, index): + def worker(barrier, data, index): + barrier.wait() while data: for d in data: if len(d.mapping) > 3: From 39ebc007108591d0de2bfa2f7eb0452ce6952e70 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 10 Feb 2025 12:01:18 +0100 Subject: [PATCH 20/24] typo --- Modules/_json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_json.c b/Modules/_json.c index 12be911d09f53f..8425aa65277d51 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1848,7 +1848,7 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, result = _encoder_iterate_fast_seq_lock_held(s, writer, seq, s_fast, indent_level, indent_cache, separator); Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); - if (result < -1) { + if (result < 0) { goto bail; } if (ident != NULL) { From 7c5b18508005803855cb630d25d4dddabdf5649d Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 10 Feb 2025 12:04:48 +0100 Subject: [PATCH 21/24] whitespace --- Modules/_json.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_json.c b/Modules/_json.c index 8425aa65277d51..e685b051137d98 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1807,7 +1807,6 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, s_fast = PySequence_Fast(seq, "encoder_listencode_list needs a sequence"); if (s_fast == NULL) return -1; - if (PySequence_Fast_GET_SIZE(s_fast) == 0) { Py_DECREF(s_fast); return PyUnicodeWriter_WriteUTF8(writer, "[]", 2); From acd0ad1eb7ef1dba1a2bb1a4f80c5ba4de0f14f5 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 14:03:08 +0200 Subject: [PATCH 22/24] resolve merge conflicts --- Modules/_json.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Modules/_json.c b/Modules/_json.c index 7cfaeac918e52f..9d6ce0932b0bb5 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1639,12 +1639,11 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs static inline int _encoder_iterate_mapping_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, - PyObject *dct, PyObject *items, + bool *first, PyObject *dct, PyObject *items, Py_ssize_t indent_level, PyObject *indent_cache, PyObject *separator) { PyObject *key, *value; - bool first = true; for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) { PyObject *item = PyList_GET_ITEM(items, i); @@ -1655,7 +1654,7 @@ _encoder_iterate_mapping_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, key = PyTuple_GET_ITEM(item, 0); value = PyTuple_GET_ITEM(item, 1); - if (encoder_encode_key_value(s, writer, &first, dct, key, value, + if (encoder_encode_key_value(s, writer, first, dct, key, value, indent_level, indent_cache, separator) < 0) { return -1; @@ -1667,14 +1666,13 @@ _encoder_iterate_mapping_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, static inline int _encoder_iterate_dict_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, - PyObject *dct, Py_ssize_t indent_level, + bool *first, PyObject *dct, Py_ssize_t indent_level, PyObject *indent_cache, PyObject *separator) { PyObject *key, *value; Py_ssize_t pos = 0; - bool first = true; while (PyDict_Next(dct, &pos, &key, &value)) { - if (encoder_encode_key_value(s, writer, &first, dct, key, value, + if (encoder_encode_key_value(s, writer, first, dct, key, value, indent_level, indent_cache, separator) < 0) { return -1; @@ -1685,12 +1683,13 @@ _encoder_iterate_dict_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, static int encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, - PyObject *dct, + PyObject *dct, Py_ssize_t indent_level, PyObject *indent_cache) { /* Encode Python dict dct a JSON term */ PyObject *ident = NULL; PyObject *items = NULL; + bool first = true; if (PyDict_GET_SIZE(dct) == 0) { /* Fast path */ @@ -1733,7 +1732,7 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, int result; Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(items); - result = _encoder_iterate_mapping_lock_held(s, writer, dct, + result = _encoder_iterate_mapping_lock_held(s, writer, &first, dct, items, indent_level, indent_cache, separator); Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); Py_CLEAR(items); @@ -1745,7 +1744,7 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, } else { int result; Py_BEGIN_CRITICAL_SECTION(dct); - result = _encoder_iterate_dict_lock_held(s, writer, dct, + result = _encoder_iterate_dict_lock_held(s, writer, &first, dct, indent_level, indent_cache, separator); Py_END_CRITICAL_SECTION(); if (result < 0) { From 41e3deef334f7d67e823f52831412066493d2179 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 20:58:31 +0200 Subject: [PATCH 23/24] Update Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst --- .../2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst index 8ae38e757ff925..fe6a7229223de8 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst @@ -1 +1 @@ -Make the module :mod:`json` safe to use under the free-theading build. +Make the module :mod:`json` safe to use under the free-threading build. From 75884cbded1b9678031aef4e241eb251bd98843f Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 20:58:44 +0200 Subject: [PATCH 24/24] cleanup --- Lib/test/test_free_threading/test_json.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_free_threading/test_json.py b/Lib/test/test_free_threading/test_json.py index 192f2da0191501..4e5d727a8a0857 100644 --- a/Lib/test/test_free_threading/test_json.py +++ b/Lib/test/test_free_threading/test_json.py @@ -29,7 +29,7 @@ def items(self): @threading_helper.reap_threads @threading_helper.requires_working_threading() class TestJsonEncoding(CTest): - # Test encoding json with multiple threads modifying the data cannot + # Test encoding json with concurrent threads modifying the data cannot # corrupt the interpreter def test_json_mutating_list(self): @@ -59,6 +59,7 @@ def worker(barrier, data, index): pass else: d[index] = index + encode_json_helper(self.json, worker, [{}, {}], number_of_threads=16) def test_json_mutating_mapping(self): @@ -71,8 +72,9 @@ def worker(barrier, data, index): d.mapping.clear() else: d.mapping.append((index, index)) - encode_json_helper(self.json, - worker, [MyMapping(), MyMapping()], number_of_threads=16) + + data = [MyMapping(), MyMapping()] + encode_json_helper(self.json, worker, data, number_of_threads=16) if __name__ == "__main__":