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
This commit is contained in:
Zane Bitter 2019-01-25 09:25:06 +13:00
parent 4eb61941f8
commit c5b065c3e4
2 changed files with 27 additions and 7 deletions

View File

@ -150,16 +150,12 @@ class EventletEvent(object):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(EventletEvent, self).__init__() super(EventletEvent, self).__init__()
self._set = False
self.clear() self.clear()
def clear(self): def clear(self):
old_event = getattr(self, "_event", None) if getattr(self, '_set', True):
was_set = self._set self._set = False
self._set = False self._event = _eventlet.event.Event()
self._event = _eventlet.event.Event()
if old_event is not None and not was_set:
old_event.send(True)
def is_set(self): def is_set(self):
return self._set return self._set

View File

@ -201,6 +201,30 @@ class EventletUtilsTest(test_base.BaseTestCase):
with eventlet.timeout.Timeout(0.7): with eventlet.timeout.Timeout(0.7):
b.wait() 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') @mock.patch('oslo_utils.eventletutils._eventlet.event.Event')
def test_event_clear_already_sent(self, mock_event): def test_event_clear_already_sent(self, mock_event):
old_event = mock.Mock() old_event = mock.Mock()