Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-116738: Make _json module safe in the free-threading build#119438

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
kumaraditya303 merged 53 commits intopython:mainfromeendebakpt:json_ft
Aug 31, 2025
Merged
Show file tree
Hide file tree
Changes from41 commits
Commits
Show all changes
53 commits
Select commitHold shift + click to select a range
da0e917
Make the _json module thread safe
eendebakptMay 22, 2024
3797dfa
Update Modules/_json.c
eendebakptMay 22, 2024
366654c
handle goto and return statements
eendebakptMay 24, 2024
5b72cdf
Apply suggestions from code review
eendebakptMay 25, 2024
c4c24c3
Update Include/internal/pycore_critical_section.h
eendebakptMay 25, 2024
370191b
rename macro
eendebakptMay 31, 2024
93c4466
Merge branch 'main' into json_ft
eendebakptMay 31, 2024
eafd3c1
fix typo
eendebakptMay 31, 2024
daeec46
Merge branch 'json_ft' of github.com:eendebakpt/cpython into json_ft
eendebakptMay 31, 2024
d54baf2
fix missing to exit critical section
eendebakptJun 4, 2024
e5fa305
revert changes to tests
eendebakptJun 4, 2024
d4ddf5d
📜🤖 Added by blurb_it.
blurb-it[bot]Jun 4, 2024
67d942f
Merge branch 'main' into json_ft
eendebakptJun 4, 2024
4ffc1b2
Merge branch 'main' into json_ft
eendebakptAug 14, 2024
384ca59
sync with main
eendebakptAug 14, 2024
64e20aa
sync with main
eendebakptAug 14, 2024
e6ce9c9
update news entry
eendebakptAug 14, 2024
34885a0
fix normal build
eendebakptAug 14, 2024
2fe760b
Merge branch 'main' into json_ft
eendebakptAug 14, 2024
eebccac
add lock around result of PyMapping_Items
eendebakptAug 15, 2024
db8947c
add tests
eendebakptAug 15, 2024
c19ad14
fix argument of Py_END_CRITICAL_SECTION_SEQUENCE_FAST
eendebakptAug 15, 2024
8b12e0f
Merge branch 'main' into json_ft
eendebakptFeb 10, 2025
78d3595
avoid Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST
eendebakptFeb 10, 2025
6e8615f
use barriers in test
eendebakptFeb 10, 2025
39ebc00
typo
eendebakptFeb 10, 2025
7c5b185
whitespace
eendebakptFeb 10, 2025
adf78c7
Merge branch 'main' into json_ft
eendebakptJun 24, 2025
acd0ad1
resolve merge conflicts
eendebakptJun 24, 2025
41e3dee
Update Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-20-26-21.gh-issu…
eendebakptJun 24, 2025
75884cb
cleanup
eendebakptJun 24, 2025
0424c58
Merge branch 'json_ft' of github.com:eendebakpt/cpython into json_ft
eendebakptJun 24, 2025
9c964f9
Merge branch 'main' into json_ft
eendebakptJun 24, 2025
5acc999
Merge branch 'main' into json_ft
eendebakptJul 28, 2025
5173373
format
eendebakptJul 28, 2025
7d00562
only use items locally in encoder_listencode_dict
eendebakptAug 6, 2025
00f4d7f
Merge branch 'main' into json_ft
eendebakptAug 14, 2025
b95c07f
use strong references in iteration
eendebakptAug 24, 2025
cedc2c3
Merge branch 'main' into json_ft
eendebakptAug 24, 2025
0ca453d
replace Py_IncRef with Py_INCREF
eendebakptAug 26, 2025
45544f3
Update Modules/_json.c
eendebakptAug 27, 2025
dcb088e
Apply suggestion from @kumaraditya303
kumaraditya303Aug 27, 2025
5f8ad07
add header
kumaraditya303Aug 27, 2025
61bc8d1
Apply suggestions from code review
eendebakptAug 27, 2025
f85b695
review comments
eendebakptAug 28, 2025
2c0cd18
use _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED
eendebakptAug 28, 2025
c133b71
remove testing code
eendebakptAug 29, 2025
7609117
Merge branch 'main' into json_ft
eendebakptAug 29, 2025
7656a2a
add incref/decref to fast seq iteration
eendebakptAug 30, 2025
1920aed
Merge branch 'json_ft' of github.com:eendebakpt/cpython into json_ft
eendebakptAug 30, 2025
d5d65ab
fix
eendebakptAug 30, 2025
4a16e18
fix
eendebakptAug 30, 2025
00139f3
Update Modules/_json.c
kumaraditya303Aug 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletionsLib/test/test_free_threading/test_json.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
importunittest
fromthreadingimportBarrier,Thread
fromtest.test_jsonimportCTest
fromtest.supportimportthreading_helper


defencode_json_helper(
json,worker,data,number_of_threads=12,number_of_json_encodings=100
):
worker_threads= []
barrier=Barrier(number_of_threads)
forindexinrange(number_of_threads):
worker_threads.append(
Thread(target=worker,args=[barrier,data,index])
)
fortinworker_threads:
t.start()
foriiinrange(number_of_json_encodings):
json.dumps(data)
data.clear()
fortinworker_threads:
t.join()


classMyMapping(dict):
def__init__(self):
self.mapping= []

defitems(self):
returnself.mapping


@threading_helper.reap_threads
@threading_helper.requires_working_threading()
classTestJsonEncoding(CTest):
# Test encoding json with concurrent threads modifying the data cannot
# corrupt the interpreter

