From 073da9803afcc530734981e1130ca22ecc6bca3b Mon Sep 17 00:00:00 2001 From: hellysmile Date: Fri, 22 Sep 2017 23:49:17 +0300 Subject: [PATCH 1/7] Cancel asyncio.wait_for faster when timeout==0 --- Lib/asyncio/tasks.py | 5 +++ Lib/test/test_asyncio/test_tasks.py | 31 +++++++++++++++++++ .../2017-09-22-23-48-49.bpo-31556.9J0u5H.rst | 1 + 3 files changed, 37 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 575d205404ae394..ef8afcd62f1cd71 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -334,6 +334,11 @@ def wait_for(fut, timeout, *, loop=None): if timeout is None: return (yield from fut) + if timeout == 0: + fut = ensure_future(fut, loop=loop) + fut.cancel() + raise futures.TimeoutError() + waiter = loop.create_future() timeout_handle = loop.call_later(timeout, _release_waiter, waiter) cb = functools.partial(_release_waiter, waiter) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 36082ec7c6a79a3..8c06ced58ddb890 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -661,6 +661,37 @@ def task(): t.cancel() self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t) + def test_wait_for_timeout_equals_0(self): + def gen(): + when = yield + self.assertAlmostEqual(0.2, when) + when = yield 0 + self.assertAlmostEqual(0, when) + + 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, 0, 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 gen(): diff --git a/Misc/NEWS.d/next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst b/Misc/NEWS.d/next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst new file mode 100644 index 000000000000000..28d527771136b81 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst @@ -0,0 +1 @@ +Cancel asyncio.wait_for future faster if timeout==0 From 7bfd99f7db83cf3cdd15aba6f7231a8b1f842c4c Mon Sep 17 00:00:00 2001 From: hellysmile Date: Sat, 23 Sep 2017 00:20:07 +0300 Subject: [PATCH 2/7] Change computed timeouts behaviour for timeout<=0 --- Lib/asyncio/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index ef8afcd62f1cd71..66ddd881f59bb93 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -334,7 +334,7 @@ def wait_for(fut, timeout, *, loop=None): if timeout is None: return (yield from fut) - if timeout == 0: + if timeout <= 0: fut = ensure_future(fut, loop=loop) fut.cancel() raise futures.TimeoutError() From cbac0f4d78ae719e6cc5145820e581cbf803f470 Mon Sep 17 00:00:00 2001 From: hellysmile Date: Sat, 23 Sep 2017 00:25:41 +0300 Subject: [PATCH 3/7] Fix news entry for timeout <= 0 --- .../next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst b/Misc/NEWS.d/next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst index 28d527771136b81..2e6b0284696c748 100644 --- a/Misc/NEWS.d/next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst +++ b/Misc/NEWS.d/next/Library/2017-09-22-23-48-49.bpo-31556.9J0u5H.rst @@ -1 +1 @@ -Cancel asyncio.wait_for future faster if timeout==0 +Cancel asyncio.wait_for future faster if timeout <= 0 From 60530660b02b723864a040cd83ecf186820d87e9 Mon Sep 17 00:00:00 2001 From: hellysmile Date: Sat, 23 Sep 2017 00:29:17 +0300 Subject: [PATCH 4/7] Added test for asyncio.wait_for timeout=-1 --- Lib/test/test_asyncio/test_tasks.py | 45 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 8c06ced58ddb890..3d8dd91a979e53d 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -668,29 +668,32 @@ def gen(): when = yield 0 self.assertAlmostEqual(0, when) - loop = self.new_test_loop(gen) + for timeout in [0, -1]: + with self.subTest(timeout=timeout): + loop = self.new_test_loop(gen) - foo_running = None + 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, 0, 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) + @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): From e9026d5c68fb005ef6ed3ecae7d301c4093c32e3 Mon Sep 17 00:00:00 2001 From: hellysmile Date: Sat, 23 Sep 2017 00:54:08 +0300 Subject: [PATCH 5/7] Fix cancellation for done futures. --- Lib/asyncio/tasks.py | 3 +++ Lib/test/test_asyncio/test_tasks.py | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 66ddd881f59bb93..6139ea50c2e0778 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -335,6 +335,9 @@ def wait_for(fut, timeout, *, loop=None): return (yield from fut) if timeout <= 0: + if fut.done(): + return fut.result() + fut = ensure_future(fut, loop=loop) fut.cancel() raise futures.TimeoutError() diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 3d8dd91a979e53d..9d8b6a80f13936b 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -661,7 +661,21 @@ def task(): t.cancel() self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t) - def test_wait_for_timeout_equals_0(self): + 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') + + def test_wait_for_timeout_less_then_0_or_0(self): def gen(): when = yield self.assertAlmostEqual(0.2, when) From 9326df1bf94d781e3bf7d8b3140e6e3a405237b1 Mon Sep 17 00:00:00 2001 From: hellysmile Date: Sat, 23 Sep 2017 00:55:55 +0300 Subject: [PATCH 6/7] Fix fut can be coroutine --- Lib/asyncio/tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 6139ea50c2e0778..52fef181cecc66b 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -335,10 +335,11 @@ def wait_for(fut, timeout, *, loop=None): return (yield from fut) if timeout <= 0: + fut = ensure_future(fut, loop=loop) + if fut.done(): return fut.result() - fut = ensure_future(fut, loop=loop) fut.cancel() raise futures.TimeoutError() From 8b2b71d17757f6d8ff6091d067a12e17ec16c4e2 Mon Sep 17 00:00:00 2001 From: hellysmile Date: Sat, 23 Sep 2017 01:08:17 +0300 Subject: [PATCH 7/7] Added testcase for coroutine, that event not started --- Lib/test/test_asyncio/test_tasks.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 9d8b6a80f13936b..7ff56b560b692e7 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -674,6 +674,28 @@ def gen(): 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():