Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
g_saved_alloc_pub and include module finalizer to fix memalloc module shutdown crash
🎉 All green!🧪 All tests passed 🔗 Commit SHA: 0e1dea7 | Docs | View more details | Give us feedback! |
Codeowners resolved asResolved from the full PR diff against |
Dependency direction analysis
|
Circular import analysis
|
g_saved_alloc_pub and include module finalizer to fix memalloc module shutdown crashg_saved_alloc_pub and include module finalizer
g_saved_alloc_pub and include module finalizerg_saved_alloc_pub and include module finalizer
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc754054f6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
ee6490a to
1e9af43
Compare
There was a problem hiding this comment.
Would it make sense to split this into two PRs, one per suspected gap?
On free-threaded Python (no GIL on PYMEM_DOMAIN_OBJ), any hook that already loaded the non-null pub after stop() would call through to the saved allocator context after the heap tracker was destroyed
Also, the module fails to compile on free-threaded Python, so not sure why this reasoning would be relevant.
be2e134 to
685056c
Compare
521cee0 to
e48f169
Compare
g_saved_alloc_pub and include module finalizer07e2b42 to
82edb29
Compare
e48f169 to
98126a9
Compare
82edb29 to
a8b1522
Compare
98126a9 to
8f6606c
Compare
a8b1522 to
f1f7300
Compare
8f6606c to
7858cf5
Compare
f1f7300 to
29296e2
Compare
0a98970 to
5a21673
Compare
29296e2 to
0a3aabf
Compare

Description
While looking at crash reports, I saw a series of crash reports that look like such: https://app.datadoghq.com/error-tracking/issue/4fe0aa66-5607-11f1-b89e-da7ad0900002?query=version%3A4.15.2&et-issue__error-sample-viz=sample&refresh_mode=sliding&from_ts=1789393982142&to_ts=1790603582142&live=true
The crashes are in the memalloc profiler's allocator hook lifecycle caused SIGSEGV crashes in gunicorn/gevent workers (observed as SI_KERNEL at
alloc.malloc(alloc.ctx, ...)inmemalloc_alloc).From looking at the code, I saw that there was no module finalizer so hooks outlive the interpreter
PyModuleDefhad nom_freecallback. When a process such as a gevent worker exits without the Python-level collector callingstop()explicitly, the OBJ and MEM hooks remain installed throughPy_Finalize(). CPython then tears down its internal allocator state, and any allocation that reaches the hook callsalloc.malloc(alloc.ctx, ...)with a stale context which will fault.I added
g_saved_alloc_pub.store(nullptr)instop()to match the MEM domain, and registermemalloc_module_freeasm_freeinPyModuleDefto always uninstall hooks during module deallocation.Testing
Its hard to write a deterministic test for this as waiting on GC to do what it does with module unload is not deterministic.I added a test-only helper
_test_invoke_module_freethat exposes them_freepath directly to Python and added this regression test:test_m_free_uninstalls_hooks_deterministicNot the biggest fan of writing API just for testing but 🤷
Risks
Low IMO
PyMem_SetAllocatoris a plain struct copy with no heap allocation, safe to call at any shutdown stage.memalloc_heap_tracker_deinit_no_cpythonfrees C++ objects without calling any CPython API.Additional Notes
I used AI to write the test following my directions.