mirror of https://github.com/python/cpython
bpo-31556: asyncio.wait_for can cancel futures faster with timeout <= 0 (#3703)
This commit is contained in:
parent
11045c9d8a
commit
4d07189788
|
@ -334,6 +334,15 @@ def wait_for(fut, timeout, *, loop=None):
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
return (yield from fut)
|
return (yield from fut)
|
||||||
|
|
||||||
|
if timeout <= 0:
|
||||||
|
fut = ensure_future(fut, loop=loop)
|
||||||
|
|
||||||
|
if fut.done():
|
||||||
|
return fut.result()
|
||||||
|
|
||||||
|
fut.cancel()
|
||||||
|
raise futures.TimeoutError()
|
||||||
|
|
||||||
waiter = loop.create_future()
|
waiter = loop.create_future()
|
||||||
timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
|
timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
|
||||||
cb = functools.partial(_release_waiter, waiter)
|
cb = functools.partial(_release_waiter, waiter)
|
||||||
|
|
|
@ -661,6 +661,76 @@ class BaseTaskTests:
|
||||||
t.cancel()
|
t.cancel()
|
||||||
self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t)
|
self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t)
|
||||||
|
|
||||||
|
def test_wait_for_timeout_less_then_0_or_0_future_done(self):
|
||||||
|
def gen():
|
||||||
|
when = yield
|
||||||
|
self.assertAlmostEqual(0, when)
|
||||||
|
|
||||||
|
loop = self.new_test_loop(gen)
|
||||||
|
|
||||||
|
fut = self.new_future(loop)
|
||||||
|
fut.set_result('done')
|
||||||
|
|
||||||
|
ret = loop.run_until_complete(asyncio.wait_for(fut, 0, loop=loop))
|
||||||
|
|
||||||
|
self.assertEqual(ret, 'done')
|
||||||
|
self.assertTrue(fut.done())
|
||||||
|
self.assertAlmostEqual(0, loop.time())
|
||||||
|
|
||||||
|
def test_wait_for_timeout_less_then_0_or_0_coroutine_do_not_started(self):
|
||||||
|
def gen():
|
||||||
|
when = yield
|
||||||
|
self.assertAlmostEqual(0, when)
|
||||||
|
|
||||||
|
loop = self.new_test_loop(gen)
|
||||||
|
|
||||||
|
foo_started = False
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def foo():
|
||||||
|
nonlocal foo_started
|
||||||
|
foo_started = True
|
||||||
|
|
||||||
|
with self.assertRaises(asyncio.TimeoutError):
|
||||||
|
loop.run_until_complete(asyncio.wait_for(foo(), 0, loop=loop))
|
||||||
|
|
||||||
|
self.assertAlmostEqual(0, loop.time())
|
||||||
|
self.assertEqual(foo_started, False)
|
||||||
|
|
||||||
|
def test_wait_for_timeout_less_then_0_or_0(self):
|
||||||
|
def gen():
|
||||||
|
when = yield
|
||||||
|
self.assertAlmostEqual(0.2, when)
|
||||||
|
when = yield 0
|
||||||
|
self.assertAlmostEqual(0, when)
|
||||||
|
|
||||||
|
for timeout in [0, -1]:
|
||||||
|
with self.subTest(timeout=timeout):
|
||||||
|
loop = self.new_test_loop(gen)
|
||||||
|
|
||||||
|
foo_running = None
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def foo():
|
||||||
|
nonlocal foo_running
|
||||||
|
foo_running = True
|
||||||
|
try:
|
||||||
|
yield from asyncio.sleep(0.2, loop=loop)
|
||||||
|
finally:
|
||||||
|
foo_running = False
|
||||||
|
return 'done'
|
||||||
|
|
||||||
|
fut = self.new_task(loop, foo())
|
||||||
|
|
||||||
|
with self.assertRaises(asyncio.TimeoutError):
|
||||||
|
loop.run_until_complete(asyncio.wait_for(
|
||||||
|
fut, timeout, loop=loop))
|
||||||
|
self.assertTrue(fut.done())
|
||||||
|
# it should have been cancelled due to the timeout
|
||||||
|
self.assertTrue(fut.cancelled())
|
||||||
|
self.assertAlmostEqual(0, loop.time())
|
||||||
|
self.assertEqual(foo_running, False)
|
||||||
|
|
||||||
def test_wait_for(self):
|
def test_wait_for(self):
|
||||||
|
|
||||||
def gen():
|
def gen():
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Cancel asyncio.wait_for future faster if timeout <= 0
|
Loading…
Reference in New Issue