-
Notifications
You must be signed in to change notification settings - Fork 212
Avoid race due to deleted imports when cleaning up objects at interpreter shutdown #973
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
Conversation
Auto-sync is disabled for ready for review pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
Thanks, Brandon! Please let's not touch the |
if self._handle is not None: | ||
err, = driver.cuEventDestroy(self._handle) | ||
if not is_shutting_down() | ||
err, = driver.cuEventDestroy(self._handle) |
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.
What's the issue with this?
err, = driver.cuEventDestroy(self._handle) | |
if (destroy := getattr(driver, "cuEventDestroy", None)) is not None: | |
err, = destroy(self._handle) |
Then you don't need the global hack.
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.
We want to use a field-tested solution (see the internal thread) instead of coming up with a new one.
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.
How about sys.is_finalizing()
? There's probably not a more field tested solution than what's in the standard library.
In this case it also seems particularly suited to the problem being solved here.
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.
This is definitely better, but this is still a hack where if we run things under tools like cuda-memcheck
that check for resource leaks it will still pop.
Additionally, we'd need to carry these checks everywhere that code could be called in __del__
functions, I believe even transitively. I.E. the raise_if_driver_error
function.
What if we moved to a __dealloc__
function?
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.
As discussed internally,sys.is_finalizing()
- while official - only returns True
at the very late stage of interpreter shutdown, later than all of the exit handlers (which this PR is based on). It is unclear to me if this solves the problem. I feel nervous about this. Do we have any known, big projects using this solution?
What if we moved to a
__dealloc__
function?
No, we can't do this (yet), because we currently call Python bindings. Once #866 lands we can switch to this, but I need some time to work it out and I prefer this to be fixed independently (and asap).
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.
Here are the relevant steps, in order, during interpreter shutdown:
- wait for threads to shutdown
- wait for any pending calls
- call
atexit
handlers (where the flag would be set) - set the interpreter to be officially in finalizing mode (this information is what
sys.is_finalizing()
uses) - collect garbage (
__del__
would be called here)
So, it doesn't really matter which approach we take, and it's overall less code and less hacky code to use a standard library builtin.
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.
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.
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.
This makes it sound like the previously proposed solution using atexit would work, but it wouldn't, because __del__
is still called at the same point in the program regardless of where the state of the interpreter is checked.
It would be more helpful to provide thorough reasoning (as I did above). Right now, it just looks like everything I said was incorrect without any description as to why that is.
@leofang the problem with this approach is that |
|
Ah! This is what I have missed - thanks!
Sounds like a good idea. I like a variant of this: cdef _shutdown_safe_close(self, is_shutting_down=is_shutting_down):
if is_shutting_down and is_shutting_down():
return
# do cleanup
def __del__(self):
self._shutdown_safe_close()
cpdef close(self, stream=None):
self._shutdown_safe_close(is_shutting_down=None) # bypass the shutdown check |
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.
LGTM!
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.
Ah, sorry Brandon, one more thing: Could you add a rel note entry to cuda_core/docs/source/release/0.X.Y-notes.rst?
/ok to test |
This comment has been minimized.
This comment has been minimized.
|
Closes #803
I verified the fixes locally when applied on top of the
12.9.2
tag as I'm on cuda 12. Since_memory.pyx
was not present at that time the changes there are "blind" for now. However the change to the stream object seems to get me around the original error reliably.