Files
deb-python-eventlet/tests/test__pool.py
2010-02-23 19:51:09 -05:00

305 lines
9.6 KiB
Python

import eventlet
import warnings
warnings.simplefilter('ignore', DeprecationWarning)
from eventlet import pool, coros, api, hubs, timeout
warnings.simplefilter('default', DeprecationWarning)
from eventlet import event as _event
from tests import LimitedTestCase
from unittest import main
class TestCoroutinePool(LimitedTestCase):
klass = pool.Pool
def test_execute_async(self):
done = _event.Event()
def some_work():
done.send()
pool = self.klass(0, 2)
pool.execute_async(some_work)
done.wait()
def test_execute(self):
value = 'return value'
def some_work():
return value
pool = self.klass(0, 2)
worker = pool.execute(some_work)
self.assertEqual(value, worker.wait())
def test_waiting(self):
pool = self.klass(0,1)
done = _event.Event()
def consume():
done.wait()
def waiter(pool):
evt = pool.execute(consume)
evt.wait()
waiters = []
waiters.append(eventlet.spawn(waiter, pool))
api.sleep(0)
self.assertEqual(pool.waiting(), 0)
waiters.append(eventlet.spawn(waiter, pool))
api.sleep(0)
self.assertEqual(pool.waiting(), 1)
waiters.append(eventlet.spawn(waiter, pool))
api.sleep(0)
self.assertEqual(pool.waiting(), 2)
done.send(None)
for w in waiters:
w.wait()
self.assertEqual(pool.waiting(), 0)
def test_multiple_coros(self):
evt = _event.Event()
results = []
def producer():
results.append('prod')
evt.send()
def consumer():
results.append('cons1')
evt.wait()
results.append('cons2')
pool = self.klass(0, 2)
done = pool.execute(consumer)
pool.execute_async(producer)
done.wait()
self.assertEquals(['cons1', 'prod', 'cons2'], results)
def test_timer_cancel(self):
# this test verifies that local timers are not fired
# outside of the context of the execute method
timer_fired = []
def fire_timer():
timer_fired.append(True)
def some_work():
hubs.get_hub().schedule_call_local(0, fire_timer)
pool = self.klass(0, 2)
worker = pool.execute(some_work)
worker.wait()
api.sleep(0)
self.assertEquals(timer_fired, [])
def test_reentrant(self):
pool = self.klass(0,1)
def reenter():
waiter = pool.execute(lambda a: a, 'reenter')
self.assertEqual('reenter', waiter.wait())
outer_waiter = pool.execute(reenter)
outer_waiter.wait()
evt = _event.Event()
def reenter_async():
pool.execute_async(lambda a: a, 'reenter')
evt.send('done')
pool.execute_async(reenter_async)
evt.wait()
def assert_pool_has_free(self, pool, num_free):
def wait_long_time(e):
e.wait()
timer = timeout.Timeout(1, api.TimeoutError)
try:
evt = _event.Event()
for x in xrange(num_free):
pool.execute(wait_long_time, evt)
# if the pool has fewer free than we expect,
# then we'll hit the timeout error
finally:
timer.cancel()
# if the runtime error is not raised it means the pool had
# some unexpected free items
timer = timeout.Timeout(0, RuntimeError)
self.assertRaises(RuntimeError, pool.execute, wait_long_time, evt)
# clean up by causing all the wait_long_time functions to return
evt.send(None)
api.sleep(0)
api.sleep(0)
def test_resize(self):
pool = self.klass(max_size=2)
evt = _event.Event()
def wait_long_time(e):
e.wait()
pool.execute(wait_long_time, evt)
pool.execute(wait_long_time, evt)
self.assertEquals(pool.free(), 0)
self.assert_pool_has_free(pool, 0)
# verify that the pool discards excess items put into it
pool.resize(1)
# cause the wait_long_time functions to return, which will
# trigger puts to the pool
evt.send(None)
api.sleep(0)
api.sleep(0)
self.assertEquals(pool.free(), 1)
self.assert_pool_has_free(pool, 1)
# resize larger and assert that there are more free items
pool.resize(2)
self.assertEquals(pool.free(), 2)
self.assert_pool_has_free(pool, 2)
def test_stderr_raising(self):
# testing that really egregious errors in the error handling code
# (that prints tracebacks to stderr) don't cause the pool to lose
# any members
import sys
pool = self.klass(min_size=1, max_size=1)
def crash(*args, **kw):
raise RuntimeError("Whoa")
class FakeFile(object):
write = crash
# we're going to do this by causing the traceback.print_exc in
# safe_apply to raise an exception and thus exit _main_loop
normal_err = sys.stderr
try:
sys.stderr = FakeFile()
waiter = pool.execute(crash)
self.assertRaises(RuntimeError, waiter.wait)
# the pool should have something free at this point since the
# waiter returned
# pool.Pool change: if an exception is raised during execution of a link,
# the rest of the links are scheduled to be executed on the next hub iteration
# this introduces a delay in updating pool.sem which makes pool.free() report 0
# therefore, sleep:
api.sleep(0)
self.assertEqual(pool.free(), 1)
# shouldn't block when trying to get
t = timeout.Timeout(0.1)
try:
pool.execute(api.sleep, 1)
finally:
t.cancel()
finally:
sys.stderr = normal_err
def test_track_events(self):
pool = self.klass(track_events=True)
for x in range(6):
pool.execute(lambda n: n, x)
for y in range(6):
pool.wait()
def test_track_slow_event(self):
pool = self.klass(track_events=True)
def slow():
api.sleep(0.1)
return 'ok'
pool.execute(slow)
self.assertEquals(pool.wait(), 'ok')
def test_pool_smash(self):
# The premise is that a coroutine in a Pool tries to get a token out
# of a token pool but times out before getting the token. We verify
# that neither pool is adversely affected by this situation.
from eventlet import pools
pool = self.klass(min_size=1, max_size=1)
tp = pools.TokenPool(max_size=1)
token = tp.get() # empty pool
def do_receive(tp):
timeout.Timeout(0, RuntimeError())
try:
t = tp.get()
self.fail("Shouldn't have recieved anything from the pool")
except RuntimeError:
return 'timed out'
# the execute makes the token pool expect that coroutine, but then
# immediately cuts bait
e1 = pool.execute(do_receive, tp)
self.assertEquals(e1.wait(), 'timed out')
# the pool can get some random item back
def send_wakeup(tp):
tp.put('wakeup')
api.spawn(send_wakeup, tp)
# now we ask the pool to run something else, which should not
# be affected by the previous send at all
def resume():
return 'resumed'
e2 = pool.execute(resume)
self.assertEquals(e2.wait(), 'resumed')
# we should be able to get out the thing we put in there, too
self.assertEquals(tp.get(), 'wakeup')
class PoolBasicTests(LimitedTestCase):
klass = pool.Pool
def test_execute_async(self):
p = self.klass(max_size=2)
self.assertEqual(p.free(), 2)
r = []
def foo(a):
r.append(a)
evt = p.execute(foo, 1)
self.assertEqual(p.free(), 1)
evt.wait()
self.assertEqual(r, [1])
api.sleep(0)
self.assertEqual(p.free(), 2)
#Once the pool is exhausted, calling an execute forces a yield.
p.execute_async(foo, 2)
self.assertEqual(1, p.free())
self.assertEqual(r, [1])
p.execute_async(foo, 3)
self.assertEqual(0, p.free())
self.assertEqual(r, [1])
p.execute_async(foo, 4)
self.assertEqual(r, [1,2,3])
api.sleep(0)
self.assertEqual(r, [1,2,3,4])
def test_execute(self):
p = self.klass()
evt = p.execute(lambda a: ('foo', a), 1)
self.assertEqual(evt.wait(), ('foo', 1))
def test_with_intpool(self):
from eventlet import pools
class IntPool(pools.Pool):
def create(self):
self.current_integer = getattr(self, 'current_integer', 0) + 1
return self.current_integer
def subtest(intpool_size, pool_size, num_executes):
def run(int_pool):
token = int_pool.get()
api.sleep(0.0001)
int_pool.put(token)
return token
int_pool = IntPool(max_size=intpool_size)
pool = self.klass(max_size=pool_size)
for ix in xrange(num_executes):
pool.execute(run, int_pool)
pool.waitall()
subtest(4, 7, 7)
subtest(50, 75, 100)
for isize in (20, 30, 40, 50):
for psize in (25, 35, 50):
subtest(isize, psize, psize)
if __name__=='__main__':
main()