[Python-ideas] libuv based eventloop for tulip experiment

Saúl Ibarra Corretgé saghul at gmail.com
Tue Jan 29 21:08:33 CET 2013


Hi!

[snip]

>
>> Here [2] is the source code, along with some notes I took during the
>> implementation.
>
> Hm... I see you just copied all of tulip and then hacked on it for a
> while. :-) I wonder if you could refactor things so that an app would
> be able to dynamically choose between tulip's and rose's event loop
> using tulip's EventLoopPolicy machinery? The app could just
> instantiate tulip.unix_eventloop._UnixEventLoop() (yes, this should
> really be renamed!) or rose.uv.EventLoop, but all its imports should
> come from tulip.
>
> Also, there's a refactoring of the event loop classes underway in
> tulip's iocp branch -- this adds IOCP support on Windows.
>

Sure, that's the idea, I just put everything together so that it would 
still run even if some API changes :-) Anyway, since I plan to follow 
this more closely I'll definitely go for that and rose will just create 
a new EventLoopPolicy which uses the uv event loop.

>> I know that the idea is not to re-implement the PEP itself but for people to
>> create different EventLoop implementations. On rose I bundled tulip just to
>> make a single package I could play with easily, once tulip makes it to the
>> stdlib only the EventLoop will remain.
>
> It will be a long time before tulip makes it into the stdlib -- but
> for easy experimentation it should be possible for apps to choose
> between tulip and rose without having to change all their tulip
> imports to rose imports.
>

Agreed.

>> Here are some thoughts (in no particular order):
>>
>> - add_connector / remove_connector seem to be related to Windows, but being
>> exposed like that feels a bit like leaking an implementation detail. I guess
>> there was no way around it.
>
> They would only be needed if we ever were to support WSAPoll() on
> Windows, but I'm pretty much decided against that (need to check with
> Richard Oudkerk once more). Then we can kill add_connector and
> remove_connector.
>

Ok, good to hear :-)

>> - libuv implements a type of handle (Poll) which provides level-triggered
>> file descriptor polling which also works on Windows, while being highly
>> performant. It uses something called AFD Polling apparently, which is only
>> available on Windows>= Vista, and a select thread on XP. I'm no Windows
>> expert, but thanks to this the API is consistent across all platforms, which
>> is nice. mAybe it's worth investigating? [3]
>
> Again that's probably for Richard to look into. I have no idea how it
> relates to IOCP.

I'm no windows expert either :-) AFAIS, IOCP provides a completion-based 
interface, but many people/libraries are used to level-triggered 
readiness notifications. It's apparently not easy to have unix style 
file descriptor polling in Windows, but that AFD Poll stuff (fairy dust 
to me, to be honest) does the trick. It only works for sockets, but I 
guess that's ok.

>
>> - The transport abstraction seems quite tight to socket objects.
>
> I'm confused to hear you say this, since the APIs for transports and
> protocols are one of the few places of PEP 3156 where sockets are
> *not* explicitly mentioned. (Though they are used in the
> implementations, but I am envisioning alternate implementations that
> don't use sockets.)
>

Indeed I meant the implementation. For example right now start_serving 
returns a Python socket object maybe some sort of ServerHandler class 
could hide that and provide some some convenience methods such as 
getsockname. If the eventloop implementation uses Python sockets it 
could just call the function in the underlying sockets, but some other 
implementations may have other means so gather that information.

>> pyuv
>> provides a TCP and UDP handles, which provide a completion-style API and use
>> a better approach than Poll handles.
>
> So it implements TCP and UDP without socket objects? I actually like
> this, because it validates my decision to keep socket objects out of
> the transport/protocol APIs. (Note that PEP 3156 and Tulip currently
> don't support UDP; it will require a somewhat different API between
> transports and protocols.)
>

Yes, the TCP and UDP handles from pyuv are wrappers to their 
corresponding types in libuv. They exist because JS doesn't have sockets 
so the had to create them for nodejs. The API, however, is completion 
style, here is a simple example on how data is read from a TCP handle:

def on_data_received(handle, data, error):
     if error == pyuv.error.UV_EOF:
         # Remove closed the connection
         handle.close()
         return
     print(data)

tcp_handle.start_read(on_data_received)

This model actually fits pretty well in tulip's transport/protocol 
mechanism.

>> They should give better performance
>> since EINTR in handled internally and there are less roundtrips between
>> Python-land and C-land.
>
> Why would EINTR handling be important? That should occur almost never.
> Or did you mean EAGAIN?
>

Actually, both. If the process receives signal epoll_wait would be 
interrupted, and libuv takes care of rearming the file descriptor, which 
happens in C without the GIL. Same goes for EAGAIN, basically libuv 
tries to read 64k chunks when start_read is called, and it automatically 
retires on EAGAIN. I don't have number to back this up (yet) but 
conceptually sounds pretty plausible.

>> Was it ever considered to provide some sort of
>> abstraction so that transports can be used on top of something other than
>> regular sockets? For example I see no way to get the remote party from the
>> transport, without checking the underlying socket.
>
> This we are considering in another thread -- there are in fact two
> proposals on the table, one to add transport methods get_name() and
> get_peer(), which should return (host, port) pairs if possible, or
> None if the transport is not talking to an IP connection (or there are
> too many layers in between to dig out that information). The other
> proposal is a more generic API to get info out of the transport, e.g.
> get_extra_info("name") and get_extra_info("peer"), which can be more
> easily extended (without changing the PEP) to support other things,
> e.g. certificate info if the transport implements SSL.
>

The second model seems more flexible indeed. I guess the SSL transport 
could be tricky, because while currently Tulip uses the ssl module I 
have no TLS handle on pyuv so I'd have to build one on top of a TCP 
handle with pyOpenSSL (I have a prototype here [1]), so object types / 
APIs wouldn't match, unless Tulip provides some wrappers for SSL related 
objects such as certificates...

>> Thanks for reading this far and keep up the good work.
>
> Thanks for looking at this and reimplementing PEP 3156 on top of
> libuv! This is exactly the kind of thing I am hoping for.
>

I'll follow up the discussion closer now :-)


[1]: https://gist.github.com/4599801#file-uvtls-py

Regards,

-- 
Saúl Ibarra Corretgé
http://saghul.net/blog | http://about.me/saghul



More information about the Python-ideas mailing list