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/_emerge/
Date: Tue, 07 Feb 2012 23:04:32
Message-Id: e9d1125f6730c85c4b384a580da55da68338acf1.zmedico@gentoo
1 commit: e9d1125f6730c85c4b384a580da55da68338acf1
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Tue Feb 7 19:11:50 2012 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Tue Feb 7 19:11:50 2012 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=e9d1125f
7
8 PollScheduler: add timeout_add like glib's
9
10 This will be useful as a substitute for recursion, in order to avoid
11 hitting the recursion limit for bug #402335.
12
13 ---
14 pym/_emerge/PollScheduler.py | 89 ++++++++++++++++++++++++++++++++++++++++--
15 pym/_emerge/Scheduler.py | 8 ++--
16 2 files changed, 89 insertions(+), 8 deletions(-)
17
18 diff --git a/pym/_emerge/PollScheduler.py b/pym/_emerge/PollScheduler.py
19 index fd9dfc0..fd57359 100644
20 --- a/pym/_emerge/PollScheduler.py
21 +++ b/pym/_emerge/PollScheduler.py
22 @@ -24,7 +24,12 @@ from _emerge.PollSelectAdapter import PollSelectAdapter
23 class PollScheduler(object):
24
25 class _sched_iface_class(SlotObject):
26 - __slots__ = ("output", "register", "schedule", "unregister")
27 + __slots__ = ("output", "register", "schedule",
28 + "source_remove", "timeout_add", "unregister")
29 +
30 + class _timeout_handler_class(SlotObject):
31 + __slots__ = ("args", "function", "interval", "source_id",
32 + "timestamp")
33
34 def __init__(self):
35 self._terminated = threading.Event()
36 @@ -37,13 +42,17 @@ class PollScheduler(object):
37 self._poll_event_handler_ids = {}
38 # Increment id for each new handler.
39 self._event_handler_id = 0
40 + self._timeout_handlers = {}
41 self._poll_obj = create_poll_instance()
42 + self._polling = False
43 self._scheduling = False
44 self._background = False
45 self.sched_iface = self._sched_iface_class(
46 output=self._task_output,
47 register=self._register,
48 schedule=self._schedule_wait,
49 + source_remove=self._unregister,
50 + timeout_add=self._timeout_add,
51 unregister=self._unregister)
52
53 def terminate(self):
54 @@ -82,7 +91,7 @@ class PollScheduler(object):
55 should return False immediately (since there's no need to
56 schedule anything after _terminate_tasks() has been called).
57 """
58 - raise NotImplementedError()
59 + pass
60
61 def _schedule(self):
62 """
63 @@ -140,6 +149,23 @@ class PollScheduler(object):
64 StopIteration if timeout is None and there are
65 no file descriptors to poll.
66 """
67 + if self._polling:
68 + return
69 + self._polling = True
70 + try:
71 + self._do_poll(timeout=timeout)
72 + finally:
73 + self._polling = False
74 +
75 + def _do_poll(self, timeout=None):
76 + """
77 + All poll() calls pass through here. The poll events
78 + are added directly to self._poll_event_queue.
79 + In order to avoid endless blocking, this raises
80 + StopIteration if timeout is None and there are
81 + no file descriptors to poll.
82 + """
83 + self._run_timeouts()
84 if not self._poll_event_handlers:
85 self._schedule()
86 if timeout is None and \
87 @@ -226,6 +252,51 @@ class PollScheduler(object):
88
89 return bool(events_handled)
90
91 + def _timeout_add(self, interval, function, *args):
92 + """
93 + Like glib.timeout_add(), interval argument is the number of
94 + milliseconds between calls to your function, and your function
95 + should return False to stop being called, or True to continue
96 + being called. Any additional positional arguments given here
97 + are passed to your function when it's called.
98 +
99 + NOTE: Timeouts registered by this function currently do not
100 + keep the main loop running when there are no remaining callbacks
101 + registered for IO events. This is not an issue if the purpose of
102 + the timeout is to place an upper limit on the time allowed for
103 + a particular IO event to occur, since the handler associated with
104 + the IO event will serve to keep the main loop running.
105 + """
106 + self._event_handler_id += 1
107 + source_id = self._event_handler_id
108 + self._timeout_handlers[source_id] = \
109 + self._timeout_handler_class(
110 + interval=interval, function=function, args=args,
111 + source_id=source_id, timestamp=time.time())
112 + return source_id
113 +
114 + def _run_timeouts(self):
115 + ready_timeouts = []
116 + current_time = time.time()
117 + for x in self._timeout_handlers.values():
118 + elapsed_seconds = current_time - x.timestamp
119 + # elapsed_seconds < 0 means the system clock has been adjusted
120 + if elapsed_seconds < 0 or \
121 + (x.interval - 1000 * elapsed_seconds) <= 0:
122 + ready_timeouts.append(x)
123 +
124 + # Iterate of our local list, since self._timeout_handlers can be
125 + # modified during the exection of these callbacks.
126 + for x in ready_timeouts:
127 + if x.source_id not in self._timeout_handlers:
128 + # it got cancelled while executing another timeout
129 + continue
130 + x.timestamp = time.time()
131 + if not x.function(*x.args):
132 + self._unregister(x.source_id)
133 +
134 + return bool(ready_timeouts)
135 +
136 def _register(self, f, eventmask, handler):
137 """
138 @rtype: Integer
139 @@ -242,7 +313,17 @@ class PollScheduler(object):
140 return reg_id
141
142 def _unregister(self, reg_id):
143 - f = self._poll_event_handler_ids[reg_id]
144 + """
145 + Like glib.source_remove(), this returns True if the given reg_id
146 + is found and removed, and False if the reg_id is invalid or has
147 + already been removed.
148 + """
149 + timeout_handler = self._timeout_handlers.pop(reg_id, None)
150 + if timeout_handler is not None:
151 + return True
152 + f = self._poll_event_handler_ids.pop(reg_id, None)
153 + if f is None:
154 + return False
155 self._poll_obj.unregister(f)
156 if self._poll_event_queue:
157 # Discard any unhandled events that belong to this file,
158 @@ -262,7 +343,7 @@ class PollScheduler(object):
159 self._poll_event_queue[:] = remaining_events
160
161 del self._poll_event_handlers[f]
162 - del self._poll_event_handler_ids[reg_id]
163 + return True
164
165 def _schedule_wait(self, wait_ids=None, timeout=None, condition=None):
166 """
167
168 diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py
169 index d09b474..5b56650 100644
170 --- a/pym/_emerge/Scheduler.py
171 +++ b/pym/_emerge/Scheduler.py
172 @@ -79,11 +79,9 @@ class Scheduler(PollScheduler):
173 _opts_no_self_update = frozenset(["--buildpkgonly",
174 "--fetchonly", "--fetch-all-uri", "--pretend"])
175
176 - class _iface_class(SlotObject):
177 + class _iface_class(PollScheduler._sched_iface_class):
178 __slots__ = ("fetch",
179 - "output", "register", "schedule",
180 - "scheduleSetup", "scheduleUnpack", "scheduleYield",
181 - "unregister")
182 + "scheduleSetup", "scheduleUnpack", "scheduleYield")
183
184 class _fetch_iface_class(SlotObject):
185 __slots__ = ("log_file", "schedule")
186 @@ -223,6 +221,8 @@ class Scheduler(PollScheduler):
187 scheduleSetup=self._schedule_setup,
188 scheduleUnpack=self._schedule_unpack,
189 scheduleYield=self._schedule_yield,
190 + source_remove=self._unregister,
191 + timeout_add=self._timeout_add,
192 unregister=self._unregister)
193
194 self._prefetchers = weakref.WeakValueDictionary()