geertj / gruvi

Async IO for Python, Simplified
http://gruvi.readthedocs.org/
MIT License
94 stars 12 forks source link

pyuv (and gruvi) will not exit on windows after getaddrinfo #12

Closed jmooreoliva closed 10 years ago

jmooreoliva commented 10 years ago

Environments tested: Windows 7 x64, python 2.7.8 32bit, libuv 0.11.4, gruvi from github Windows 7 x64, python 2.7.7 32bit, libuv latest from github, gruvi from github

_Please read the last comment in this thread. The culprit appears to be getaddrinfo, not listen_

It seems that once a HttpServer has started listening, the process cannot be exited _even with os.exit . The process stops executing, but it must be killed via task manager to finally end. I have also tried gruvi.get_hub().close() with no change in behaviour.

Finally - running test_http.py also exhibits similar behaviour: The process appears to finish executing, but will not close until killed from the task manager.

Any help would be much appreciated!

Output of test program:

>python testws.py
Press CTRL-C to exit
stopping server
stopping

test program testws.py

import logging
import os
import argparse
import sys

import gruvi
from gruvi import get_hub
from gruvi.http import HttpServer

logging.basicConfig()

def do_stop():
    gruvi.sleep(10)
    print 'stopping server'
    server.close()
    print 'stopping'

    os._exit(0)

def hello_app(environ, start_response):
    headers = [('Content-Type', 'text/plain')]
    start_response('200 OK', headers)

    return [b'Hello, world!']

server = HttpServer(hello_app)

server.listen(('0.0.0.0', 8443))

gruvi.spawn(do_stop)

hub = get_hub()
print 'Press CTRL-C to exit'
hub.switch()
jmooreoliva commented 10 years ago

I tested a raw pyuv program that simply listened to a socket - it is able to exit after it starts listening, unlike my gruvi test.

I did this in an attempt to pinpoint if they problem was in pyuv or in gruvi. This test appears to demonstrate that it has something to do with gruvi.

Output of test

>python testpyuv.py
LOOP
LOOP
LOOP
LOOP
LOOP
LOOP
LOOP
LOOP
LOOP
LOOP
EXITING

testpyuv.py

import os
import pyuv

loop = pyuv.Loop.default_loop()
timer = pyuv.Timer(loop)
timer.start(lambda *args: None, 0.0, 0.05)
listen_socket = pyuv.TCP(loop)
listen_socket.nodelay(True)
listen_socket.bind(("0.0.0.0", 8443))
had_listen_callback = False
def listen_callback(*args):
    global had_listen_callback
    print "listen_callback", args
    had_listen_callback = True

listen_socket.listen(listen_callback, 5)
ctr = 0
while not had_listen_callback:
    print 'LOOP'
    ctr += 1

    if ctr == 10:
        print 'EXITING'
        os._exit(0)
    timer.again()
    loop.run(pyuv.UV_RUN_NOWAIT)
jmooreoliva commented 10 years ago

I traced the issue down to getaddrinfo. It appears that once it has been run, python will not exit. (NOTE: On a restart of my computer and reinstall of pyuv, it worked the first time, then failed on subsequent attempts).

I can replicate this both using gruvi, and using pyuv directly. Any idea what is wrong?

test_gruvi_addr_info.py

import logging
import os
import argparse
import socket
import sys

import gruvi
from gruvi import get_hub
from gruvi.address import getaddrinfo

logging.basicConfig()

def do_stop():
    ret = getaddrinfo('127.0.0.1', 8443, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, 0)
    print ret
    gruvi.sleep(5)
    print 'stopping'

    hub = get_hub()
    hub.close()
    print 'exiting'

    os._exit(0)

gruvi.spawn(do_stop)

hub = get_hub()
print 'Press CTRL-C to exit'
hub.switch()

pyuv_output

C:\svn\io_software\Releases\trunk\IODataCenter.Monitor\v3\nerelay\relay>python t
estpyuvaddrinfo.py
[addrinfo_result(family=2, socktype=1, proto=6, canonname='', sockaddr=('74.125.
239.48', 8443)), addrinfo_result(family=2, socktype=1, proto=6, canonname='', so
ckaddr=('74.125.239.49', 8443)), addrinfo_result(family=2, socktype=1, proto=6,
canonname='', sockaddr=('74.125.239.50', 8443)), addrinfo_result(family=2, sockt
ype=1, proto=6, canonname='', sockaddr=('74.125.239.51', 8443)), addrinfo_result
(family=2, socktype=1, proto=6, canonname='', sockaddr=('74.125.239.52', 8443))]

None
EXITING
(Hangs here until process is killed)

test_pyuv_addr_info.py

import os
import socket
import pyuv

loop = pyuv.Loop.default_loop()
timer = pyuv.Timer(loop)
timer.start(lambda *args: None, 0.0, 0.05)

can_shut_down = False
def addr_callback(result, errorno):
    print result
    print errorno

    global can_shut_down
    can_shut_down = True

#request = pyuv.util.getaddrinfo(loop, addr_callback, node, service, family, socktype, protocol, flags)
request = pyuv.util.getaddrinfo(loop, addr_callback, '127.0.0.1', 8443, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, 0)

while not can_shut_down:
    timer.again()
    #loop.run_once()
    loop.run(pyuv.UV_RUN_NOWAIT)

loop.stop()

loop.run(pyuv.UV_RUN_DEFAULT)

print 'EXITING'
os._exit(0)
geertj commented 10 years ago

@jmooreoliva thanks for reporting!

I've submitted this as saghul/pyuv#171.

geertj commented 10 years ago

This is fixed by moving to a newer pyuv in 61dbb4e56f530df07e90f04340db774a018fdac0.