8000 Merge branch '867-fix-eventlet-shutdown' · alex-python/gunicorn@2f6aa75 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2f6aa75

Browse files
committed
Merge branch '867-fix-eventlet-shutdown'
2 parents fcba1a6 + 2aabf48 commit 2f6aa75

File tree

1 file changed

+53
-10
lines changed

1 file changed

+53
-10
lines changed

gunicorn/workers/geventlet.py

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from functools import partial
77
import errno
8+
import sys
89

910
try:
1011
import eventlet
@@ -16,13 +17,15 @@
1617
raise RuntimeError("You need eventlet >= 0.9.7")
1718

1819

19-
from eventlet import hubs
20+
from eventlet import hubs, greenthread
2021
from eventlet.greenio import GreenSocket
2122
from eventlet.hubs import trampoline
23+
import greenlet
2224

2325
from gunicorn.http.wsgi import sendfile as o_sendfile
2426
from gunicorn.workers.async import AsyncWorker
2527

28+
2629
def _eventlet_sendfile(fdout, fdin, offset, nbytes):
2730
while True:
2831
try:
@@ -33,12 +36,54 @@ def _eventlet_sendfile(fdout, fdin, offset, nbytes):
3336
else:
3437
raise
3538

39+
40+
def _eventlet_serve(sock, handle, concurrency):
41+
"""
42+
Serve requests forever.
43+
44+
This code is nearly identical to ``eventlet.convenience.serve`` except
45+
that it attempts to join the pool at the end, which allows for gunicorn
46+
graceful shutdowns.
47+
"""
48+
pool = eventlet.greenpool.GreenPool(concurrency)
49+
server_gt = eventlet.greenthread.getcurrent()
50+
51+
while True:
52+
try:
53+
conn, addr = sock.accept()
54+
gt = pool.spawn(handle, conn, addr)
55+
gt.link(_eventlet_stop, server_gt, conn)
56+
conn, addr, gt = None, None, None
57+
except eventlet.StopServe:
58+
pool.waitall()
59+
return
60+
61+
62+
def _eventlet_stop(client, server, conn):
63+
"""
64+
Stop a greenlet handling a request and close its connection.
65+
66+
This code is lifted from eventlet so as not to depend on undocumented
67+
functions in the library.
68+
"""
69+
try:
70+
try:
71+
client.wait()
72+
finally:
73+
conn.close()
74+
except greenlet.GreenletExit:
75+
pass
76+
except Exception:
77+
greenthread.kill(server, *sys.exc_info())
78+
79+
3680
def patch_sendfile():
3781
from gunicorn.http import wsgi
3882

3983
if o_sendfile is not None:
4084
setattr(wsgi, "sendfile", _eventlet_sendfile)
4185

86+
4287
class EventletWorker(AsyncWorker):
4388

4489
def patch(self):
@@ -56,21 +101,18 @@ def timeout_ctx(self):
56101
def handle(self, listener, client, addr):
57102
if self.cfg.is_ssl:
58103
client = eventlet.wrap_ssl(client, server_side=True,
59-
**self.cfg.ssl_options)
104+
**self.cfg.ssl_options)
60105

61106
super(EventletWorker, self).handle(listener, client, addr)
62107

63-
if not self.alive:
64-
raise eventlet.StopServe()
65-
66108
def run(self):
67109
acceptors = []
68110
for sock in self.sockets:
69-
sock = GreenSocket(sock)
70-
sock.setblocking(1)
71-
hfun = partial(self.handle, sock)
72-
acceptor = eventlet.spawn(eventlet.serve, sock, hfun,
73-
self.worker_connections)
111+
gsock = GreenSocket(sock)
112+
gsock.setblocking(1)
113+
hfun = partial(self.handle, gsock)
114+
acceptor = eventlet.spawn(_eventlet_serve, gsock, hfun,
115+
self.worker_connections)
74116

75117
acceptors.append(acceptor)
76118
eventlet.sleep(0.0)
@@ -82,6 +124,7 @@ def run(self):
82124
self.notify()
83125
try:
84126
with eventlet.Timeout(self.cfg.graceful_timeout) as t:
127+
[a.kill(eventlet.StopServe()) for a in acceptors]
85128
[a.wait() for a in acceptors]
86129
except eventlet.Timeout as te:
87130
if te != t:

0 commit comments

Comments
 (0)
0