Skip to content

Commit 40937f5

Browse files
Workaround for strange memory leak in Eventlet's Thread class (Fixes #328)
1 parent 541f172 commit 40937f5

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

src/engineio/async_drivers/eventlet.py

+23-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
from __future__ import absolute_import
22

3-
from eventlet.green.threading import Thread, Event
4-
from eventlet import queue
5-
from eventlet import sleep
3+
from eventlet.green.threading import Event
4+
from eventlet import queue, sleep, spawn
65
from eventlet.websocket import WebSocketWSGI as _WebSocketWSGI
76

87

8+
class EventletThread: # pragma: no cover
9+
"""Thread class that uses eventlet green threads.
10+
11+
Eventlet's own Thread class has a strange bug that causes _DummyThread
12+
objects to be created and leaked, since they are never garbage collected.
13+
"""
14+
def __init__(self, target, args=None, kwargs=None):
15+
self.target = target
16+
self.args = args or ()
17+
self.kwargs = kwargs or {}
18+
self.g = None
19+
20+
def start(self):
21+
self.g = spawn(self.target, *self.args, **self.kwargs)
22+
23+
def join(self):
24+
if self.g:
25+
return self.g.wait()
26+
27+
928
class WebSocketWSGI(_WebSocketWSGI):
1029
def __init__(self, handler, server):
1130
try:
@@ -26,7 +45,7 @@ def __call__(self, environ, start_response):
2645

2746

2847
_async = {
29-
'thread': Thread,
48+
'thread': EventletThread,
3049
'queue': queue.Queue,
3150
'queue_empty': queue.Empty,
3251
'event': Event,

tests/common/test_server.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,10 @@ def test_async_mode_eventlet(self):
126126
s = server.Server(async_mode='eventlet')
127127
assert s.async_mode == 'eventlet'
128128

129-
from eventlet.green import threading
130129
from eventlet import queue
131130
from engineio.async_drivers import eventlet as async_eventlet
132131

133-
assert s._async['thread'] == threading.Thread
132+
assert s._async['thread'] == async_eventlet.EventletThread
134133
assert s._async['queue'] == queue.Queue
135134
assert s._async['websocket'] == async_eventlet.WebSocketWSGI
136135

@@ -318,7 +317,7 @@ def test_on_event_invalid(self):
318317
s.on('invalid')
319318

320319
def test_trigger_event(self):
321-
s = server.Server()
320+
s = server.Server(async_mode='threading')
322321
f = {}
323322

324323
@s.on('connect')
@@ -1126,7 +1125,7 @@ def test_background_tasks(self):
11261125
def bg_task():
11271126
flag['task'] = True
11281127

1129-
s = server.Server()
1128+
s = server.Server(async_mode='threading')
11301129
task = s.start_background_task(bg_task)
11311130
task.join()
11321131
assert 'task' in flag
@@ -1162,15 +1161,15 @@ def test_log_error_once(self):
11621161
s.logger.info.assert_called_with('foo')
11631162

11641163
def test_service_task_started(self):
1165-
s = server.Server(monitor_clients=True)
1164+
s = server.Server(async_mode='threading', monitor_clients=True)
11661165
s._service_task = mock.MagicMock()
11671166
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'EIO=4'}
11681167
start_response = mock.MagicMock()
11691168
s.handle_request(environ, start_response)
11701169
s._service_task.assert_called_once_with()
11711170

11721171
def test_shutdown(self):
1173-
s = server.Server(monitor_clients=True)
1172+
s = server.Server(async_mode='threading', monitor_clients=True)
11741173
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'EIO=4'}
11751174
start_response = mock.MagicMock()
11761175
s.handle_request(environ, start_response)

0 commit comments

Comments
 (0)