-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Avoid local_internals destruction #4192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
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.
Hmm, wait doesn't this leak now? as in the local_internals memory is never freed?
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.
Which is fine I suppose since there doesn't appear to be a good alternative, but we need to document it.
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.
Defining a function-local static variable that leaks like this is a common pattern (recommended by Google C++ style guide, for example: https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables). Since the destructor would only be called when the program exits, the "leak" is irrelevant as long as it is just memory. You can define it in a slightly more complicated way (https://github.com/google/tensorstore/blob/master/tensorstore/internal/no_destructor.h) to avoid the extra heap allocation, but one extra heap allocation is presumably not very important.
There are actually two different cases to consider here:
get_local_internals
used within an extension module, Python never actually unloads extension modules (because in general it is not really feasible to do that safely), so the destructor is never invoked anyway.get_local_internals
is used outside of an extension module, e.g. when using an embedded interpreter, or when statically linking other libraries directly to the Python interpreter itself (as we do internally at Google).Looking more closely at
finalize_interpreter
, though, the use ofget_local_internals
seems suspect:pybind11/include/pybind11/embed.h
Line 229 in 424ac4f
By default there will be a separate copy of
local_internals
for each extension module, and for the embedding program itself. Therefore, this will only free types and exception handlers that were registered module-local by whichever module callsfinalize_interpreter
. Any types registered by other modules will remain registered. To work properly, this logic would need to change. Though given all of the bugs and limitations with multiple initialization and finalization of the python interpreter, I think it might be better to just not support that at all.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.
@jbms How would suggest changing this logic then? Removing that line?
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.
Either to remove those two lines from
finalize_interpreter
, and document thatinitialize_interpreter
should not be called more than once, or have the global internal state keep a reference to all of the local internals states, so thatfinalize_interpreter
can free all of the local internals states.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.
The first option sounds like something that can break someones old code. Don't we care about it?
About the second option I do not quite understand how will it fix the bug. We still will be able to create static in the destructor routine. Or maybe I do not understand the second option...
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.
Thinking pragmatically, I think the original fix is great, maybe we could add a link to the comment, e.g. what Jeremy provided (https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables). The original fix solves an immediate problem without disturbing anything else. All it does is shift the memory for the local internals from the data section of the binary to the heap (inconsequential side-effect) and not run the destructors (desired fix).
Thinking idealistically, long-term I'd really want to make it difficult to use pybind11 to initialize/finalize the interpreter multiple times. It's a trap / illusion. But I think it's best to not venture there in this PR, or now.
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.
The option to have
finalize_interpreter
free all of the local states would require some changes to howlocal_internals
works, which would hopefully be done in such a way as to avoid this bug.I agree that the current PR is the simplest fix, and we can address this other issue of
finalize_interpreter
not freeing all of the local internals as a separate issue.