mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
asyncio: Improve canceled timer handles cleanup. Closes issue #22448.
Patch by Joshua Moore-Oliva.
This commit is contained in:
parent
bca9acf7ca
commit
1cfd36fdd7
5 changed files with 148 additions and 26 deletions
|
@ -2,6 +2,7 @@
|
|||
|
||||
import errno
|
||||
import logging
|
||||
import math
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
@ -73,13 +74,6 @@ class BaseEventLoopTests(test_utils.TestCase):
|
|||
self.assertFalse(self.loop._scheduled)
|
||||
self.assertIn(h, self.loop._ready)
|
||||
|
||||
def test__add_callback_timer(self):
|
||||
h = asyncio.TimerHandle(time.monotonic()+10, lambda: False, (),
|
||||
self.loop)
|
||||
|
||||
self.loop._add_callback(h)
|
||||
self.assertIn(h, self.loop._scheduled)
|
||||
|
||||
def test__add_callback_cancelled_handle(self):
|
||||
h = asyncio.Handle(lambda: False, (), self.loop)
|
||||
h.cancel()
|
||||
|
@ -283,6 +277,82 @@ class BaseEventLoopTests(test_utils.TestCase):
|
|||
self.assertTrue(processed)
|
||||
self.assertEqual([handle], list(self.loop._ready))
|
||||
|
||||
def test__run_once_cancelled_event_cleanup(self):
|
||||
self.loop._process_events = mock.Mock()
|
||||
|
||||
self.assertTrue(
|
||||
0 < base_events._MIN_CANCELLED_TIMER_HANDLES_FRACTION < 1.0)
|
||||
|
||||
def cb():
|
||||
pass
|
||||
|
||||
# Set up one "blocking" event that will not be cancelled to
|
||||
# ensure later cancelled events do not make it to the head
|
||||
# of the queue and get cleaned.
|
||||
not_cancelled_count = 1
|
||||
self.loop.call_later(3000, cb)
|
||||
|
||||
# Add less than threshold (base_events._MIN_SCHEDULED_TIMER_HANDLES)
|
||||
# cancelled handles, ensure they aren't removed
|
||||
|
||||
cancelled_count = 2
|
||||
for x in range(2):
|
||||
h = self.loop.call_later(3600, cb)
|
||||
h.cancel()
|
||||
|
||||
# Add some cancelled events that will be at head and removed
|
||||
cancelled_count += 2
|
||||
for x in range(2):
|
||||
h = self.loop.call_later(100, cb)
|
||||
h.cancel()
|
||||
|
||||
# This test is invalid if _MIN_SCHEDULED_TIMER_HANDLES is too low
|
||||
self.assertLessEqual(cancelled_count + not_cancelled_count,
|
||||
base_events._MIN_SCHEDULED_TIMER_HANDLES)
|
||||
|
||||
self.assertEqual(self.loop._timer_cancelled_count, cancelled_count)
|
||||
|
||||
self.loop._run_once()
|
||||
|
||||
cancelled_count -= 2
|
||||
|
||||
self.assertEqual(self.loop._timer_cancelled_count, cancelled_count)
|
||||
|
||||
self.assertEqual(len(self.loop._scheduled),
|
||||
cancelled_count + not_cancelled_count)
|
||||
|
||||
# Need enough events to pass _MIN_CANCELLED_TIMER_HANDLES_FRACTION
|
||||
# so that deletion of cancelled events will occur on next _run_once
|
||||
add_cancel_count = int(math.ceil(
|
||||
base_events._MIN_SCHEDULED_TIMER_HANDLES *
|
||||
base_events._MIN_CANCELLED_TIMER_HANDLES_FRACTION)) + 1
|
||||
|
||||
add_not_cancel_count = max(base_events._MIN_SCHEDULED_TIMER_HANDLES -
|
||||
add_cancel_count, 0)
|
||||
|
||||
# Add some events that will not be cancelled
|
||||
not_cancelled_count += add_not_cancel_count
|
||||
for x in range(add_not_cancel_count):
|
||||
self.loop.call_later(3600, cb)
|
||||
|
||||
# Add enough cancelled events
|
||||
cancelled_count += add_cancel_count
|
||||
for x in range(add_cancel_count):
|
||||
h = self.loop.call_later(3600, cb)
|
||||
h.cancel()
|
||||
|
||||
# Ensure all handles are still scheduled
|
||||
self.assertEqual(len(self.loop._scheduled),
|
||||
cancelled_count + not_cancelled_count)
|
||||
|
||||
self.loop._run_once()
|
||||
|
||||
# Ensure cancelled events were removed
|
||||
self.assertEqual(len(self.loop._scheduled), not_cancelled_count)
|
||||
|
||||
# Ensure only uncancelled events remain scheduled
|
||||
self.assertTrue(all([not x._cancelled for x in self.loop._scheduled]))
|
||||
|
||||
def test_run_until_complete_type_error(self):
|
||||
self.assertRaises(TypeError,
|
||||
self.loop.run_until_complete, 'blah')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue