-
Notifications
You must be signed in to change notification settings - Fork 217
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
[BUG]: Relinquished instance exception for still alive object #879
Comments
Hello @WillFroom. First, a quick check before I look deeper into this: did you read about the Using unique casters without this special deleter has some inherent limitations that are discussed in the documentation sections on ownership. |
Hi @wjakob, I did read the documentation about |
Ah. I think I understand. What you are trying to do is currently not supported by the library. What is important to keep in mind is that nanobind associates at most one Python instance with each C++ object.
.. but that instance is marked as invalid, so the use in The behavior that is tested and documented is that a type is consistently accessed using unique ownership. For example, if No proposal on how to fix this yet, this is just to explain the cause. |
Is it possible that your experiment works after adding:
just before the last line? That would confirm the hypothesis. |
Adding |
This commit addresses an issue arising when multiple threads want to access the Python object associated with the same C++ instance, which does not exist yet and therefore must be created. @vfdev-5 reported that TSAN detects a race condition in code that uses this pattern, caused by concurrent unprotected reads/writes of internal ``nb_inst`` fields. There is also a larger problem: depending on how operations are sequenced, it is possible that two threads simultaneously create a Python wrapper, which violates the usual invariant that each (C++ instance pointer, type) pair maps to at most one Python object. This PR updates nanobind to preserve this invariant. When registering a newly created wrapper object in the internal data structures, nanobind checks if another equivalent wrapper has been created in the meantime. If so, we destroy the thread's instance and return the registered one. This requires some extra handling code, that, however, only runs with very low probability. It also adds a new ``registered`` bit flag to ``nb_inst``, which makes it possible to have ``nb_inst`` objects that aren't registered in the internal data structures. I am planning to use that feature to fix the (unrelated) issue #879.
Hello @WillFroom, I thought about this issue some more, and it's complicated. I think it will need a significant redesign of how unique pointers are handed in nanobind. Currently, instances have a "relinquished" flag to indicate that ownership has been transferred away. However, the data structures of nanobind still map the C++ instance pointer to this relinquished object, which prevents return via This isn't related to anything I need for my own work, and it will take a lot of time to understand and fix. I think I prefer to close it as "out of of (my) support scope" with an express invitation for external contributions. |
Problem description
I am hitting an issue where nanobind is raising an exception when I am trying to use a C++ object that has already been on a round-trip to python and back but is now wrapped by a outer class.
I have attached a MRE, I get the following error on the
use_inner_class
line:RuntimeWarning: nanobind: attempted to access a relinquished instance of type ...
.I believe what I am doing is valid, but if there is a way around this please let me know.
I haven't tried reverting it but I think that this change: #591 may be being to strict?
Reproducible example code
Extension:
Failing test:
The text was updated successfully, but these errors were encountered: