-
Notifications
You must be signed in to change notification settings - Fork 221
usbdmx: Move widget deletion to main thread #1712
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
usbdmx: Move widget deletion to main thread #1712
Conversation
peternewman
left a comment
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.
|
@kripton you might be interested in this to avoid merge conflicts with your changes, and you may have a better idea of the code than me! |
DanielG
left a comment
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.
Thanks for the review!
Have you seen this issue in your code then?
Indeed I ran into this deadlock in our out-of-tree code. AFAICS there isn't an in-tree user that is currently triggering it because no one is crazy enough to have the usb tranciever (threads) be part of the (derrived) Port class, but there's nothing preventing it from happening in the future.
The approximate backtrace for the main thead (from memory) is something like this:
libusb_close()
....
LibUsbAdaptor::Close()
AsyncUsbSender::~AsyncUsbSender()
MyAsyncUsbSender::~MyAsyncUsbSender()
MyPort::~MyPort()
Device::DeleteAllPorts()
~Device::Stop()
AsyncPluginImpl::ShutdownDevice()
...
while the libusb thread is stuck waiting for the future f.Get() call in AsyncPluginImpl::DeviceEvent.
For details on the libusb_close behavour see the libusb_close definition libusb/core.c, it has this:
/* Similarly to libusb_open(), we want to interrupt all event handlers
* at this point. More importantly, we want to perform the actual close of
* the device while holding the event handling lock (preventing any other
* thread from doing event handling) because we will be removing a file
* descriptor from the polling loop. If this is being called by the current
* event handler, we can bypass the interruption code because we already
* hold the event handling lock. */
if (!handling_events) {
[...]
/* take event handling lock */
libusb_lock_events(ctx);
}
and IIRC libusb_lock_events() is where things deadlock.
If you'd like to see the details I could probably post the relevant parts of our code somewhere, it's just not really ready for inclusion is any way shape or form :)
|
@peternewman, thanks for pinging me here. I can also follow the reasoning of the change but just as you, I wouldn't know if it breaks something. I agree that @nomis52 might be able to comment on it. However since I assume it worked before #930 and nomis52 might just not have been aware of the problem, this could just be merged. |
|
Oh, and I don't have any USB DMX dongle to actually check the code changes ;) Oh, just thinkig about it: I have one old Anyma uDMX around that I can test the code with :D |
I think you want to be calling adaptor->DetachKernelDriver(usb_handle, <USB iface num>) before trying to access that stuff, see |
|
I did test this patch together with our out-of-tree widget support code and it works just fine. I don't have any other supported usbdmx box either though, just some usbpro ones. |
|
@kripton I just found your branch that Peter was talking about (I think): master...kripton:RP2040UsbDmx. I would ask you to consider submitting that as a PR if only so I can do some review. I also have some muti-universe support for usbdmx in my (private) patch queue using a different approach and I think we might want to hammer out a way to do this that works for both of us. |
No need for that, if you've hit the issue, made the change and its fixed it, that's good enough for me. |
peternewman
left a comment
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.
So from my perspective, LGTM, if we could fix those two minor issues then I'm happy to merge.
Logistics wise, I know #1627 slipped through the net before, is there any benefit to that going in before or afterwards?
Awesome, I'll try to get a new revision out by next week.
No worries, I never had time to fix the CI failures there but I'll get that cleaned up. FYI @kripton you might want to have a look at that PR since your branch has a vaguely similar multi-transfer mechanism.
I don't think the order makes any difference. |
Looks like it may just be a space around a comment or something trivial! |
e9d0e3f to
cd3fcaa
Compare
peternewman
left a comment
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 within my knowledge of the code @DanielG
Happy to merge given the tests before and afterwards that you've done. Would you mind resolving the two outstanding nits please.
9e478b5 to
5aeaf22
Compare
|
@peternewman This is ready for another round of review, also the CI seems to need approval: "First-time contributors need a maintainer to approve running workflows." despite this not being my first rodeo. Weird. |
peternewman
left a comment
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.
Sorry just one more minor style nit please.
Currently when a usb device is remove the libusb event thread, though the hotplug handler, will cause the widget's Device to be deleted in the main thread and wait for this to complete in the libusb thread. If any of the destructors triggered by device deletion causes libusb_close to be called this causes a deadlock as libusb_close will try to take over handling of events in the main thread while the libusb thread is waiting for libusb_close, via the destructors, to finish. Just move both widget and device deletion to the main thread to prevent this scenario.
5aeaf22 to
3faa662
Compare
|
No worries, thanks for the quick review :) |
peternewman
left a comment
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.
Thanks for this @DanielG !
I assume you've tested the latest iteration?
It would be nice for backwards compatibility if we could keep the old method too, but I'm guessing we can't do that without risking causing the issue we're trying to fix!
|
Just to be clear: I have not tested the post-review version. I think we only made cosmetic changes though so I wouldn't expect any breakage having been introduced. Unfortunately my test bed for this is torn down ATM. So if you want it tested again it'll have to sit around until I get around to the project this came from again. As for your backwards compat point, yeah, the whole point is to prevent a deadlock so I don't see how we can do that while keeping DeleteWidget in the libusb thread. |
Yet another reason to hate force pushing! :)
Okay I've merged it anyway, hopefully myself or someone will notice fairly sharpish if this breaks things!
Yep, makes sense, just thought I should check. |
Currently when a usb device is remove the libusb event thread, though the
hotplug handler, will cause the widget's Device to be deleted in the main
thread and wait for this to complete in the libusb thread.
If any of the destructors triggered by device deletion causes libusb_close
to be called this causes a deadlock as libusb_close will try to take over
handling of events in the main thread while the libusb thread is waiting
for libusb_close, via the destructors, to finish.
Just move both widget and device deletion to the main thread to prevent
this scenario.