Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH] Eventloop: fix deadlock involving idle_add/call_soon (bug 617550)
Date: Fri, 05 May 2017 09:13:33
Message-Id: 20170505091253.14612-1-zmedico@gentoo.org
1 Guarantee that newly added idle_add/call_soon callbacks have an
2 opportunity to execute before the event loop decides to wait on
3 self._thread_condition without a timeout. This fixes a case where
4 the event loop would wait on self._thread_condition indefinitely,
5 even though a callback scheduled by the AsynchronousTask._async_wait
6 method needed to be executed first.
7
8 X-Gentoo-bug: 617550
9 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=617550
10 ---
11 pym/portage/util/_eventloop/EventLoop.py | 18 ++++++++++++++++--
12 1 file changed, 16 insertions(+), 2 deletions(-)
13
14 diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py
15 index 712838e..b7c8762 100644
16 --- a/pym/portage/util/_eventloop/EventLoop.py
17 +++ b/pym/portage/util/_eventloop/EventLoop.py
18 @@ -108,6 +108,15 @@ class EventLoop(object):
19 self._poll_event_handler_ids = {}
20 # Increment id for each new handler.
21 self._event_handler_id = 0
22 + # New call_soon callbacks must have an opportunity to
23 + # execute before it's safe to wait on self._thread_condition
24 + # without a timeout, since delaying its execution indefinitely
25 + # could lead to a deadlock. The following attribute stores the
26 + # event handler id of the most recently added call_soon callback.
27 + # If this attribute has changed since the last time that the
28 + # call_soon callbacks have been called, then it's not safe to
29 + # wait on self._thread_condition without a timeout.
30 + self._call_soon_id = 0
31 # Use OrderedDict in order to emulate the FIFO queue behavior
32 # of the AbstractEventLoop.call_soon method.
33 self._idle_callbacks = OrderedDict()
34 @@ -250,10 +259,15 @@ class EventLoop(object):
35
36 if not event_handlers:
37 with self._thread_condition:
38 + prev_call_soon_id = self._call_soon_id
39 if self._run_timeouts():
40 events_handled += 1
41 timeouts_checked = True
42 - if not event_handlers and not events_handled and may_block:
43 +
44 + call_soon = bool(prev_call_soon_id != self._call_soon_id)
45 +
46 + if (not call_soon and not event_handlers
47 + and not events_handled and may_block):
48 # Block so that we don't waste cpu time by looping too
49 # quickly. This makes EventLoop useful for code that needs
50 # to wait for timeout callbacks regardless of whether or
51 @@ -457,7 +471,7 @@ class EventLoop(object):
52 @return: an integer ID
53 """
54 with self._thread_condition:
55 - source_id = self._new_source_id()
56 + source_id = self._call_soon_id = self._new_source_id()
57 self._idle_callbacks[source_id] = self._idle_callback_class(
58 args=args, callback=callback, source_id=source_id)
59 self._thread_condition.notify()
60 --
61 2.10.2

Replies