GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — gh-139540: Fix executor deallocation crash when JIT compilation fails by ashm-dev · Pull Request #139952 · python/cpython Conversation
|
|
Note that this wouldn't solve the cold executor issue. It would only prevent an assertion failure in case we branch here (but if we branch here, we're already in a very bad state) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@picnixz Thanks for the context regarding the "cold executor issue". |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
It's a larger and more complex issue. The fact that we have an immortal cold executor seems to make normal executors leak. I don't know if it's because we reset executors in exit sides to a cold executor that then makes that specific executor not garbage-collectable (this is concerning). Your PR would still be fine but I don't know if it won't be eventually rechanged because of the fix we need to do on the other side. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Sorry but I have the same PR up here https://github.com/python/cpython/pull/137016/files, since July of this year |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
The executor object wasn't decremented if Tier 2 code returned with an exception set but not a fatal error. This change moves the Py_DECREF call inside the TIER1_TO_TIER2 macro. This ensures the executor's reference count is always decremented.
|
Considering this is a duplicate of gh-137016, I'm closing this PR. Sorry! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
For the other leaks, |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| if (cold == NULL) { | ||
| Py_FatalError("Cannot allocate core JIT code"); | ||
| } | ||
| _PyObject_GC_TRACK(cold); |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect. The executor is immortal for now.
No this was correct, my bad. I didn't read the function correctly. The problem is that we're setting it immortal afterwards and I'm not sure it will be happy.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Good point, thanks for the correction.
The intent was to track it for consistency immediately after allocation. My assumption is the GC correctly handles a tracked object that later becomes immortal.
Is that a safe assumption, or should we explicitly _PyObject_GC_UNTRACK it just before immortalization?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
That I don't know. The problem here is whether we want an immortal cold executor or not. That's why I wanted to wait for Mark's output here, and didn't write a PR already.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@Fidget-Spinner Do you mind incorporating some observations from this PR in yours? I don't know whether the change is correct here when changing |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Hi @Fidget-Spinner , Thanks for pointing that out and linking your PR. I've taken a close look at both. While our changes overlap in However, this PR primarily addresses a critical reference counting leak that was detectable with ASan. The root cause was that the My PR also includes the same I believe this PR provides a more complete solution for the stability issues in this code path. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
These changes are correct and necessary. The intended lifecycle is:
The other |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Does it fix the leak as in the issue? that is, does it fix the refcounting? I can't check this now, but please check whether the reproducer (without ASAN) is fixed when you run |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| #define TIER1_TO_TIER2(EXECUTOR) \ | ||
| #define TIER1_TO_TIER2(EXECUTOR) \ | ||
| do { \ | ||
| OPT_STAT_INC(traces_executed); \ |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Why was this changed? please revert.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| /* Tier-switching macros. */ | ||
|
|
||
| #define TIER1_TO_TIER2(EXECUTOR) \ | ||
| #define TIER1_TO_TIER2(EXECUTOR) \ |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Why was this changed? please revert.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| tstate->current_executor = (PyObject *)executor; | ||
| _PyFrame_SetStackPointer(frame, stack_pointer); | ||
| stack_pointer = _PyFrame_GetStackPointer(frame); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This seems to be redundant.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
We are setting the stack pointer and requerying it while doing nothing inbetween.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| next_instr = _Py_jit_entry((EXECUTOR), frame, stack_pointer, tstate); \ | ||
| frame = tstate->current_frame; \ | ||
| stack_pointer = _PyFrame_GetStackPointer(frame); \ | ||
| Py_DECREF(EXECUTOR); \ |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Why should we decref it here?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| assert(tstate->current_executor == NULL); | ||
| assert(executor != tstate->interp->cold_executor); | ||
| tstate->jit_exit = NULL; | ||
| tstate->current_executor = (PyObject *)executor; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Is it just to hold a reference to the executor?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.