[Python-ideas] libuv based eventloop for tulip experiment

Guido van Rossum guido at python.org
Tue Jan 29 21:26:52 CET 2013


On Tue, Jan 29, 2013 at 12:08 PM, Saúl Ibarra Corretgé <saghul at gmail.com> wrote:
> [snip]
[snip*2]
> 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.

Yeah, so do the other polling things on Windows. (Well, mostly
sockets. There are some other things supported like named pipes.)

I guess in order to support this we'd need some kind of abstraction
away from socket objects and file descriptors, at least for event loop
methods like sock_recv() and add_reader(). But those are mostly meant
for transports to build upon, so I think that would be fine.

>>> - 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.

Ah, yes, the start_serving() API. It is far from ready. :-(

>>> 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.

Yeah, I see. If we squint and read "handle" instead of "socket" we
could even make it so that loop.sock_recv() takes one of these -- it
would return a Future and your callback would set the Future's result,
or its exception if an error was set.

>>> 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.

Hm. Anything that uses signals for its normal operation sounds highly
suspect to me. But it probably doesn't matter either way.

>>> 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...

Hm, I thought certificates were just blobs of data? We should probably
come up with a standard way to represent these that isn't tied to the
stdlib's ssl module. But I don't think this should be part of PEP 3156
-- it's too big already.

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

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

-- 
--Guido van Rossum (python.org/~guido)



More information about the Python-ideas mailing list