deftest_json_mutating_list(self):
defworker(barrier,data,index):
barrier.wait()
whiledata:
fordindata:
iflen(d)>5:
d.clear()
else:
d.append(index)

data= [[], []]
encode_json_helper(self.json,worker,data)

deftest_json_mutating_exact_dict(self):
defworker(barrier,data,index):
barrier.wait()
whiledata:
fordindata:
iflen(d)>5:
try:
key=list(d)[0]
d.pop()
except (KeyError,IndexError):
pass
else:
d[index]=index

data= [{}, {}]
encode_json_helper(self.json,worker,data)

deftest_json_mutating_mapping(self):
defworker(barrier,data,index):
barrier.wait()
whiledata:
fordindata:
iflen(d.mapping)>3:
d.mapping.clear()
else:
d.mapping.append((index,index))

data= [MyMapping(),MyMapping()]
encode_json_helper(self.json,worker,data)


if__name__=="__main__":
importtime
t0=time.time()
unittest.main()
dt=time.time()-t0
print(f"Done:{dt:.2f}")
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
Make the module :mod:`json` safe to use under the free-threading build.
161 changes: 120 additions & 41 deletionsModules/_json.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@

#include "Python.h"
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
#include "pycore_critical_section.h"
#include "pycore_global_strings.h" // _Py_ID()
#include "pycore_pyerrors.h" // _PyErr_FormatNote
#include "pycore_runtime.h" // _PyRuntime
Expand DownExpand Up@@ -1456,7 +1457,7 @@ write_newline_indent(PyUnicodeWriter *writer,
static PyObject *
encoder_call(PyObject *op, PyObject *args, PyObject *kwds)
{
/* Python callable interface toencode_listencode_obj */
/* Python callable interface toencoder_listencode_obj */
static char *kwlist[] = {"obj", "_current_indent_level", NULL};
PyObject *obj;
Py_ssize_t indent_level;
Expand DownExpand Up@@ -1743,15 +1744,70 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs
return 0;
}

static inline int
_encoder_iterate_mapping_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer,
bool *first, PyObject *dct, PyObject *items,
Py_ssize_t indent_level, PyObject *indent_cache,
PyObject *separator)
{
PyObject *key, *value;
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,
bool *first, PyObject *dct, Py_ssize_t indent_level,
PyObject *indent_cache, PyObject *separator)
{
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(dct, &pos, &key, &value)) {
#ifdef Py_GIL_DISABLED
// gh-119438: in the free-threading build the lock on dct can get suspended
Py_INCREF(key);
Py_INCREF(value);
#endif
if (encoder_encode_key_value(s, writer, first, dct, key, value,
indent_level, indent_cache,
separator) < 0) {
#ifdef Py_GIL_DISABLED
Py_DECREF(key);
Py_DECREF(value);
#endif
return -1;
}
#ifdef Py_GIL_DISABLED
Py_DECREF(key);
Py_DECREF(value);
#endif
}
return 0;
}

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;
PyObject *key, *value;
bool first = true;

if (PyDict_GET_SIZE(dct) == 0) {
Expand DownExpand Up@@ -1788,34 +1844,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))
PyObject *items = PyMapping_Items(dct);
if (items == NULL || (s->sort_keys && PyList_Sort(items) < 0)) {
Py_XDECREF(items);
goto bail;
}

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");
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,
indent_level, indent_cache,
separator) < 0)
goto bail;
int result;
Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(items);
result = _encoder_iterate_mapping_lock_held(s, writer, &first, dct,
items, indent_level, indent_cache, separator);
Py_END_CRITICAL_SECTION_SEQUENCE_FAST();
Py_DECREF(items);
if (result < 0) {
goto bail;
}
Py_CLEAR(items);

} else {
Py_ssize_t pos = 0;
while (PyDict_Next(dct, &pos, &key, &value)) {
if (encoder_encode_key_value(s, writer, &first, dct, key, value,
indent_level, indent_cache,
separator) < 0)
goto bail;
int result;
Py_BEGIN_CRITICAL_SECTION(dct);
result = _encoder_iterate_dict_lock_held(s, writer, &first, dct,
indent_level, indent_cache, separator);
Py_END_CRITICAL_SECTION();
if (result < 0) {
goto bail;
}
}

Expand All@@ -1837,22 +1889,52 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer,
return 0;

bail:
Py_XDECREF(items);
Py_XDECREF(ident);
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);
#ifdef Py_GIL_DISABLED
// gh-119438: in the free-threading build the lock on s_fast can get suspended
Py_INCREF(obj);
#endif
if (i) {
if (PyUnicodeWriter_WriteStr(writer, separator) < 0) {
#ifdef Py_GIL_DISABLED
Py_DECREF(obj);
#endif
return -1;
}
}
if (encoder_listencode_obj(s, writer, obj, indent_level, indent_cache)) {
_PyErr_FormatNote("when serializing %T item %zd", seq, i);
#ifdef Py_GIL_DISABLED
Py_DECREF(obj);
#endif
return -1;
}
#ifdef Py_GIL_DISABLED
Py_DECREF(obj);
#endif
}
return 0;
}

static int
encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer,
PyObject *seq,
Py_ssize_t indent_level, PyObject *indent_cache)
{
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;
if (PySequence_Fast_GET_SIZE(s_fast) == 0) {
Expand DownExpand Up@@ -1890,16 +1972,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 < 0) {
goto bail;
}
if (ident != NULL) {
if (PyDict_DelItem(s->markers, ident))
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp