GeezerVison is showing github.com — gh-116738: Make _json module safe in the free-threading build by eendebakpt · Pull Request #119438 · python/cpython
Conversation
eendebakpt
commented
May 22, 2024
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
(updated description)
Writing JSON files (or encoding to a string) is not thread-safe in the sense that when encoding data to json while another thread is mutating the data, the result is not well-defined (this is true for both the normal and free-threading build). But the free-threading build can crash the interpreter while writing JSON because of the usage of methods like PySequence_Fast_GET_ITEM. In this PR we make the free-threading build safe by adding locks in three places in the JSON encoder.
Reading from a JSON file is safe: objects constructed are only known to the executing thread. Encoding data to JSON needs a bit more care: mutable Python objects such as a list or a dict could be modified by another thread during encoding.
- When encoding a list use
Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST to project against mutation the list
- When encoding a dict, we use a critical section for iteration over exact dicts (
PyDict_Next is used there). The non-exact dicts use PyMapping_Items to create a list of tuples. PyMapping_Items itself is assumed to be thread safe, but the resulting list is not a copy and can be mutated.
Update 2025-02-10: refactored to avoid using Py_EXIT_CRITICAL_SECTION_SEQUENCE_FAST
- The script below was used to test the free-threading implementation. Similar code was added to the tests.
Test script
import json
from threading import Thread
import time
class JsonThreadingTest:
def __init__(self, number_of_threads=4, number_of_json_dumps=10):
self.data = [ [], [], {}, {}, {}]
self.json = {str(ii): d for ii, d in enumerate(self.data)}
self.results =[]
self.number_of_threads=number_of_threads
self.number_of_json_dumps =number_of_json_dumps
def modify(self, index):
while self.continue_thread:
for d in self.data:
if isinstance(d, list ):
if len(d)>20:
d.clear()
else:
d.append(index)
else:
if len(d)>20:
try:
d.pop(list(d)[0])
except KeyError:
pass
else:
if index%2:
d[index] = index
else:
d[bytes(index)] = bytes(index)
def test(self):
self.continue_thread = True
self.modifying_threads = []
for ii in range(self.number_of_threads):
t = Thread(target=self.modify, args=[ii])
self.modifying_threads.append(t)
self.results.clear()
for t in self.modifying_threads:
print(f'start {t}')
t.start()
for ii in range(self.number_of_json_dumps):
print(f'dump {ii}')
time.sleep(0.01)
indent = ii if ii%3==0 else None
if ii%5==0:
try:
j = json.dumps(self.data, indent=indent, skipkeys=True)
except TypeError:
pass
else:
j = json.dumps(self.data, indent=indent)
self.results.append(j)
self.continue_thread= False
print([hash(r) for r in self.results])
t=JsonThreadingTest(number_of_json_dumps=102, number_of_threads=8)
t0=time.time()
t.test()
dt=time.time()-t0
print(t.results[-1])
print(f'Done: {dt:.2f}')
- The test script with
t=JsonThreadingTest(number_of_json_dumps=102, number_of_threads=8) is a factor 25 faster using free-threading. Nice!
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
You need to include the file that defines that macro.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
eendebakpt
changed the title
Draft: gh-116738: Make _json module thread-safe #117530
gh-116738: Make _json module thread-safe #117530
May 31, 2024
eendebakpt
changed the title
gh-116738: Make _json module thread-safe #117530
gh-116738: Make _json module thread-safe
May 31, 2024
eendebakpt
changed the title
gh-116738: Make _json module thread-safe
gh-116738: Make _json module safe in the free-threading build
Aug 14, 2024
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Reviewers
+1 more reviewer
Reviewers whose approvals may not affect merge requirements
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|