tpool: isolate internal socket from default timeout; Thanks to Alex Villacís Lasso

Since commit f37a87b the internal implementation of tpool uses sockets
instead of pipes for communication. If, for whatever reason, the user code
calls socket.setdefaulttimeout() with some nonzero value, tpool's internal
socket now inherits this timeout. This causes the tpool to receive
a `socket.timeout` exception where none was expected.
https://github.com/eventlet/eventlet/pull/330
This commit is contained in:
Sergey Shepelev
2016-08-10 11:14:25 +05:00
parent db415e58b3
commit cb8ac9b567
4 changed files with 52 additions and 34 deletions

View File

@@ -134,3 +134,4 @@ Thanks To
* Alexis Lee * Alexis Lee
* Steven Erenst * Steven Erenst
* Piët Delport * Piët Delport
* Alex Villacís Lasso

View File

@@ -276,9 +276,11 @@ def setup():
csock.connect(sock.getsockname()) csock.connect(sock.getsockname())
csock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) csock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
_wsock, _addr = sock.accept() _wsock, _addr = sock.accept()
_wsock.settimeout(None)
_wsock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) _wsock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
sock.close() sock.close()
_rsock = greenio.GreenSocket(csock) _rsock = greenio.GreenSocket(csock)
_rsock.settimeout(None)
for i in six.moves.range(_nthreads): for i in six.moves.range(_nthreads):
t = threading.Thread(target=tworker, t = threading.Thread(target=tworker,

View File

@@ -0,0 +1,15 @@
__test__ = False
if __name__ == '__main__':
import eventlet
import eventlet.tpool
import socket
def do():
eventlet.sleep(0.2)
return True
socket.setdefaulttimeout(0.05)
result = eventlet.tpool.execute(do)
assert result
print('pass')

View File

@@ -22,7 +22,7 @@ import time
import eventlet import eventlet
from eventlet import tpool, debug, event from eventlet import tpool, debug, event
from eventlet.support import six from eventlet.support import six
from tests import LimitedTestCase, skip_with_pyevent, main import tests
one = 1 one = 1
@@ -39,7 +39,7 @@ def raise_exception():
raise RuntimeError("hi") raise RuntimeError("hi")
class TestTpool(LimitedTestCase): class TestTpool(tests.LimitedTestCase):
def setUp(self): def setUp(self):
super(TestTpool, self).setUp() super(TestTpool, self).setUp()
@@ -47,7 +47,7 @@ class TestTpool(LimitedTestCase):
tpool.killall() tpool.killall()
super(TestTpool, self).tearDown() super(TestTpool, self).tearDown()
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_tuple(self): def test_wrap_tuple(self):
my_tuple = (1, 2) my_tuple = (1, 2)
prox = tpool.Proxy(my_tuple) prox = tpool.Proxy(my_tuple)
@@ -55,7 +55,7 @@ class TestTpool(LimitedTestCase):
self.assertEqual(prox[1], 2) self.assertEqual(prox[1], 2)
self.assertEqual(len(my_tuple), 2) self.assertEqual(len(my_tuple), 2)
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_string(self): def test_wrap_string(self):
my_object = "whatever" my_object = "whatever"
prox = tpool.Proxy(my_object) prox = tpool.Proxy(my_object)
@@ -63,7 +63,7 @@ class TestTpool(LimitedTestCase):
self.assertEqual(len(my_object), len(prox)) self.assertEqual(len(my_object), len(prox))
self.assertEqual(my_object.join(['a', 'b']), prox.join(['a', 'b'])) self.assertEqual(my_object.join(['a', 'b']), prox.join(['a', 'b']))
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_uniterable(self): def test_wrap_uniterable(self):
prox = tpool.Proxy([]) prox = tpool.Proxy([])
@@ -76,7 +76,7 @@ class TestTpool(LimitedTestCase):
self.assertRaises(IndexError, index) self.assertRaises(IndexError, index)
self.assertRaises(TypeError, key) self.assertRaises(TypeError, key)
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_dict(self): def test_wrap_dict(self):
my_object = {'a': 1} my_object = {'a': 1}
prox = tpool.Proxy(my_object) prox = tpool.Proxy(my_object)
@@ -85,7 +85,7 @@ class TestTpool(LimitedTestCase):
self.assertEqual(str(my_object), str(prox)) self.assertEqual(str(my_object), str(prox))
self.assertEqual(repr(my_object), repr(prox)) self.assertEqual(repr(my_object), repr(prox))
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_module_class(self): def test_wrap_module_class(self):
prox = tpool.Proxy(re) prox = tpool.Proxy(re)
self.assertEqual(tpool.Proxy, type(prox)) self.assertEqual(tpool.Proxy, type(prox))
@@ -93,7 +93,7 @@ class TestTpool(LimitedTestCase):
self.assertEqual(exp.groups, 3) self.assertEqual(exp.groups, 3)
assert repr(prox.compile) assert repr(prox.compile)
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_eq(self): def test_wrap_eq(self):
prox = tpool.Proxy(re) prox = tpool.Proxy(re)
exp1 = prox.compile('.') exp1 = prox.compile('.')
@@ -102,12 +102,12 @@ class TestTpool(LimitedTestCase):
exp3 = prox.compile('/') exp3 = prox.compile('/')
assert exp1 != exp3 assert exp1 != exp3
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_ints(self): def test_wrap_ints(self):
p = tpool.Proxy(4) p = tpool.Proxy(4)
assert p == 4 assert p == 4
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_hash(self): def test_wrap_hash(self):
prox1 = tpool.Proxy('' + 'A') prox1 = tpool.Proxy('' + 'A')
prox2 = tpool.Proxy('A' + '') prox2 = tpool.Proxy('A' + '')
@@ -118,7 +118,7 @@ class TestTpool(LimitedTestCase):
proxList = tpool.Proxy([]) proxList = tpool.Proxy([])
self.assertRaises(TypeError, hash, proxList) self.assertRaises(TypeError, hash, proxList)
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_nonzero(self): def test_wrap_nonzero(self):
prox = tpool.Proxy(re) prox = tpool.Proxy(re)
exp1 = prox.compile('.') exp1 = prox.compile('.')
@@ -126,7 +126,7 @@ class TestTpool(LimitedTestCase):
prox2 = tpool.Proxy([1, 2, 3]) prox2 = tpool.Proxy([1, 2, 3])
assert bool(prox2) assert bool(prox2)
@skip_with_pyevent @tests.skip_with_pyevent
def test_multiple_wraps(self): def test_multiple_wraps(self):
prox1 = tpool.Proxy(re) prox1 = tpool.Proxy(re)
prox2 = tpool.Proxy(re) prox2 = tpool.Proxy(re)
@@ -135,18 +135,18 @@ class TestTpool(LimitedTestCase):
del x2 del x2
prox2.compile('.') prox2.compile('.')
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_getitem(self): def test_wrap_getitem(self):
prox = tpool.Proxy([0, 1, 2]) prox = tpool.Proxy([0, 1, 2])
self.assertEqual(prox[0], 0) self.assertEqual(prox[0], 0)
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_setitem(self): def test_wrap_setitem(self):
prox = tpool.Proxy([0, 1, 2]) prox = tpool.Proxy([0, 1, 2])
prox[1] = 2 prox[1] = 2
self.assertEqual(prox[1], 2) self.assertEqual(prox[1], 2)
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_iterator(self): def test_wrap_iterator(self):
self.reset_timeout(2) self.reset_timeout(2)
prox = tpool.Proxy(range(10)) prox = tpool.Proxy(range(10))
@@ -155,7 +155,7 @@ class TestTpool(LimitedTestCase):
result.append(i) result.append(i)
self.assertEqual(list(range(10)), result) self.assertEqual(list(range(10)), result)
@skip_with_pyevent @tests.skip_with_pyevent
def test_wrap_iterator2(self): def test_wrap_iterator2(self):
self.reset_timeout(5) # might take a while due to imprecise sleeping self.reset_timeout(5) # might take a while due to imprecise sleeping
@@ -184,7 +184,7 @@ class TestTpool(LimitedTestCase):
assert counter[0] > 10, counter[0] assert counter[0] > 10, counter[0]
gt.kill() gt.kill()
@skip_with_pyevent @tests.skip_with_pyevent
def test_raising_exceptions(self): def test_raising_exceptions(self):
prox = tpool.Proxy(re) prox = tpool.Proxy(re)
@@ -196,7 +196,7 @@ class TestTpool(LimitedTestCase):
prox = tpool.Proxy(tpool_test) prox = tpool.Proxy(tpool_test)
self.assertRaises(RuntimeError, prox.raise_exception) self.assertRaises(RuntimeError, prox.raise_exception)
@skip_with_pyevent @tests.skip_with_pyevent
def test_variable_and_keyword_arguments_with_function_calls(self): def test_variable_and_keyword_arguments_with_function_calls(self):
import optparse import optparse
parser = tpool.Proxy(optparse.OptionParser()) parser = tpool.Proxy(optparse.OptionParser())
@@ -204,7 +204,7 @@ class TestTpool(LimitedTestCase):
opts, args = parser.parse_args(["-nfoo"]) opts, args = parser.parse_args(["-nfoo"])
self.assertEqual(opts.n, 'foo') self.assertEqual(opts.n, 'foo')
@skip_with_pyevent @tests.skip_with_pyevent
def test_contention(self): def test_contention(self):
from tests import tpool_test from tests import tpool_test
prox = tpool.Proxy(tpool_test) prox = tpool.Proxy(tpool_test)
@@ -216,19 +216,19 @@ class TestTpool(LimitedTestCase):
results = list(pile) results = list(pile)
self.assertEqual(len(results), 3) self.assertEqual(len(results), 3)
@skip_with_pyevent @tests.skip_with_pyevent
def test_timeout(self): def test_timeout(self):
import time import time
eventlet.Timeout(0.1, eventlet.TimeoutError()) eventlet.Timeout(0.1, eventlet.TimeoutError())
self.assertRaises(eventlet.TimeoutError, self.assertRaises(eventlet.TimeoutError,
tpool.execute, time.sleep, 0.3) tpool.execute, time.sleep, 0.3)
@skip_with_pyevent @tests.skip_with_pyevent
def test_killall(self): def test_killall(self):
tpool.killall() tpool.killall()
tpool.setup() tpool.setup()
@skip_with_pyevent @tests.skip_with_pyevent
def test_killall_remaining_results(self): def test_killall_remaining_results(self):
semaphore = event.Event() semaphore = event.Event()
@@ -244,7 +244,7 @@ class TestTpool(LimitedTestCase):
tpool.killall() tpool.killall()
gt.wait() gt.wait()
@skip_with_pyevent @tests.skip_with_pyevent
def test_autowrap(self): def test_autowrap(self):
x = tpool.Proxy({'a': 1, 'b': 2}, autowrap=(int,)) x = tpool.Proxy({'a': 1, 'b': 2}, autowrap=(int,))
assert isinstance(x.get('a'), tpool.Proxy) assert isinstance(x.get('a'), tpool.Proxy)
@@ -255,7 +255,7 @@ class TestTpool(LimitedTestCase):
assert isinstance(x.one, tpool.Proxy) assert isinstance(x.one, tpool.Proxy)
assert not isinstance(x.none, tpool.Proxy) assert not isinstance(x.none, tpool.Proxy)
@skip_with_pyevent @tests.skip_with_pyevent
def test_autowrap_names(self): def test_autowrap_names(self):
x = tpool.Proxy({'a': 1, 'b': 2}, autowrap_names=('get',)) x = tpool.Proxy({'a': 1, 'b': 2}, autowrap_names=('get',))
assert isinstance(x.get('a'), tpool.Proxy) assert isinstance(x.get('a'), tpool.Proxy)
@@ -265,7 +265,7 @@ class TestTpool(LimitedTestCase):
assert isinstance(x.one, tpool.Proxy) assert isinstance(x.one, tpool.Proxy)
assert not isinstance(x.two, tpool.Proxy) assert not isinstance(x.two, tpool.Proxy)
@skip_with_pyevent @tests.skip_with_pyevent
def test_autowrap_both(self): def test_autowrap_both(self):
from tests import tpool_test from tests import tpool_test
x = tpool.Proxy(tpool_test, autowrap=(int,), autowrap_names=('one',)) x = tpool.Proxy(tpool_test, autowrap=(int,), autowrap_names=('one',))
@@ -273,7 +273,7 @@ class TestTpool(LimitedTestCase):
# violating the abstraction to check that we didn't double-wrap # violating the abstraction to check that we didn't double-wrap
assert not isinstance(x._obj, tpool.Proxy) assert not isinstance(x._obj, tpool.Proxy)
@skip_with_pyevent @tests.skip_with_pyevent
def test_callable(self): def test_callable(self):
def wrapped(arg): def wrapped(arg):
return arg return arg
@@ -284,7 +284,7 @@ class TestTpool(LimitedTestCase):
assert isinstance(x(4), tpool.Proxy) assert isinstance(x(4), tpool.Proxy)
self.assertEqual("4", str(x(4))) self.assertEqual("4", str(x(4)))
@skip_with_pyevent @tests.skip_with_pyevent
def test_callable_iterator(self): def test_callable_iterator(self):
def wrapped(arg): def wrapped(arg):
yield arg yield arg
@@ -295,22 +295,22 @@ class TestTpool(LimitedTestCase):
for r in x(3): for r in x(3):
self.assertEqual(3, r) self.assertEqual(3, r)
@skip_with_pyevent @tests.skip_with_pyevent
def test_eventlet_timeout(self): def test_eventlet_timeout(self):
def raise_timeout(): def raise_timeout():
raise eventlet.Timeout() raise eventlet.Timeout()
self.assertRaises(eventlet.Timeout, tpool.execute, raise_timeout) self.assertRaises(eventlet.Timeout, tpool.execute, raise_timeout)
@skip_with_pyevent @tests.skip_with_pyevent
def test_tpool_set_num_threads(self): def test_tpool_set_num_threads(self):
tpool.set_num_threads(5) tpool.set_num_threads(5)
self.assertEqual(5, tpool._nthreads) self.assertEqual(5, tpool._nthreads)
class TpoolLongTests(LimitedTestCase): class TpoolLongTests(tests.LimitedTestCase):
TEST_TIMEOUT = 60 TEST_TIMEOUT = 60
@skip_with_pyevent @tests.skip_with_pyevent
def test_a_buncha_stuff(self): def test_a_buncha_stuff(self):
assert_ = self.assert_ assert_ = self.assert_
@@ -339,7 +339,7 @@ class TpoolLongTests(LimitedTestCase):
self.assertEqual(len(results), cnt) self.assertEqual(len(results), cnt)
tpool.killall() tpool.killall()
@skip_with_pyevent @tests.skip_with_pyevent
def test_leakage_from_tracebacks(self): def test_leakage_from_tracebacks(self):
tpool.execute(noop) # get it started tpool.execute(noop) # get it started
gc.collect() gc.collect()
@@ -362,5 +362,5 @@ class TpoolLongTests(LimitedTestCase):
tpool.killall() tpool.killall()
if __name__ == '__main__': def test_isolate_from_socket_default_timeout():
main() tests.run_isolated('tpool_isolate_socket_default_timeout.py', timeout=1)