[Python-ideas] Tulip / PEP 3156 event loop implementation question: CPU vs. I/O starvation

Guido van Rossum guido at python.org
Sat Jan 12 00:41:05 CET 2013


Here's an interesting puzzle. Check out the core of Tulip's event
loop: http://code.google.com/p/tulip/source/browse/tulip/unix_events.py#672

Specifically this does something like this:

1. poll for I/O, appending any ready handlers to the _ready queue

2. append any handlers scheduled for a time <= now to the _ready queue

3. while _ready:
       handler = _ready.popleft()
       call handler

It is the latter loop that causes me some concern. In theory it is
possible for a bad callback to make this loop never finish, as
follows:

def hogger():
    tulip.get_event_loop().call_soon(hogger)

Because call_soon() appends the handler to the _ready queue, the while
loop will never finish.

There is a simple enough solution (Tornado uses this AFAIK):

now_ready = list(_ready)
_ready.clear()
for handler in now_ready:
    call handler

However this implies that we go back to the I/O polling code more
frequently. While the I/O polling code sets the timeout to zero when
there's anything in the _ready queue, so it won't block, it still
isn't free; it's an expensive system call that we'd like to put off
until we have nothing better to do.

I can imagine various patterns where handlers append other handlers to
the _ready queue for immediate execution, and I'd make such patterns
efficient (i.e. the user shouldn't have to worry about the cost of the
I/O poll compared to the amount of work appended to the _ready queue).
It is also convenient to say that a hogger that really wants to hog
the CPU can do so anyway, e.g.:

def hogger():
    while True:
        pass

However this would pretty much assume malice; real-life versions of
the former hogger pattern may be spread across many callbacks and
could be hard to recognize or anticipate.

So what's more important? Avoid I/O starvation at all cost or make the
callbacks-posting-callbacks pattern efficient? I can see several
outcomes of this discussion: we could end up deciding that one or the
other strategy is always best; we could also leave it up to the
implementation (but then I still would want guidance for what to do in
Tulip); we could even decide this is so important that the user needs
to be able to control the policy here (though I hate having many
configuration options, since in practice few people bother to take
control, and you might as well have hard-coded the default...).

Thoughts? Do I need to explain it better?

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



More information about the Python-ideas mailing list