Repository: benoitc/gunicorn

Issue:
Remove time.sleep for gevent
When using gevent worker, time.sleep will block under gevent.
Please review, thanks.

Sample output:

```
[2015-10-15 11:02:45 +0800] [2526] [INFO] Starting gunicorn 19.3.0
[2015-10-15 11:02:45 +0800] [2526] [INFO] Listening at: http://0.0.0.0:8080 (2526)
[2015-10-15 11:02:45 +0800] [2526] [INFO] Using worker: dae.gworkers.ggevent.GeventWorker
[2015-10-15 11:02:45 +0800] [2529] [INFO] Booting worker with pid: 2529
[2015-10-15 11:02:45 +0800] [2530] [INFO] Booting worker with pid: 2530
^CTraceback (most recent call last):
  File "/Users/xtao/Work/douban/dae/venv/src/gunicorn/gunicorn/workers/base.py", line 158, in handle_quit
Traceback (most recent call last):
    time.sleep(0.1)
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 75, in sleep
  File "/Users/xtao/Work/douban/dae/venv/src/gunicorn/gunicorn/workers/base.py", line 158, in handle_quit
    time.sleep(0.1)
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 75, in sleep
    hub.wait(loop.timer(seconds, ref=ref))
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 341, in wait
    result = waiter.get()
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 568, in get
    return self.hub.switch()
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 330, in switch
    switch_out()
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 334, in switch_out
    raise AssertionError('Impossible to call blocking function in the event loop callback')
AssertionError: Impossible to call blocking function in the event loop callback
    hub.wait(loop.timer(seconds, ref=ref))
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 341, in wait
    result = waiter.get()
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 568, in get
    return self.hub.switch()
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 330, in switch
    switch_out()
  File "/Users/xtao/Work/douban/dae/venv/lib/python2.7/site-packages/gevent/hub.py", line 334, in switch_out
    raise AssertionError('Impossible to call blocking function in the event loop callback')
AssertionError: Impossible to call blocking function in the event loop callback
[2015-10-15 11:02:45 +0800] [2526] [INFO] Handling signal: int
[2015-10-15 11:02:45 +0800] [2530] [INFO] Worker exiting (pid: 2530)
[2015-10-15 11:02:45 +0800] [2529] [INFO] Worker exiting (pid: 2529)
[2015-10-15 11:02:46 +0800] [2526] [INFO] Shutting down: Master
```



Discussion:
Gunicorn calls `gevent.monkey.patch_all()` so I am not sure why this is a problem. Can you explain?

Also, this change effects all workers and this sleep is supposed to give a moment for the worker to shutdown maybe more cleanly or the worker_int callback to finish some scheduled work, I think. We should be careful about changing this for all workers without totally understanding why it's a problem for gevent.

This is basically a problem because `sleep` is called from a signal handler, and signal handler's can't use the gevent blocking APIs. Thus you get this exception, and instead of the process exiting cleanly, it exits uncleanly.

I suspected it might be something like that. Maybe this should be overridden in the geven worker to schedule an async task to invoke exit.


Resolving patch:
diff --git a/gunicorn/workers/ggevent.py b/gunicorn/workers/ggevent.py
--- a/gunicorn/workers/ggevent.py
+++ b/gunicorn/workers/ggevent.py
@@ -166,6 +166,11 @@ def handle_request(self, *args):
         except SystemExit:
             pass
 
+    def handle_quit(self, sig, frame):
+        # Move this out of the signal handler so we can use
+        # blocking calls. See #1126
+        gevent.spawn(super(GeventWorker, self).handle_quit, sig, frame)
+
     if gevent.version_info[0] == 0:
 
         def init_process(self):


Test patch: