Skip to content

Future based async implementation#221

Open
bobozaur wants to merge 17 commits intoa1ien:masterfrom
bobozaur:async-futures
Open

Future based async implementation#221
bobozaur wants to merge 17 commits intoa1ien:masterfrom
bobozaur:async-futures

Conversation

@bobozaur
Copy link

@bobozaur bobozaur commented May 12, 2025

Hey there 👋 ,

I'm new to the libusb world and, by extension, rusb, but I'm currently using it in a personal embedded project. To give some context, the host side of the project is a tray icon implemented through gtk-rs which does very little most of the time and is in no way UI intensive. So I started wondering if, instead of the sync interface, I could integrate the async API of libusb into the glib event loop to keep things as snappy as possible while still staying single-threaded.

I saw the efforts from the rusb-async branch but I felt like there could be so much more to this, so I took it into my own hands to implement proper a Future based libusb integration. The integration is meant to be runtime agnostic and could in fact support event handling in a dedicated async background task or even a background thread.

But that's more for platforms like Windows where file descriptors are not available. For UNIX-like systems, I implemented a FdEventHandler with a FdCallbacks trait interface that allows creating event sources from the file descriptors libusb uses for direct integration in event loops. I did that successfully for glib and I also added some examples that show how to do this in tokio.

I haven't yet published my project because I still have to write docs for it and I got sidetracked with this, but when I do I could add a link as a reference for how the glib integration works. It's really not that different from tokio, and I strongly suspect it wouldn't be very different regardless of the runtime used.

With that being said, I know the implementation here still needs some polishing, but I wanted to open a dialog and look for some reviews on how it looks like so far. At the top of my head, the current things come to mind:

  • Better error handling should be done. However, I do not know what the end game is meant to be. Will this ever get integrated into rusb itself, or will it live in a separate crate? That can dictate whether to reuse the rusb error type or have one dedicated for the async operations. Based on this one error type needs to be extended.
  • The FdEventHandler is a bit quirky in the sense that it should only be constructed once per Context. Or rather, at most one FdEventHandler should be alive at any given time for each context. Otherwise things get confusing since the file descriptor even callbacks get registered/unregistered in a weird order. Maybe it's worth exploring introducing some sort of AsyncContext? I explored the idea of AsyncContext and ended up implementing it. The event handler registration is done through a static map and the AsyncUsbContext trait was introduced to separate sync contexts from async ones. GlobalContext does implement AsyncUsbContext, but event handler registration must be explicit.
  • I experimented with splitting the event handling part of UsbContext so that AsyncContext cannot be used for event handling directly, just to kinda restrict the users from doing that instead of setting an event handler. That, however, implies messing with the current state of rusb and would be a breaking change. Might be worth doing if the async part will be integrated into rusb though.

I'll add things to the list as they come to mind and as people get to experiment with this more. Personally I only use interrupt transfers in my project, so I can't really vouch for them all. But the implementation is based on the rusb-async branch.

I also could not help but notice #217 so I took into account the suggested changes there as well and came up with a nicer interface for isochronous endpoints.

I'm willing to work more on this so that it hopefully gets merged in as the de facto way of doing async with rusb, but I'd highly appreciate some assistance in order to get there faster.

@bobozaur
Copy link
Author

@Aytixel Since you worked on #217 , it would be awesome if you could have a look over this implementation and see if it solves the issues you had with isochronous endpoints before.

@Aytixel
Copy link

Aytixel commented May 12, 2025

@Aytixel Since you worked on #217 , it would be awesome if you could have a look over this implementation and see if it solves the issues you had with isochronous endpoints before.

The implementation seems nice, I'll test it later when I have time and give you feedbacks.

@bobozaur
Copy link
Author

Edited the description to reflect the fact that the AsyncContext type and AsyncUsbContext trait were added. This allows integrating the event handlers into the context from construction (apart from GlobalContext, for which the event handler needs to be set explicitly).

Additionally, the EventHandler and EventHandlerData traits were added which allow the registration of any custom event handler. For UNIX-like systems, FdCallbacksEventHandler<T> will already implement EventHandler for T: FdCallbacks.

The EventHandler trait will allow common event handlers to be created in downstream crates and used by consumers.

@bobozaur
Copy link
Author

@a1ien Would love some feedback on this, especially around the error handling situation. I personally believe that integrating the async stuff into rusb directly would be the best way to go about it. I assume the idea with having it as a separate crate was to allow for experimentation before committing to anything.

That would allow reusing the rusb::Error and maybe creating a TransferError type that could be dedicated to the Future impls. There's also the matter of the double Arc'ed inner pointer for the AsyncContext, which would go away by migrating the code into rusb.

@a1ien
Copy link
Owner

a1ien commented May 14, 2025

Hi, thank you for you effort. The rusb project deserves more attention than I currently have. I will promise that i will take a look this PR.
But if some one want to join this project as so-maintainer I will appreciate that.

@bobozaur
Copy link
Author

@a1ien I could step up to help if you want, but perhaps it's worth discussing what that would entail and how it would work in terms of changes, code reviews, etc.

Copy link

@Aytixel Aytixel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bobozaur apart from this fix, everything works great 👍. I still have problems with isochronous transfers, but I think it's due to my cluky program.

Co-authored-by: Aytixel <36609903+Aytixel@users.noreply.github.com>
@bobozaur
Copy link
Author

@bobozaur apart from this fix, everything works great 👍. I still have problems with isochronous transfers, but I think it's due to my cluky program.

Awesome! Thanks for taking the time to test it out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants