GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

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

@eendebakpt eendebakpt commented May 22, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

(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

  • Issue: Audit all built-in modules for thread safety #116738
  • 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.

All reactions
@eendebakpt
Make the _json module thread safe
da0e917
@bedevere-app bedevere-app Bot added the awaiting review label May 22, 2024
@bedevere-app bedevere-app Bot mentioned this pull request May 22, 2024
Audit all built-in modules for thread safety #116738
Open
@nineteendo

nineteendo commented May 22, 2024

Copy link
Copy Markdown
Contributor

You need to include the file that defines that macro.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt
eendebakpt commented May 22, 2024
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt added 2 commits May 23, 2024 00:10
@eendebakpt
Update Modules/_json.c
3797dfa
@eendebakpt
handle goto and return statements
366654c
@eendebakpt eendebakpt mentioned this pull request May 24, 2024
PySequence_Fast needs new macros to be safe in a nogil world #119247
Closed
nineteendo
nineteendo reviewed May 25, 2024
View reviewed changes

@nineteendo nineteendo left a comment

Copy link
Copy Markdown
Contributor

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.

Revert newlines

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@eendebakpt @nineteendo
Apply suggestions from code review
5b72cdf
Co-authored-by: Nice Zombies <[email protected]>
eendebakpt
eendebakpt commented May 25, 2024
View reviewed changes
Comment thread Include/internal/pycore_critical_section.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt added 5 commits May 25, 2024 12:47
@eendebakpt
Update Include/internal/pycore_critical_section.h
c4c24c3
@eendebakpt
rename macro
370191b
@eendebakpt
Merge branch 'main' into json_ft
93c4466
@eendebakpt
fix typo
eafd3c1
@eendebakpt
Merge branch 'json_ft' of github.com:eendebakpt/cpython into json_ft
daeec46
@eendebakpt 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 eendebakpt changed the title gh-116738: Make _json module thread-safe #117530 gh-116738: Make _json module thread-safe May 31, 2024
eendebakpt and others added 4 commits June 4, 2024 15:26
@eendebakpt
fix missing to exit critical section
d54baf2
@eendebakpt
revert changes to tests
e5fa305
@blurb-it
📜🤖 Added by blurb_it.
d4ddf5d
@eendebakpt
Merge branch 'main' into json_ft
67d942f
@eendebakpt eendebakpt mentioned this pull request Jun 16, 2024
gh-120496: Make enum_iter thread safe #120591
Closed
@eendebakpt 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
eendebakpt added 6 commits August 14, 2024 22:30
@eendebakpt
Merge branch 'main' into json_ft
4ffc1b2
@eendebakpt
sync with main
384ca59
@eendebakpt
sync with main
64e20aa
@eendebakpt
update news entry
e6ce9c9
@eendebakpt
fix normal build
34885a0
@eendebakpt
Merge branch 'main' into json_ft
2fe760b
@eendebakpt
replace Py_IncRef with Py_INCREF
0ca453d
kumaraditya303
kumaraditya303 reviewed Aug 26, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt
eendebakpt commented Aug 27, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@eendebakpt
Update Modules/_json.c
45544f3
kumaraditya303
kumaraditya303 reviewed Aug 27, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

kumaraditya303 added 2 commits August 27, 2025 18:31
@kumaraditya303
Apply suggestion from @kumaraditya303
dcb088e
@kumaraditya303
add header
5f8ad07
kumaraditya303
kumaraditya303 reviewed Aug 27, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

kumaraditya303
kumaraditya303 reviewed Aug 27, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@eendebakpt @kumaraditya303
Apply suggestions from code review
61bc8d1
Co-authored-by: Kumar Aditya <[email protected]>
kumaraditya303
kumaraditya303 reviewed Aug 28, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

kumaraditya303
kumaraditya303 reviewed Aug 28, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt added 2 commits August 28, 2025 20:11
@eendebakpt
review comments
f85b695
@eendebakpt
use _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED
2c0cd18
kumaraditya303
kumaraditya303 reviewed Aug 29, 2025
View reviewed changes
Comment thread Lib/test/test_free_threading/test_json.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt added 2 commits August 29, 2025 16:37
@eendebakpt
remove testing code
c133b71
@eendebakpt
Merge branch 'main' into json_ft
7609117
kumaraditya303
kumaraditya303 reviewed Aug 29, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt added 4 commits August 30, 2025 11:36
@eendebakpt
add incref/decref to fast seq iteration
7656a2a
@eendebakpt
Merge branch 'json_ft' of github.com:eendebakpt/cpython into json_ft
1920aed
@eendebakpt
fix
d5d65ab
@eendebakpt
fix
4a16e18
kumaraditya303
kumaraditya303 reviewed Aug 30, 2025
View reviewed changes
Comment thread Modules/_json.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@kumaraditya303
Update Modules/_json.c
00139f3
kumaraditya303
kumaraditya303 approved these changes Aug 31, 2025
View reviewed changes

@kumaraditya303 kumaraditya303 left a comment

Copy link
Copy Markdown
Contributor

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.

Thanks!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@kumaraditya303
kumaraditya303 merged commit 4357302 into python:main Aug 31, 2025
@bedevere-app bedevere-app Bot removed the awaiting review label Aug 31, 2025
nineteendo
nineteendo reviewed Sep 1, 2025
View reviewed changes
Comment thread Lib/test/test_free_threading/test_json.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

lkollar pushed a commit to lkollar/cpython that referenced this pull request Sep 9, 2025
@eendebakpt @kumaraditya303 @lkollar
pythongh-116738: Make _json module thread-safe in the free-threading (
73f3eb3
python#119438)

Co-authored-by: Kumar Aditya <[email protected]>
@hugovk hugovk mentioned this pull request Oct 3, 2026
Enable missing TestJsonEncoding tests #158668
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@kumaraditya303 kumaraditya303 kumaraditya303 approved these changes
+1 more reviewer
@nineteendo nineteendo nineteendo left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

topic-free-threading

Projects

None yet

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.

3 participants

@eendebakpt @nineteendo @kumaraditya303
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.