Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: pym/portage/util/_eventloop/, pym/_emerge/
Date: Fri, 11 May 2012 06:55:00
Message-Id: 1336719269.5228eb4ce60765cd421a5e363e51a71626739267.zmedico@gentoo
1 commit: 5228eb4ce60765cd421a5e363e51a71626739267
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Fri May 11 06:32:26 2012 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Fri May 11 06:54:29 2012 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=5228eb4c
7
8 PollScheduler: use local EventLoop (thread safe)
9
10 For API consumers, this makes the doebuild() function compatible with
11 threads, avoiding a ValueError raised by the signal module, as reported
12 at http://bugs.sabayon.org/show_bug.cgi?id=3305. Classes derived from
13 PollScheduler still use the signal module when possible.
14
15 ---
16 pym/_emerge/MetadataRegen.py | 2 +-
17 pym/_emerge/PollScheduler.py | 14 +++++++-
18 pym/_emerge/QueueScheduler.py | 4 +-
19 pym/_emerge/Scheduler.py | 2 +-
20 pym/_emerge/TaskScheduler.py | 6 ++--
21 pym/portage/util/_eventloop/EventLoop.py | 50 +++++++++++++++++++-----------
22 6 files changed, 51 insertions(+), 27 deletions(-)
23
24 diff --git a/pym/_emerge/MetadataRegen.py b/pym/_emerge/MetadataRegen.py
25 index 79446ee..e82015f 100644
26 --- a/pym/_emerge/MetadataRegen.py
27 +++ b/pym/_emerge/MetadataRegen.py
28 @@ -11,7 +11,7 @@ class MetadataRegen(PollScheduler):
29
30 def __init__(self, portdb, cp_iter=None, consumer=None,
31 max_jobs=None, max_load=None):
32 - PollScheduler.__init__(self)
33 + PollScheduler.__init__(self, main=True)
34 self._portdb = portdb
35 self._global_cleanse = False
36 if cp_iter is None:
37
38 diff --git a/pym/_emerge/PollScheduler.py b/pym/_emerge/PollScheduler.py
39 index 1c631c3..965dc20 100644
40 --- a/pym/_emerge/PollScheduler.py
41 +++ b/pym/_emerge/PollScheduler.py
42 @@ -13,6 +13,7 @@ from portage import _encodings
43 from portage import _unicode_encode
44 from portage.util import writemsg_level
45 from portage.util.SlotObject import SlotObject
46 +from portage.util._eventloop.EventLoop import EventLoop
47 from portage.util._eventloop.global_event_loop import global_event_loop
48
49 from _emerge.getloadavg import getloadavg
50 @@ -26,7 +27,13 @@ class PollScheduler(object):
51 "output", "register", "run",
52 "source_remove", "timeout_add", "unregister")
53
54 - def __init__(self):
55 + def __init__(self, main=False):
56 + """
57 + @param main: If True then use global_event_loop(), otherwise use
58 + a local EventLoop instance (default is False, for safe use in
59 + a non-main thread)
60 + @type main: bool
61 + """
62 self._terminated = threading.Event()
63 self._terminated_tasks = False
64 self._max_jobs = 1
65 @@ -34,7 +41,10 @@ class PollScheduler(object):
66 self._jobs = 0
67 self._scheduling = False
68 self._background = False
69 - self._event_loop = global_event_loop()
70 + if main:
71 + self._event_loop = global_event_loop()
72 + else:
73 + self._event_loop = EventLoop(main=False)
74 self.sched_iface = self._sched_iface_class(
75 IO_ERR=self._event_loop.IO_ERR,
76 IO_HUP=self._event_loop.IO_HUP,
77
78 diff --git a/pym/_emerge/QueueScheduler.py b/pym/_emerge/QueueScheduler.py
79 index 9d73b78..206087c 100644
80 --- a/pym/_emerge/QueueScheduler.py
81 +++ b/pym/_emerge/QueueScheduler.py
82 @@ -10,8 +10,8 @@ class QueueScheduler(PollScheduler):
83 run() method returns when no tasks remain.
84 """
85
86 - def __init__(self, max_jobs=None, max_load=None):
87 - PollScheduler.__init__(self)
88 + def __init__(self, main=True, max_jobs=None, max_load=None):
89 + PollScheduler.__init__(self, main=main)
90
91 if max_jobs is None:
92 max_jobs = 1
93
94 diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py
95 index 5500acf..30a7e10 100644
96 --- a/pym/_emerge/Scheduler.py
97 +++ b/pym/_emerge/Scheduler.py
98 @@ -137,7 +137,7 @@ class Scheduler(PollScheduler):
99 def __init__(self, settings, trees, mtimedb, myopts,
100 spinner, mergelist=None, favorites=None, graph_config=None,
101 uninstall_only=False):
102 - PollScheduler.__init__(self)
103 + PollScheduler.__init__(self, main=True)
104
105 if mergelist is not None:
106 warnings.warn("The mergelist parameter of the " + \
107
108 diff --git a/pym/_emerge/TaskScheduler.py b/pym/_emerge/TaskScheduler.py
109 index 71ac80f..583bfe3 100644
110 --- a/pym/_emerge/TaskScheduler.py
111 +++ b/pym/_emerge/TaskScheduler.py
112 @@ -1,4 +1,4 @@
113 -# Copyright 1999-2009 Gentoo Foundation
114 +# Copyright 1999-2012 Gentoo Foundation
115 # Distributed under the terms of the GNU General Public License v2
116
117 from _emerge.QueueScheduler import QueueScheduler
118 @@ -11,9 +11,9 @@ class TaskScheduler(object):
119 add tasks and call run(). The run() method returns when no tasks remain.
120 """
121
122 - def __init__(self, max_jobs=None, max_load=None):
123 + def __init__(self, main=True, max_jobs=None, max_load=None):
124 self._queue = SequentialTaskQueue(max_jobs=max_jobs)
125 - self._scheduler = QueueScheduler(
126 + self._scheduler = QueueScheduler(main=main,
127 max_jobs=max_jobs, max_load=max_load)
128 self.sched_iface = self._scheduler.sched_iface
129 self.run = self._scheduler.run
130
131 diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py
132 index ef20ce4..bbbce52 100644
133 --- a/pym/portage/util/_eventloop/EventLoop.py
134 +++ b/pym/portage/util/_eventloop/EventLoop.py
135 @@ -35,7 +35,15 @@ class EventLoop(object):
136 __slots__ = ("args", "function", "calling", "interval", "source_id",
137 "timestamp")
138
139 - def __init__(self):
140 + def __init__(self, main=True):
141 + """
142 + @param main: If True then this is a singleton instance for use
143 + in the main thread, otherwise it is a local instance which
144 + can safely be use in a non-main thread (default is True, so
145 + that global_event_loop does not need constructor arguments)
146 + @type main: bool
147 + """
148 + self._use_signal = main
149 self._poll_event_queue = []
150 self._poll_event_handlers = {}
151 self._poll_event_handler_ids = {}
152 @@ -198,20 +206,22 @@ class EventLoop(object):
153 self._child_handlers[source_id] = self._child_callback_class(
154 callback=callback, data=data, pid=pid, source_id=source_id)
155
156 - if self._sigchld_read is None:
157 - self._sigchld_read, self._sigchld_write = os.pipe()
158 - fcntl.fcntl(self._sigchld_read, fcntl.F_SETFL,
159 - fcntl.fcntl(self._sigchld_read, fcntl.F_GETFL) | os.O_NONBLOCK)
160 -
161 - # The IO watch is dynamically registered and unregistered as
162 - # needed, since we don't want to consider it as a valid source
163 - # of events when there are no child listeners. It's important
164 - # to distinguish when there are no valid sources of IO events,
165 - # in order to avoid an endless poll call if there's no timeout.
166 - if self._sigchld_src_id is None:
167 - self._sigchld_src_id = self.io_add_watch(
168 - self._sigchld_read, self.IO_IN, self._sigchld_io_cb)
169 - signal.signal(signal.SIGCHLD, self._sigchld_sig_cb)
170 + if self._use_signal:
171 + if self._sigchld_read is None:
172 + self._sigchld_read, self._sigchld_write = os.pipe()
173 + fcntl.fcntl(self._sigchld_read, fcntl.F_SETFL,
174 + fcntl.fcntl(self._sigchld_read,
175 + fcntl.F_GETFL) | os.O_NONBLOCK)
176 +
177 + # The IO watch is dynamically registered and unregistered as
178 + # needed, since we don't want to consider it as a valid source
179 + # of events when there are no child listeners. It's important
180 + # to distinguish when there are no valid sources of IO events,
181 + # in order to avoid an endless poll call if there's no timeout.
182 + if self._sigchld_src_id is None:
183 + self._sigchld_src_id = self.io_add_watch(
184 + self._sigchld_read, self.IO_IN, self._sigchld_io_cb)
185 + signal.signal(signal.SIGCHLD, self._sigchld_sig_cb)
186
187 # poll now, in case the SIGCHLD has already arrived
188 self._poll_child_processes()
189 @@ -318,10 +328,15 @@ class EventLoop(object):
190
191 def _run_timeouts(self):
192
193 + calls = 0
194 + if not self._use_signal:
195 + if self._poll_child_processes():
196 + calls += 1
197 +
198 self._run_idle_callbacks()
199
200 if not self._timeout_handlers:
201 - return False
202 + return bool(calls)
203
204 ready_timeouts = []
205 current_time = time.time()
206 @@ -334,7 +349,6 @@ class EventLoop(object):
207
208 # Iterate of our local list, since self._timeout_handlers can be
209 # modified during the exection of these callbacks.
210 - calls = 0
211 for x in ready_timeouts:
212 if x.source_id not in self._timeout_handlers:
213 # it got cancelled while executing another timeout
214 @@ -387,7 +401,7 @@ class EventLoop(object):
215 """
216 x = self._child_handlers.pop(reg_id, None)
217 if x is not None:
218 - if not self._child_handlers:
219 + if not self._child_handlers and self._use_signal:
220 signal.signal(signal.SIGCHLD, signal.SIG_DFL)
221 self.source_remove(self._sigchld_src_id)
222 self._sigchld_src_id = None