1 |
commit: fa983106fac83ca29f242cca37ce8ddf565f92d6 |
2 |
Author: Zac Medico <zmedico <AT> gentoo <DOT> org> |
3 |
AuthorDate: Tue Feb 7 19:40:55 2012 +0000 |
4 |
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Feb 7 19:40:55 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=fa983106 |
7 |
|
8 |
PollScheduler: timeouts regardless of IO events |
9 |
|
10 |
Now PollScheduler will execute timeouts predictably, even when there |
11 |
no IO events being generated. This allows the Scheduler's display |
12 |
updates to be handled via timeout_add. |
13 |
|
14 |
--- |
15 |
pym/_emerge/JobStatusDisplay.py | 10 ++++-- |
16 |
pym/_emerge/PollScheduler.py | 59 +++++++++++++++++++++++++++++++++------ |
17 |
pym/_emerge/Scheduler.py | 46 ++---------------------------- |
18 |
3 files changed, 59 insertions(+), 56 deletions(-) |
19 |
|
20 |
diff --git a/pym/_emerge/JobStatusDisplay.py b/pym/_emerge/JobStatusDisplay.py |
21 |
index 877a0c9..d84d1b0 100644 |
22 |
--- a/pym/_emerge/JobStatusDisplay.py |
23 |
+++ b/pym/_emerge/JobStatusDisplay.py |
24 |
@@ -209,24 +209,26 @@ class JobStatusDisplay(object): |
25 |
def display(self): |
26 |
""" |
27 |
Display status on stdout, but only if something has |
28 |
- changed since the last call. |
29 |
+ changed since the last call. This always returns True, |
30 |
+ for continuous scheduling via timeout_add. |
31 |
""" |
32 |
|
33 |
if self.quiet: |
34 |
- return |
35 |
+ return True |
36 |
|
37 |
current_time = time.time() |
38 |
time_delta = current_time - self._last_display_time |
39 |
if self._displayed and \ |
40 |
not self._changed: |
41 |
if not self._isatty: |
42 |
- return |
43 |
+ return True |
44 |
if time_delta < self._min_display_latency: |
45 |
- return |
46 |
+ return True |
47 |
|
48 |
self._last_display_time = current_time |
49 |
self._changed = False |
50 |
self._display_status() |
51 |
+ return True |
52 |
|
53 |
def _display_status(self): |
54 |
# Don't use len(self._completed_tasks) here since that also |
55 |
|
56 |
diff --git a/pym/_emerge/PollScheduler.py b/pym/_emerge/PollScheduler.py |
57 |
index fd57359..757d12e 100644 |
58 |
--- a/pym/_emerge/PollScheduler.py |
59 |
+++ b/pym/_emerge/PollScheduler.py |
60 |
@@ -43,6 +43,7 @@ class PollScheduler(object): |
61 |
# Increment id for each new handler. |
62 |
self._event_handler_id = 0 |
63 |
self._timeout_handlers = {} |
64 |
+ self._timeout_interval = None |
65 |
self._poll_obj = create_poll_instance() |
66 |
self._polling = False |
67 |
self._scheduling = False |
68 |
@@ -142,18 +143,51 @@ class PollScheduler(object): |
69 |
return True |
70 |
|
71 |
def _poll(self, timeout=None): |
72 |
- """ |
73 |
- All poll() calls pass through here. The poll events |
74 |
- are added directly to self._poll_event_queue. |
75 |
- In order to avoid endless blocking, this raises |
76 |
- StopIteration if timeout is None and there are |
77 |
- no file descriptors to poll. |
78 |
- """ |
79 |
if self._polling: |
80 |
return |
81 |
self._polling = True |
82 |
try: |
83 |
- self._do_poll(timeout=timeout) |
84 |
+ if self._timeout_interval is None: |
85 |
+ self._run_timeouts() |
86 |
+ self._do_poll(timeout=timeout) |
87 |
+ |
88 |
+ elif timeout is None: |
89 |
+ while True: |
90 |
+ self._run_timeouts() |
91 |
+ previous_count = len(self._poll_event_queue) |
92 |
+ self._do_poll(timeout=self._timeout_interval) |
93 |
+ if previous_count != len(self._poll_event_queue): |
94 |
+ break |
95 |
+ |
96 |
+ elif timeout <= self._timeout_interval: |
97 |
+ self._run_timeouts() |
98 |
+ self._do_poll(timeout=timeout) |
99 |
+ |
100 |
+ else: |
101 |
+ remaining_timeout = timeout |
102 |
+ start_time = time.time() |
103 |
+ while True: |
104 |
+ self._run_timeouts() |
105 |
+ # _timeout_interval can change each time |
106 |
+ # _run_timeouts is called |
107 |
+ min_timeout = remaining_timeout |
108 |
+ if self._timeout_interval is not None and \ |
109 |
+ self._timeout_interval < min_timeout: |
110 |
+ min_timeout = self._timeout_interval |
111 |
+ |
112 |
+ previous_count = len(self._poll_event_queue) |
113 |
+ self._do_poll(timeout=min_timeout) |
114 |
+ if previous_count != len(self._poll_event_queue): |
115 |
+ break |
116 |
+ elapsed_time = time.time() - start_time |
117 |
+ if elapsed_time < 0: |
118 |
+ # The system clock has changed such that start_time |
119 |
+ # is now in the future, so just assume that the |
120 |
+ # timeout has already elapsed. |
121 |
+ break |
122 |
+ remaining_timeout = timeout - 1000 * elapsed_time |
123 |
+ if remaining_timeout <= 0: |
124 |
+ break |
125 |
finally: |
126 |
self._polling = False |
127 |
|
128 |
@@ -165,7 +199,6 @@ class PollScheduler(object): |
129 |
StopIteration if timeout is None and there are |
130 |
no file descriptors to poll. |
131 |
""" |
132 |
- self._run_timeouts() |
133 |
if not self._poll_event_handlers: |
134 |
self._schedule() |
135 |
if timeout is None and \ |
136 |
@@ -273,6 +306,8 @@ class PollScheduler(object): |
137 |
self._timeout_handler_class( |
138 |
interval=interval, function=function, args=args, |
139 |
source_id=source_id, timestamp=time.time()) |
140 |
+ if self._timeout_interval is None or self._timeout_interval < interval: |
141 |
+ self._timeout_interval = interval |
142 |
return source_id |
143 |
|
144 |
def _run_timeouts(self): |
145 |
@@ -320,6 +355,12 @@ class PollScheduler(object): |
146 |
""" |
147 |
timeout_handler = self._timeout_handlers.pop(reg_id, None) |
148 |
if timeout_handler is not None: |
149 |
+ if timeout_handler.interval >= self._timeout_interval: |
150 |
+ if self._timeout_handlers: |
151 |
+ self._timeout_interval = \ |
152 |
+ min(x.interval for x in self._timeout_handlers.values()) |
153 |
+ else: |
154 |
+ self._timeout_interval = None |
155 |
return True |
156 |
f = self._poll_event_handler_ids.pop(reg_id, None) |
157 |
if f is None: |
158 |
|
159 |
diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py |
160 |
index 5b56650..e6f3e0e 100644 |
161 |
--- a/pym/_emerge/Scheduler.py |
162 |
+++ b/pym/_emerge/Scheduler.py |
163 |
@@ -196,6 +196,8 @@ class Scheduler(PollScheduler): |
164 |
|
165 |
self._status_display = JobStatusDisplay( |
166 |
xterm_titles=('notitles' not in settings.features)) |
167 |
+ self._timeout_add(self._max_display_latency, |
168 |
+ self._status_display.display) |
169 |
self._max_load = myopts.get("--load-average") |
170 |
max_jobs = myopts.get("--jobs") |
171 |
if max_jobs is None: |
172 |
@@ -352,50 +354,8 @@ class Scheduler(PollScheduler): |
173 |
gc.collect() |
174 |
|
175 |
def _poll(self, timeout=None): |
176 |
- |
177 |
self._schedule() |
178 |
- |
179 |
- if timeout is None: |
180 |
- while True: |
181 |
- if not self._poll_event_handlers: |
182 |
- self._schedule() |
183 |
- if not self._poll_event_handlers: |
184 |
- raise StopIteration( |
185 |
- "timeout is None and there are no poll() event handlers") |
186 |
- previous_count = len(self._poll_event_queue) |
187 |
- PollScheduler._poll(self, timeout=self._max_display_latency) |
188 |
- self._status_display.display() |
189 |
- if previous_count != len(self._poll_event_queue): |
190 |
- break |
191 |
- |
192 |
- elif timeout <= self._max_display_latency: |
193 |
- PollScheduler._poll(self, timeout=timeout) |
194 |
- if timeout == 0: |
195 |
- # The display is updated by _schedule() above, so it would be |
196 |
- # redundant to update it here when timeout is 0. |
197 |
- pass |
198 |
- else: |
199 |
- self._status_display.display() |
200 |
- |
201 |
- else: |
202 |
- remaining_timeout = timeout |
203 |
- start_time = time.time() |
204 |
- while True: |
205 |
- previous_count = len(self._poll_event_queue) |
206 |
- PollScheduler._poll(self, |
207 |
- timeout=min(self._max_display_latency, remaining_timeout)) |
208 |
- self._status_display.display() |
209 |
- if previous_count != len(self._poll_event_queue): |
210 |
- break |
211 |
- elapsed_time = time.time() - start_time |
212 |
- if elapsed_time < 0: |
213 |
- # The system clock has changed such that start_time |
214 |
- # is now in the future, so just assume that the |
215 |
- # timeout has already elapsed. |
216 |
- break |
217 |
- remaining_timeout = timeout - 1000 * elapsed_time |
218 |
- if remaining_timeout <= 0: |
219 |
- break |
220 |
+ PollScheduler._poll(self, timeout=timeout) |
221 |
|
222 |
def _set_max_jobs(self, max_jobs): |
223 |
self._max_jobs = max_jobs |