5
5
6
6
from functools import partial
7
7
import errno
8
+ import sys
8
9
9
10
try :
10
11
import eventlet
16
17
raise RuntimeError ("You need eventlet >= 0.9.7" )
17
18
18
19
19
- from eventlet import hubs
20
+ from eventlet import hubs , greenthread
20
21
from eventlet .greenio import GreenSocket
21
22
from eventlet .hubs import trampoline
23
+ import greenlet
22
24
23
25
from gunicorn .http .wsgi import sendfile as o_sendfile
24
26
from gunicorn .workers .async import AsyncWorker
25
27
28
+
26
29
def _eventlet_sendfile (fdout , fdin , offset , nbytes ):
27
30
while True :
28
31
try :
@@ -33,12 +36,54 @@ def _eventlet_sendfile(fdout, fdin, offset, nbytes):
33
36
else :
34
37
raise
35
38
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
+
36
80
def patch_sendfile ():
37
81
from gunicorn .http import wsgi
38
82
39
83
if o_sendfile is not None :
40
84
setattr (wsgi , "sendfile" , _eventlet_sendfile )
41
85
86
+
42
87
class EventletWorker (AsyncWorker ):
43
88
44
89
def patch (self ):
@@ -56,21 +101,18 @@ def timeout_ctx(self):
56
101
def handle (self , listener , client , addr ):
57
102
if self .cfg .is_ssl :
58
103
client = eventlet .wrap_ssl (client , server_side = True ,
59
- ** self .cfg .ssl_options )
104
+ ** self .cfg .ssl_options )
60
105
61
106
super (EventletWorker , self ).handle (listener , client , addr )
62
107
63
- if not self .alive :
64
- raise eventlet .StopServe ()
65
-
66
108
def run (self ):
67
109
acceptors = []
68
110
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 )
74
116
75
117
acceptors .append (acceptor )
76
118
eventlet .sleep (0.0 )
@@ -82,6 +124,7 @@ def run(self):
82
124
self .notify ()
83
125
try :
84
126
with eventlet .Timeout (self .cfg .graceful_timeout ) as t :
127
+ [a .kill (eventlet .StopServe ()) for a in acceptors ]
85
128
[a .wait () for a in acceptors ]
86
129
except eventlet .Timeout as te :
87
130
if te != t :
0 commit comments