Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / internet / _glibbase.py
1 # -*- test-case-name: twisted.internet.test -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 This module provides base support for Twisted to interact with the glib/gtk
7 mainloops.
8
9 The classes in this module should not be used directly, but rather you should
10 import gireactor or gtk3reactor for GObject Introspection based applications,
11 or glib2reactor or gtk2reactor for applications using legacy static bindings.
12 """
13
14 import sys
15
16 from twisted.internet import base, posixbase, selectreactor
17 from twisted.internet.interfaces import IReactorFDSet
18 from twisted.python import log
19 from twisted.python.compat import set
20 from zope.interface import implements
21
22
23 def ensureNotImported(moduleNames, errorMessage, preventImports=[]):
24     """
25     Check whether the given modules were imported, and if requested, ensure
26     they will not be importable in the future.
27
28     @param moduleNames: A list of module names we make sure aren't imported.
29     @type moduleNames: C{list} of C{str}
30
31     @param preventImports: A list of module name whose future imports should
32         be prevented.
33     @type preventImports: C{list} of C{str}
34
35     @param errorMessage: Message to use when raising an C{ImportError}.
36     @type errorMessage: C{str}
37
38     @raises: C{ImportError} with given error message if a given module name
39         has already been imported.
40     """
41     for name in moduleNames:
42         if sys.modules.get(name) is not None:
43             raise ImportError(errorMessage)
44
45     # Disable module imports to avoid potential problems.
46     for name in preventImports:
47         sys.modules[name] = None
48
49
50
51 class GlibWaker(posixbase._UnixWaker):
52     """
53     Run scheduled events after waking up.
54     """
55
56     def doRead(self):
57         posixbase._UnixWaker.doRead(self)
58         self.reactor._simulate()
59
60
61
62 class GlibReactorBase(posixbase.PosixReactorBase, posixbase._PollLikeMixin):
63     """
64     Base class for GObject event loop reactors.
65
66     Notification for I/O events (reads and writes on file descriptors) is done
67     by the the gobject-based event loop. File descriptors are registered with
68     gobject with the appropriate flags for read/write/disconnect notification.
69
70     Time-based events, the results of C{callLater} and C{callFromThread}, are
71     handled differently. Rather than registering each event with gobject, a
72     single gobject timeout is registered for the earliest scheduled event, the
73     output of C{reactor.timeout()}. For example, if there are timeouts in 1, 2
74     and 3.4 seconds, a single timeout is registered for 1 second in the
75     future. When this timeout is hit, C{_simulate} is called, which calls the
76     appropriate Twisted-level handlers, and a new timeout is added to gobject
77     by the C{_reschedule} method.
78
79     To handle C{callFromThread} events, we use a custom waker that calls
80     C{_simulate} whenever it wakes up.
81
82     @ivar _sources: A dictionary mapping L{FileDescriptor} instances to
83         GSource handles.
84
85     @ivar _reads: A set of L{FileDescriptor} instances currently monitored for
86         reading.
87
88     @ivar _writes: A set of L{FileDescriptor} instances currently monitored for
89         writing.
90
91     @ivar _simtag: A GSource handle for the next L{simulate} call.
92     """
93     implements(IReactorFDSet)
94
95     # Install a waker that knows it needs to call C{_simulate} in order to run
96     # callbacks queued from a thread:
97     _wakerFactory = GlibWaker
98
99     def __init__(self, glib_module, gtk_module, useGtk=False):
100         self._simtag = None
101         self._reads = set()
102         self._writes = set()
103         self._sources = {}
104         self._glib = glib_module
105         self._gtk = gtk_module
106         posixbase.PosixReactorBase.__init__(self)
107
108         self._source_remove = self._glib.source_remove
109         self._timeout_add = self._glib.timeout_add
110
111         def _mainquit():
112             if self._gtk.main_level():
113                 self._gtk.main_quit()
114
115         if useGtk:
116             self._pending = self._gtk.events_pending
117             self._iteration = self._gtk.main_iteration_do
118             self._crash = _mainquit
119             self._run = self._gtk.main
120         else:
121             self.context = self._glib.main_context_default()
122             self._pending = self.context.pending
123             self._iteration = self.context.iteration
124             self.loop = self._glib.MainLoop()
125             self._crash = lambda: self._glib.idle_add(self.loop.quit)
126             self._run = self.loop.run
127
128
129     def _handleSignals(self):
130         # First, install SIGINT and friends:
131         base._SignalReactorMixin._handleSignals(self)
132         # Next, since certain versions of gtk will clobber our signal handler,
133         # set all signal handlers again after the event loop has started to
134         # ensure they're *really* set. We don't call this twice so we don't
135         # leak file descriptors created in the SIGCHLD initialization:
136         self.callLater(0, posixbase.PosixReactorBase._handleSignals, self)
137
138
139     # The input_add function in pygtk1 checks for objects with a
140     # 'fileno' method and, if present, uses the result of that method
141     # as the input source. The pygtk2 input_add does not do this. The
142     # function below replicates the pygtk1 functionality.
143
144     # In addition, pygtk maps gtk.input_add to _gobject.io_add_watch, and
145     # g_io_add_watch() takes different condition bitfields than
146     # gtk_input_add(). We use g_io_add_watch() here in case pygtk fixes this
147     # bug.
148     def input_add(self, source, condition, callback):
149         if hasattr(source, 'fileno'):
150             # handle python objects
151             def wrapper(ignored, condition):
152                 return callback(source, condition)
153             fileno = source.fileno()
154         else:
155             fileno = source
156             wrapper = callback
157         return self._glib.io_add_watch(
158             fileno, condition, wrapper,
159             priority=self._glib.PRIORITY_DEFAULT_IDLE)
160
161
162     def _ioEventCallback(self, source, condition):
163         """
164         Called by event loop when an I/O event occurs.
165         """
166         log.callWithLogger(
167             source, self._doReadOrWrite, source, source, condition)
168         return True  # True = don't auto-remove the source
169
170
171     def _add(self, source, primary, other, primaryFlag, otherFlag):
172         """
173         Add the given L{FileDescriptor} for monitoring either for reading or
174         writing. If the file is already monitored for the other operation, we
175         delete the previous registration and re-register it for both reading
176         and writing.
177         """
178         if source in primary:
179             return
180         flags = primaryFlag
181         if source in other:
182             self._source_remove(self._sources[source])
183             flags |= otherFlag
184         self._sources[source] = self.input_add(
185             source, flags, self._ioEventCallback)
186         primary.add(source)
187
188
189     def addReader(self, reader):
190         """
191         Add a L{FileDescriptor} for monitoring of data available to read.
192         """
193         self._add(reader, self._reads, self._writes,
194                   self.INFLAGS, self.OUTFLAGS)
195
196
197     def addWriter(self, writer):
198         """
199         Add a L{FileDescriptor} for monitoring ability to write data.
200         """
201         self._add(writer, self._writes, self._reads,
202                   self.OUTFLAGS, self.INFLAGS)
203
204
205     def getReaders(self):
206         """
207         Retrieve the list of current L{FileDescriptor} monitored for reading.
208         """
209         return list(self._reads)
210
211
212     def getWriters(self):
213         """
214         Retrieve the list of current L{FileDescriptor} monitored for writing.
215         """
216         return list(self._writes)
217
218
219     def removeAll(self):
220         """
221         Remove monitoring for all registered L{FileDescriptor}s.
222         """
223         return self._removeAll(self._reads, self._writes)
224
225
226     def _remove(self, source, primary, other, flags):
227         """
228         Remove monitoring the given L{FileDescriptor} for either reading or
229         writing. If it's still monitored for the other operation, we
230         re-register the L{FileDescriptor} for only that operation.
231         """
232         if source not in primary:
233             return
234         self._source_remove(self._sources[source])
235         primary.remove(source)
236         if source in other:
237             self._sources[source] = self.input_add(
238                 source, flags, self._ioEventCallback)
239         else:
240             self._sources.pop(source)
241
242
243     def removeReader(self, reader):
244         """
245         Stop monitoring the given L{FileDescriptor} for reading.
246         """
247         self._remove(reader, self._reads, self._writes, self.OUTFLAGS)
248
249
250     def removeWriter(self, writer):
251         """
252         Stop monitoring the given L{FileDescriptor} for writing.
253         """
254         self._remove(writer, self._writes, self._reads, self.INFLAGS)
255
256
257     def iterate(self, delay=0):
258         """
259         One iteration of the event loop, for trial's use.
260
261         This is not used for actual reactor runs.
262         """
263         self.runUntilCurrent()
264         while self._pending():
265             self._iteration(0)
266
267
268     def crash(self):
269         """
270         Crash the reactor.
271         """
272         posixbase.PosixReactorBase.crash(self)
273         self._crash()
274
275
276     def stop(self):
277         """
278         Stop the reactor.
279         """
280         posixbase.PosixReactorBase.stop(self)
281         # The base implementation only sets a flag, to ensure shutting down is
282         # not reentrant. Unfortunately, this flag is not meaningful to the
283         # gobject event loop. We therefore call wakeUp() to ensure the event
284         # loop will call back into Twisted once this iteration is done. This
285         # will result in self.runUntilCurrent() being called, where the stop
286         # flag will trigger the actual shutdown process, eventually calling
287         # crash() which will do the actual gobject event loop shutdown.
288         self.wakeUp()
289
290
291     def run(self, installSignalHandlers=True):
292         """
293         Run the reactor.
294         """
295         self.callWhenRunning(self._reschedule)
296         self.startRunning(installSignalHandlers=installSignalHandlers)
297         if self._started:
298             self._run()
299
300
301     def callLater(self, *args, **kwargs):
302         """
303         Schedule a C{DelayedCall}.
304         """
305         result = posixbase.PosixReactorBase.callLater(self, *args, **kwargs)
306         # Make sure we'll get woken up at correct time to handle this new
307         # scheduled call:
308         self._reschedule()
309         return result
310
311
312     def _reschedule(self):
313         """
314         Schedule a glib timeout for C{_simulate}.
315         """
316         if self._simtag is not None:
317             self._source_remove(self._simtag)
318             self._simtag = None
319         timeout = self.timeout()
320         if timeout is not None:
321             self._simtag = self._timeout_add(
322                 int(timeout * 1000), self._simulate,
323                 priority=self._glib.PRIORITY_DEFAULT_IDLE)
324
325
326     def _simulate(self):
327         """
328         Run timers, and then reschedule glib timeout for next scheduled event.
329         """
330         self.runUntilCurrent()
331         self._reschedule()
332
333
334
335 class PortableGlibReactorBase(selectreactor.SelectReactor):
336     """
337     Base class for GObject event loop reactors that works on Windows.
338
339     Sockets aren't supported by GObject's input_add on Win32.
340     """
341     def __init__(self, glib_module, gtk_module, useGtk=False):
342         self._simtag = None
343         self._glib = glib_module
344         self._gtk = gtk_module
345         selectreactor.SelectReactor.__init__(self)
346
347         self._source_remove = self._glib.source_remove
348         self._timeout_add = self._glib.timeout_add
349
350         def _mainquit():
351             if self._gtk.main_level():
352                 self._gtk.main_quit()
353
354         if useGtk:
355             self._crash = _mainquit
356             self._run = self._gtk.main
357         else:
358             self.loop = self._glib.MainLoop()
359             self._crash = lambda: self._glib.idle_add(self.loop.quit)
360             self._run = self.loop.run
361
362
363     def crash(self):
364         selectreactor.SelectReactor.crash(self)
365         self._crash()
366
367
368     def run(self, installSignalHandlers=True):
369         self.startRunning(installSignalHandlers=installSignalHandlers)
370         self._timeout_add(0, self.simulate)
371         if self._started:
372             self._run()
373
374
375     def simulate(self):
376         """
377         Run simulation loops and reschedule callbacks.
378         """
379         if self._simtag is not None:
380             self._source_remove(self._simtag)
381         self.iterate()
382         timeout = min(self.timeout(), 0.01)
383         if timeout is None:
384             timeout = 0.01
385         self._simtag = self._timeout_add(
386             int(timeout * 1000), self.simulate,
387             priority=self._glib.PRIORITY_DEFAULT_IDLE)