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