commit:     dac5089eb7908e9fd643f46c913515082077281e
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Fri May  5 09:07:38 2017 +0000
Commit:     Brian Dolbec <dolsen <AT> gentoo <DOT> org>
CommitDate: Fri May  5 18:32:45 2017 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=dac5089e

Eventloop: fix deadlock involving idle_add/call_soon (bug 617550)

Guarantee that newly added idle_add/call_soon callbacks have an
opportunity to execute before the event loop decides to wait on
self._thread_condition without a timeout. This fixes a case where
the event loop would wait on self._thread_condition indefinitely,
even though a callback scheduled by the AsynchronousTask._async_wait
method needed to be executed first.

X-Gentoo-bug: 617550
X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=617550
Acked-by: Brian Dolbec <dolsen <AT> gentoo.org>

 pym/portage/util/_eventloop/EventLoop.py | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/pym/portage/util/_eventloop/EventLoop.py 
b/pym/portage/util/_eventloop/EventLoop.py
index 712838e3d..cd154005f 100644
--- a/pym/portage/util/_eventloop/EventLoop.py
+++ b/pym/portage/util/_eventloop/EventLoop.py
@@ -108,6 +108,15 @@ class EventLoop(object):
                self._poll_event_handler_ids = {}
                # Increment id for each new handler.
                self._event_handler_id = 0
+               # New call_soon callbacks must have an opportunity to
+               # execute before it's safe to wait on self._thread_condition
+               # without a timeout, since delaying its execution indefinitely
+               # could lead to a deadlock. The following attribute stores the
+               # event handler id of the most recently added call_soon 
callback.
+               # If this attribute has changed since the last time that the
+               # call_soon callbacks have been called, then it's not safe to
+               # wait on self._thread_condition without a timeout.
+               self._call_soon_id = 0
                # Use OrderedDict in order to emulate the FIFO queue behavior
                # of the AbstractEventLoop.call_soon method.
                self._idle_callbacks = OrderedDict()
@@ -250,10 +259,15 @@ class EventLoop(object):
 
                if not event_handlers:
                        with self._thread_condition:
+                               prev_call_soon_id = self._call_soon_id
                                if self._run_timeouts():
                                        events_handled += 1
                                timeouts_checked = True
-                               if not event_handlers and not events_handled 
and may_block:
+
+                               call_soon = prev_call_soon_id != 
self._call_soon_id
+
+                               if (not call_soon and not event_handlers
+                                       and not events_handled and may_block):
                                        # Block so that we don't waste cpu time 
by looping too
                                        # quickly. This makes EventLoop useful 
for code that needs
                                        # to wait for timeout callbacks 
regardless of whether or
@@ -457,7 +471,7 @@ class EventLoop(object):
                @return: an integer ID
                """
                with self._thread_condition:
-                       source_id = self._new_source_id()
+                       source_id = self._call_soon_id = self._new_source_id()
                        self._idle_callbacks[source_id] = 
self._idle_callback_class(
                                args=args, callback=callback, 
source_id=source_id)
                        self._thread_condition.notify()

Reply via email to