Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
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
Uh oh!
There was an error while loading.Please reload this page.
Changes from41 commits
da0e9173797dfa366654c5b72cdfc4c24c3370191b93c4466eafd3c1daeec46d54baf2e5fa305d4ddf5d67d942f4ffc1b2384ca5964e20aae6ce9c934885a02fe760beebccacdb8947cc19ad148b12e0f78d35956e8615f39ebc007c5b185adf78c7acd0ad141e3dee75884cb0424c589c964f95acc99951733737d0056200f4d7fb95c07fcedc2c30ca453d45544f3dcb088e5f8ad0761bc8d1f85b6952c0cd18c133b7176091177656a2a1920aedd5d65ab4a16e1800139f3File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff 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() | ||
kumaraditya303 marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| 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__": | ||
kumaraditya303 marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| importtime | ||
| t0=time.time() | ||
| unittest.main() | ||
| dt=time.time()-t0 | ||
| print(f"Done:{dt:.2f}") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Make the module :mod:`json` safe to use under the free-threading build. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10,6 +10,7 @@ | ||
| #include "Python.h" | ||
| #include "pycore_ceval.h" // _Py_EnterRecursiveCall() | ||
| #include "pycore_critical_section.h" | ||
kumaraditya303 marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| #include "pycore_global_strings.h" // _Py_ID() | ||
| #include "pycore_pyerrors.h" // _PyErr_FormatNote | ||
| #include "pycore_runtime.h" // _PyRuntime | ||
| @@ -1456,7 +1457,7 @@ write_newline_indent(PyUnicodeWriter *writer, | ||
| static PyObject * | ||
| encoder_call(PyObject *op, PyObject *args, PyObject *kwds) | ||
| { | ||
| /* Python callable interface toencoder_listencode_obj */ | ||
| static char *kwlist[] = {"obj", "_current_indent_level", NULL}; | ||
| PyObject *obj; | ||
| Py_ssize_t indent_level; | ||
| @@ -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; | ||
kumaraditya303 marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) { | ||
| PyObject *item = PyList_GET_ITEM(items, i); | ||
kumaraditya303 marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| 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; | ||
kumaraditya303 marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| 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 | ||
eendebakpt marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| 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, | ||
| Py_ssize_t indent_level, PyObject *indent_cache) | ||
| { | ||
| /* Encode Python dict dct a JSON term */ | ||
| PyObject *ident = NULL; | ||
| bool first = true; | ||
| if (PyDict_GET_SIZE(dct) == 0) { | ||
| @@ -1788,34 +1844,30 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, | ||
| } | ||
| if (s->sort_keys || !PyDict_CheckExact(dct)) { | ||
| PyObject *items = PyMapping_Items(dct); | ||
| if (items == NULL || (s->sort_keys && PyList_Sort(items) < 0)) { | ||
| Py_XDECREF(items); | ||
| 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; | ||
| } | ||
| } else { | ||
| 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; | ||
| } | ||
| } | ||
| @@ -1837,22 +1889,52 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, | ||
| return 0; | ||
| bail: | ||
| 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); | ||
kumaraditya303 marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| #ifdef Py_GIL_DISABLED | ||
| // gh-119438: in the free-threading build the lock on s_fast can get suspended | ||
eendebakpt marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| 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; | ||
| 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) { | ||
| @@ -1890,16 +1972,13 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, | ||
| 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)) | ||
Uh oh!
There was an error while loading.Please reload this page.