-
Notifications
You must be signed in to change notification settings - Fork 69
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
ValueError
in a finalizer on PyPy
#98
Comments
This is not a remnant, but a way to cleanly fail instead of having the finalizer randomly break (ex: on interpreter shutdown). So if two finalizers fall into the same entry in Could you post more of the traceback which leads to this failure ? Especially, I wonder which class' Also, do you have a suggestion to clarify the intent of those raises, so they do not look like they may be leftover debugging code ? EDIT: finalization order happens because references to the required objects are held by the finalizer itself, not by |
That was |
Is there a way for me to try to replicate this issue ? I unfortunately have very little time these days (which I would otherwise spend trying to get the new python-libusb1 version out, master has several improvements which were long overdue) but maybe I can figure something out. At least, I did a quick try of the In case you would like to reproduce, it goes like this: Terminal 1, in
Terminal 2, in
But this may be too simple of an example to have a chance of triggering the issue. |
Unfortunately the way I discovered it is by running an userspace RGMII PHY driver with Glasgow on PyPy3.9. |
This may be the reason: I use object ids as keys, and this is where you triggered a duplicate value conflict. IIRC object lifecycles in pypy are subtly different from cpython, and I would not be too surprised if this affected finalizer call order. Here are some things I can think of:
No better idea so far. |
Can you assign a property to an object containing a sequential numeric id, or will that get destroyed before the finalizer is called? |
Excellent idea. This is not only much simpler, but it showed me a way to simplify the entire thing. I ended up using a single This should resolve this issue, not only because of the removal of the reliance on object id, but also because I removed the raise statements (because if How does that look to you ? |
As I understand, you are using However, I have examined the current implementations in CPython and PyPy and found them to provide atomicity. The PyPy one is written in "interpreter level" code; as far as I understand, "interpreter level" Python code in PyPy is atomic in the same way C code is atomic in CPython, aside from explicit yield points. Personally I would use a mutex but I would also not die on this hill. |
Excellent point, thank you very much for your careful review. My general position on concurrency in python-libusb1 is something like:
I think count atomicity belongs to the 3rd point: if it goes wrong, some object will not be finalized in the proper order and may cause a segfault. So I must think more - hopefully I will have an idea not involving a lock. |
class Finalizer:
per_thread = threading.local()
def next_sequence(self):
self.per_thread.sequence = getattr(self.per_thread, "sequence", 0) + 1
return (self.per_thread.sequence, threading.current_thread().ident) |
(Sorry, I've unintentionally omitted a part of the code before; I've edited it to reflect that.) This solution ambiently assumes that you aren't sharing USBContext objects between threads, i.e. by the time a thread is dead, all of the objects referenced by it are also dead. I think that is a reasonable assumption but it should perhaps be made explicit. |
Indeed, if a subsequent thread is spawned and gets the same thread id, then duplicate keys may happen again. I suspect there are setups where this may not hold though, like a worker-thread design where the number of threads changes and objects are not assigned to any given thread. ...The song of the lock-mermaids sure is tempting. |
An uncontended lock should be quite cheap to take, and contention seems like it wouldn't be very common (how many people even have more than one USB context in first place?) |
It seems to be cheap indeed. I added a lock in 11e700b . |
Nice! I'm a bit exhausted lately so I can't test this immediately. |
When running code that works fine on CPython under PyPy, I'm getting a
ValueError
raised here:Is this just a remnant on some debug code that happens to work by chance on CPython, or is there an actual reason to raise it? I'm very confused.
I'm using python-libusb1 3.1.0.
The text was updated successfully, but these errors were encountered: