René's URL Explorer Experiment


Title: Protect the container walks in dumps() against concurrent mutation (#234) by espressolee · Pull Request #235 · python-rapidjson/python-rapidjson · GitHub

Open Graph Title: Protect the container walks in dumps() against concurrent mutation (#234) by espressolee · Pull Request #235 · python-rapidjson/python-rapidjson

X Title: Protect the container walks in dumps() against concurrent mutation (#234) by espressolee · Pull Request #235 · python-rapidjson/python-rapidjson

Description: Following up on #234 with the fix, since you said help would be welcome. The PR targets free-threaded, where Py_MOD_GIL_NOT_USED makes the concurrent paths reachable. The list hunk also fixes a pre-existing single-threaded callback re-entrancy crash on GIL builds, including released 1.23. Current head: 7eb5586a477535afbff4c356f9dc104cb6854d80. What it changes Four container walks in dumps() read the caller's object without holding anything: site fix why this one all_keys_are_string() critical section on the dict the loop calls no Python, so a section is enough and cheap. Restructured to a single exit — returning from inside a critical section would leak it the list walk in dumps_internal() re-read the length each iteration, PyList_GetItemRef() the unchecked macro over a size captured once reads past the end after a concurrent or re-entrant shrink; PyList_GetItemRef is bounds-checked and returns a strong reference the two dict walks in dumps_internal() on free-threaded builds, iterate a snapshot from PyDict_Items(); GIL builds retain PyDict_Next streaming a critical section is not sufficient on free-threaded builds: these bodies call back into Python (RECURSE, PyObject_Str), and a critical section is suspended when the thread blocks. A small RAII guard owns the snapshot so the eight existing early returns cannot leak it. The #ifdef Py_GIL_DISABLED boundary avoids charging default builds for a safety mechanism they do not need In rapidjson.cpp, the loop bodies are unchanged — only the headers and item acquisition move. Measured python3.14.0rc1t, module built from this branch, no PYTHON_GIL override, 10 runs per arm: reproducer before after mutate the dict being dumped (#234) 10/10 SIGSEGV 0/10 mutate the list being dumped 10/10 SIGSEGV 0/10 control — no mutator clean clean control — mutate a different object clean clean control — same test, PYTHON_GIL=1 clean clean The list bug also reproduces without concurrency: on released python-rapidjson 1.23, a single-threaded default= callback that shrinks the list being encoded crashed 5/5 on stock CPython 3.11, 3.12, and 3.13, and 10/10 on 3.14.6. Same-object controls were positive while no-mutation, different-object, append-only, and tuple controls were clean. This branch was 0/10 in both GIL and free-threaded modes for the same reproducer. pytest tests/ on the patched free-threaded build: 914 passed, 26 skipped, 2 xfailed. Snapshot cost follow-up Same interpreters, same data, nine timed samples per case, median ns/dump: build flat 16 flat 1024 nested 128×8 peak delta, flat 1024 GIL, conditional patch vs base -1.1% +0.4% +0.9% 0 bytes no-GIL, snapshot patch vs base +3.7% +1.9% +24.2% +65,592 bytes Compatibility/test matrix after the follow-up: Python 3.12.13 and 3.14.6 each 923 passed, 17 skipped, 2 xfailed; Python 3.14.0rc1t 914 passed, 26 skipped, 2 xfailed. Intermediate measurement, in case it is useful: with only the first two fixes applied the dict reproducer still crashed 8/10, which is what pinned the remaining crashes to the two dumps_internal dict walks rather than to all_keys_are_string. CI / exact head Exact-head workflow run 32349040222 completed successfully for 7eb5586a477535afbff4c356f9dc104cb6854d80: all 9 build/test jobs passed, including CPython 3.14t, debug leak tests, Linux x86_64, aarch64 and ppc64le, macOS arm64 and Intel, and Windows; the PyPI upload job was skipped on the pull request as intended; six wheel artifacts were produced. What I did not do No change to the Py_MOD_GIL_NOT_USED declaration — it is already on this branch and I did not touch it. With these four sites protected the ordering is now the safe way round; before them, the declaration was what removed the interpreter-level protection. The snapshot is free-threaded-only. A controlled microbenchmark found the uniform implementation cost 11.9–25.1% on GIL builds, with +65,592 bytes peak allocation for a 1,024-entry dict. The two PyDict_Items walks are therefore behind #ifdef Py_GIL_DISABLED; GIL builds retain PyDict_Next streaming. After that change, GIL timing was within -1.1% to +0.9% of base and peak allocation was identical. The free-threaded build still pays the snapshot cost, where it is the safety mechanism. No general dict re-entrancy claim. The list case above is a measured positive and is fixed by this branch. Five single-threaded dict callback/coercion/sorting mutation shapes stayed fault-free on patched and unpatched builds in both GIL modes; that bounds the claim to the tested inputs rather than proving no dict re-entrancy bug exists. Reshape any of this however you prefer — including rejecting the snapshot approach for the dict walks, which is the one real design choice in here. (Fix developed with AI assistance; the measurements are mine and reproducible.)

Open Graph Description: Following up on #234 with the fix, since you said help would be welcome. The PR targets free-threaded, where Py_MOD_GIL_NOT_USED makes the concurrent paths reachable. The list hunk also fixes a pre...

X Description: Following up on #234 with the fix, since you said help would be welcome. The PR targets free-threaded, where Py_MOD_GIL_NOT_USED makes the concurrent paths reachable. The list hunk also fixes a pre...

Opengraph URL: https://github.com/python-rapidjson/python-rapidjson/pull/235

X: @github

direct link

Domain: github.com

react-profiling0
react-import-mapreact,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf
route-pattern/:user_id/:repository/pull/:id/files(.:format)
route-controllerpull_requests
route-actionfiles
fetch-noncev2:6042da66-51ac-6be6-4f50-66127e5a46c5
current-catalog-service-hashae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b
request-idCAA0:B96CC:7C45A1:AD2A69:6AF85B2A
html-safe-noncebc60612a2ded10ded76b4720100c3b80d36338fdf6e0124c71e106728d21aecd
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDQUEwOkI5NkNDOjdDNDVBMTpBRDJBNjk6NkFGODVCMkEiLCJ2aXNpdG9yX2lkIjoiMTAyMzg3MDg4NjA1MTYwMTE5NCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9
visitor-hmacdeb535e6dadde3ecf287e169cc5445c919308fe02c7f159ebd965c58dfc519f7
hovercard-subject-tagpull_request:4220514956
github-keyboard-shortcutsrepository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///pull_requests/show/files
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/python-rapidjson/python-rapidjson/pull/235/files
twitter:imagehttps://avatars.githubusercontent.com/u/70549809?s=400&v=4
twitter:cardsummary_large_image
og:imagehttps://avatars.githubusercontent.com/u/70549809?s=400&v=4
og:image:altFollowing up on #234 with the fix, since you said help would be welcome. The PR targets free-threaded, where Py_MOD_GIL_NOT_USED makes the concurrent paths reachable. The list hunk also fixes a pre...
og:site_nameGitHub
og:typeobject
hostnamegithub.com
expected-hostnamegithub.com
None5713ecd83001b2f55c3509fb7a292bf7d66d0f167c7ae87a9f7eaec5a52bf8db
turbo-cache-controlno-preview
diff-viewunified
go-importgithub.com/python-rapidjson/python-rapidjson git https://github.com/python-rapidjson/python-rapidjson.git
octolytics-dimension-user_id25253642
octolytics-dimension-user_loginpython-rapidjson
octolytics-dimension-repository_id42155092
octolytics-dimension-repository_nwopython-rapidjson/python-rapidjson
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id42155092
octolytics-dimension-repository_network_root_nwopython-rapidjson/python-rapidjson
turbo-body-classeslogged-out env-production page-responsive full-width
disable-turbotrue
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release653be32ff5c755082a4db662491bfbbcfc9112c1
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/python-rapidjson/python-rapidjson/pull/235/files#start-of-content
https://github.com/
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fpython-rapidjson%2Fpython-rapidjson%2Fpull%2F235%2Ffiles
GitHubRepository hosting, code review, and collaborationhttps://github.com/platform
CopilotAI coding agent for tasks, projects, and automated workhttps://github.com/features/copilot
ActionsCI/CD and automationhttps://github.com/features/actions
Code QualityQuality analysis, fixes, and enforcementhttps://github.com/features/code-quality
CodespacesCloud development environmentshttps://github.com/features/codespaces
Code SecurityVulnerability detection and remediationhttps://github.com/security/advanced-security/code-security
Secret ProtectionSecret scanning and leak preventionhttps://github.com/security/advanced-security/secret-protection
All Featureshttps://github.com/features
Documentationhttps://docs.github.com/en
Traininghttps://learn.github.com/skills
Certificationshttps://learn.github.com/credentials
Build with Copilothttps://github.com/features/ai
Customize Copilothttps://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview
Integrationshttps://github.com/marketplace
Trending reposhttps://github.com/trending
GitHub Sponsorshttps://github.com/open-source/sponsors
Developer articleshttps://github.com/resources/articles
ChangelogYour source for the latest features, improvements, and fixes across GitHub products, all in one place.https://github.blog/changelog/
Enterpriseshttps://github.com/enterprise
Teamhttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
See all industrieshttps://github.com/solutions/industry
Why GitHubhttps://github.com/why-github
Customer Storieshttps://github.com/customer-stories
Business insightshttps://github.com/solutions/executive-insights
Trust Centerhttps://github.com/trust-center
GitHub Advanced SecurityEnterprise-grade code securityhttps://github.com/security/advanced-security
Copilot for businessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Documentationhttps://docs.github.com/en
Resource centerhttps://github.com/resources
Bloghttps://github.blog/
Events & webinarshttps://github.com/resources/events
Community Forumhttps://github.com/orgs/community/discussions
Customer Storieshttps://github.com/customer-stories
Integrationshttps://github.com/marketplace
Customer supporthttps://help.github.com/
Traininghttps://learn.github.com/skills
GitHub Statushttps://www.githubstatus.com/
Abouthttps://github.com/about
Partnershttps://github.com/partners
Changeloghttps://github.blog/changelog/
GitHub UniverseThe event for developers, by developers — talks, workshops, and what’s next for GitHub.https://githubuniverse.com/
Pricinghttps://github.com/pricing
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fpython-rapidjson%2Fpython-rapidjson%2Fpull%2F235%2Ffiles
Sign uphttps://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fpull_requests%2Fshow%2Ffiles&source=header-repo&source_repo=python-rapidjson%2Fpython-rapidjson
Reloadhttps://github.com/python-rapidjson/python-rapidjson/pull/235/files
Reloadhttps://github.com/python-rapidjson/python-rapidjson/pull/235/files
Reloadhttps://github.com/python-rapidjson/python-rapidjson/pull/235/files
python-rapidjsonhttps://github.com/python-rapidjson
python-rapidjsonhttps://github.com/python-rapidjson/python-rapidjson
Codehttps://github.com/python-rapidjson/python-rapidjson
Issues12 (12)https://github.com/python-rapidjson/python-rapidjson/issues
Pull requests7 (7)https://github.com/python-rapidjson/python-rapidjson/pulls
Actionshttps://github.com/python-rapidjson/python-rapidjson/actions
Projectshttps://github.com/python-rapidjson/python-rapidjson/projects
Wikihttps://github.com/python-rapidjson/python-rapidjson/wiki
Security and qualityhttps://github.com/python-rapidjson/python-rapidjson/security
Insightshttps://github.com/python-rapidjson/python-rapidjson/pulse
Sign up for GitHub https://github.com/signup?return_to=%2Fpython-rapidjson%2Fpython-rapidjson%2Fissues%2Fnew%2Fchoose
terms of servicehttps://docs.github.com/terms
privacy statementhttps://docs.github.com/privacy
Sign inhttps://github.com/login?return_to=%2Fpython-rapidjson%2Fpython-rapidjson%2Fissues%2Fnew%2Fchoose
espressoleehttps://github.com/espressolee
python-rapidjson:free-threadedhttps://github.com/python-rapidjson/python-rapidjson/tree/free-threaded
espressolee:ft-protect-container-walkshttps://github.com/espressolee/python-rapidjson/tree/ft-protect-container-walks
Conversation 7 https://github.com/python-rapidjson/python-rapidjson/pull/235
Commits 7 https://github.com/python-rapidjson/python-rapidjson/pull/235/commits
Checks 10 https://github.com/python-rapidjson/python-rapidjson/pull/235/checks
Files changed https://github.com/python-rapidjson/python-rapidjson/pull/235/files
Please reload this pagehttps://github.com/python-rapidjson/python-rapidjson/pull/235/files
Protect the container walks in dumps() against concurrent mutation (#234) https://github.com/python-rapidjson/python-rapidjson/pull/235/files#top
Show all changes 7 commits https://github.com/python-rapidjson/python-rapidjson/pull/235/files
9345f59 Protect the container walks in dumps() against concurrent mutation Aug 6, 2026 https://github.com/python-rapidjson/python-rapidjson/pull/235/commits/9345f59324cf23360eb8b268ed160e45e4b0389e
91f5d81 Clear IndexError when the list shrinks under the walk Aug 7, 2026 https://github.com/python-rapidjson/python-rapidjson/pull/235/commits/91f5d817d826ba44e0e1cd619630ec7f6ea80ba3
4fa6e62 Avoid dict snapshots on GIL builds espressolee Aug 20, 2026 https://github.com/python-rapidjson/python-rapidjson/pull/235/commits/4fa6e627d26af1ef69d3d181ddf99139089bbe46
d3a42da Merge remote-tracking branch 'upstream/free-threaded' into ft-protect… espressolee Aug 20, 2026 https://github.com/python-rapidjson/python-rapidjson/pull/235/commits/d3a42da0e4735bbe1867f4a0adb8c077942840b9
343dffd Support pre-3.13 Python builds espressolee Aug 20, 2026 https://github.com/python-rapidjson/python-rapidjson/pull/235/commits/343dffd4294e32ed514a4e21091282fdb728432f
db29d7b ci: replace retired macOS 13 runner espressolee Aug 20, 2026 https://github.com/python-rapidjson/python-rapidjson/pull/235/commits/db29d7b23ae6a180093b925c78d10e2cdc5f5aa8
7eb5586 ci: gate PyPI upload to release tags espressolee Aug 20, 2026 https://github.com/python-rapidjson/python-rapidjson/pull/235/commits/7eb5586a477535afbff4c356f9dc104cb6854d80
Clear filters https://github.com/python-rapidjson/python-rapidjson/pull/235/files
Please reload this pagehttps://github.com/python-rapidjson/python-rapidjson/pull/235/files
Please reload this pagehttps://github.com/python-rapidjson/python-rapidjson/pull/235/files
main.yml https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3
rapidjson.cpp https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
.github/workflows/main.ymlhttps://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3
View file https://github.com/python-rapidjson/python-rapidjson/blob/7eb5586a477535afbff4c356f9dc104cb6854d80/.github/workflows/main.yml
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/python-rapidjson/python-rapidjson/pull/235/{{ revealButtonHref }}
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3
rapidjson.cpphttps://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
View file https://github.com/python-rapidjson/python-rapidjson/blob/7eb5586a477535afbff4c356f9dc104cb6854d80/rapidjson.cpp
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/python-rapidjson/python-rapidjson/pull/235/{{ revealButtonHref }}
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
https://github.com/python-rapidjson/python-rapidjson/pull/235/files#diff-a1a8a73c0b8d9cda4a2ef82d368fd4b8376f745f66a7236cdfd61f14b97f329f
Please reload this pagehttps://github.com/python-rapidjson/python-rapidjson/pull/235/files
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.