From c5b065c3e4ffe76a3e8eb3b9668a38fd2c16f3a5 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Fri, 25 Jan 2019 09:25:06 +1300 Subject: eventletutils: Optimise EventletEvent.clear() When EventletEvent.clear() is called twice in succession without an intervening set(), there's no need to replace the underlying eventlet.event.Event object, since it has never been sent. Doing so would have woken other greenthreads waiting on the event to no particular end. When clear() is called after the event has been set(), we already did not do anything special with the existing eventlet.event.Event as we cannot call send() on it twice. We simply replace it with a new one; the code in wait() will handle the situation correctly, since it will wake up (due to the initial event having been sent) and begin waiting on the new eventlet.event.Event instead. This is consistent with the observed behaviour of threading.Event. A new unit test verifies this. Change-Id: Ibd5324926431fc760c3dd0be064324e3009cc2c2 --- oslo_utils/eventletutils.py | 10 +++------- oslo_utils/tests/test_eventletutils.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/oslo_utils/eventletutils.py b/oslo_utils/eventletutils.py index 5d3c0a6..2770de1 100644 --- a/oslo_utils/eventletutils.py +++ b/oslo_utils/eventletutils.py @@ -150,16 +150,12 @@ class EventletEvent(object): """ def __init__(self, *args, **kwargs): super(EventletEvent, self).__init__() - self._set = False self.clear() def clear(self): - old_event = getattr(self, "_event", None) - was_set = self._set - self._set = False - self._event = _eventlet.event.Event() - if old_event is not None and not was_set: - old_event.send(True) + if getattr(self, '_set', True): + self._set = False + self._event = _eventlet.event.Event() def is_set(self): return self._set diff --git a/oslo_utils/tests/test_eventletutils.py b/oslo_utils/tests/test_eventletutils.py index b6ce995..d96851c 100644 --- a/oslo_utils/tests/test_eventletutils.py +++ b/oslo_utils/tests/test_eventletutils.py @@ -201,6 +201,30 @@ class EventletUtilsTest(test_base.BaseTestCase): with eventlet.timeout.Timeout(0.7): b.wait() + def test_event_set_clear_timeout(self): + event = eventletutils.EventletEvent() + wakes = [] + + def thread_func(): + result = event.wait(0.2) + wakes.append(result) + if len(wakes) == 1: + self.assertTrue(result) + event.clear() + else: + self.assertFalse(result) + + a = greenthread.spawn(thread_func) + b = greenthread.spawn(thread_func) + eventlet.sleep(0) # start threads + event.set() + + with eventlet.timeout.Timeout(0.3): + a.wait() + b.wait() + self.assertFalse(event.is_set()) + self.assertEqual([True, False], wakes) + @mock.patch('oslo_utils.eventletutils._eventlet.event.Event') def test_event_clear_already_sent(self, mock_event): old_event = mock.Mock() -- cgit v1.2.1