Skip to content
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

C/Rust libraries and Trio introp documentation #931

Open
thedrow opened this issue Feb 14, 2019 · 6 comments
Open

C/Rust libraries and Trio introp documentation #931

thedrow opened this issue Feb 14, 2019 · 6 comments
Labels

Comments

@thedrow
Copy link
Contributor

thedrow commented Feb 14, 2019

Suppose I have a C library such as libpq or hiredis which exposes an API for async communication with their servers.
I'd like to create a python client that supports trio as the event loop but performs the socket operations on it's own.
The asyncio version is quite clear, albeit a bit lengthy.
I have not seen an example of a C extension that cooperates with the trio event loop as of yet.

I believe we should document how to do it correctly.
What do you think?

@codypiersall
Copy link

codypiersall commented Feb 14, 2019

A nice little explanation/tutorial for this would be pretty helpful. Is this issue just asking for docs, or for pointers about how to actually implement this for the two libraries you mentioned?

@thedrow
Copy link
Contributor Author

thedrow commented Feb 14, 2019

This issue is about documentation.
I think that a simple echo client/server would suffice.

If you have concrete examples of client libraries written in C or Rust working with trio, I'd be happy to examine them.

@oremanj
Copy link
Member

oremanj commented Feb 14, 2019

I'm not familiar with the particular libraries you mentioned, but if the library gives you file descriptors and asks for callbacks when they become readable/writable, you might be looking for trio.hazmat.wait_readable and wait_writable?

@codypiersall
Copy link

The library pynng, which is a Python wrapper for nng using cffi, does this. @njsmith and @tgoodlet helped me with the implementation a lot. The issue that tracked the ideas was codypiersall/pynng#4 and the commit that added support was codypiersall/pynng@ec99bda.

I think the integration was more-or-less straightforward, with the exception for me being handling of cancellations (abort_fn in @njsmith's comment) ; maybe I just didn't read the right docs, but I don't really get the interaction that's going on there with Trio's event loop.

Integrating with nng was pretty straightforward because nng's aio API takes a callback, which is called when the aio completes, so I was able to easily call token.run_sync_soon(trio.hazmat.reschedule, task) from the callback. For libpq I couldn't find something that notifies you when an operation is complete; I only found the method of polling occasionally to see if an aio completes. I probably missed something though since I'm completely unfamiliar with that library.

@oremanj
Copy link
Member

oremanj commented Feb 14, 2019

I just took a look at hiredis, and based on https://github.com/redis/hiredis/blob/master/adapters/libevent.h it seems that the library wants certain functions to be called when a particular file descriptor becomes readable or writable, plus it wants callbacks that it can invoke to enable/disable readability/writability notifications. This is a common approach used by C libraries, but is very different than the approach taken by nng, which does its I/O in a thread and hides more of the plumbing from the user.

The readability/writability callbacks approach is a slightly awkward fit for Trio only because Trio is so adamant about not using callbacks in its public APIs. :-) But it's not impossible by any means. Very roughly, you'd probably want to create Trio tasks that perform a loop of [call wait_readable or wait_writable, tell hiredis that the thing happened], and spawn each task when hiredis starts being interested in that event / cancel it when it's no longer interested. (Or the tasks could stay running continuously, and just switch between blocking on wait_readable and blocking on a trio.Event. Or whatever.)

I agree this would be a good example to have, since it's a common pattern and there are some tricky details. Unfortunately it doesn't appear that the Python hiredis bindings https://github.com/redis/hiredis-py expose hiredis's I/O logic, so we'd need to improve the bindings or use cffi. I might find time to work this out at some point, but probably not soon.

@oremanj
Copy link
Member

oremanj commented Oct 10, 2019

It would be nice to have an example wrapping the c-ares asynchronous DNS resolver. There's already a Python package for it (pycares) which has decent async primitives support, although it's very callback-based.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants