4.5.0r6
[enigma2.git] / usr / lib / enigma2 / python / e2reactor.py
1 # based on qt4reactor.py
2 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4 # Original Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
5 # Original Ported to QT4: U{Gabe Rudy<mailto:rudy@goldenhelix.com>}
6
7 """
8 This module provides support for Twisted to interact with Enigma2's mainloop.
9
10 Maintainer: U{Andreas Monzner<mailto:andreas.monzner@dream-property.net>}
11 """
12
13 __all__ = ['install']
14
15 # System Imports
16 from enigma import runMainloop, quitMainloop, eSocketNotifier, eTimer
17 from select import POLLIN, POLLOUT
18
19 from zope.interface import implementer
20
21 from twisted.internet.interfaces import IReactorFDSet
22 from twisted.python import log
23 from twisted.internet.posixbase import PosixReactorBase
24
25 from sys import exc_info
26 from monotonic import monotonic
27
28 monotonic_time = monotonic
29
30 class TwistedSocketNotifier:
31         """
32         Connection between an fd event and reader/writer callbacks.
33         """
34
35         def __init__(self, reactor, watcher, type):
36                 self.sn = eSocketNotifier(watcher.fileno(), type)
37                 self.reactor = reactor
38                 self.watcher = watcher
39                 self.fn = None
40                 if type == POLLIN:
41                         self.fn = self.read
42                 elif type == POLLOUT:
43                         self.fn = self.write
44                 self.sn_conn = self.sn.activated.connect(self.fn)
45
46         def shutdown(self):
47                 self.sn_conn = self.fn = self.watcher = None
48                 del self.sn
49
50         def read(self, sock):
51                 w = self.watcher
52                 def _read():
53                         why = None
54                         try:
55                                 why = w.doRead()
56                         except:
57                                 log.err()
58                                 why = exc_info()[1]
59                         if why:
60                                 self.reactor._disconnectSelectable(w, why, True)
61                 log.callWithLogger(w, _read)
62                 self.reactor.simulate()
63
64         def write(self, sock):
65                 w = self.watcher
66                 def _write():
67                         why = None
68                         try:
69                                 why = w.doWrite()
70                         except:
71                                 log.err()
72                                 why = exc_info()[1]
73                         if why:
74                                 self.reactor._disconnectSelectable(w, why, False)
75                 log.callWithLogger(w, _write)
76                 self.reactor.simulate()
77
78 @implementer(IReactorFDSet)
79 class e2reactor(PosixReactorBase):
80         """
81         e2 reactor.
82         """
83
84         # Reference to a DelayedCall for self.crash() when the reactor is
85         # entered through .iterate()
86         _crashCall = None
87
88         _timer = None
89
90         _now = None
91
92         def __init__(self):
93                 self._reads = {}
94                 self._writes = {}
95                 self.savedTimeout = None
96                 self._shutdownRunning = 0
97                 self._timer = eTimer()
98                 self._timer_conn = self._timer.timeout.connect(self.simulate)
99                 self._insimulate = False
100                 self._wakeupPending = False
101                 # to limit the systemcalls per loop
102                 # twistes gets a cached monotonic time for internal timers
103                 # only updated once per loop (monotonic_time call in def simulate)
104                 PosixReactorBase.seconds = self.now
105                 PosixReactorBase.__init__(self)
106                 self.addSystemEventTrigger('after', 'shutdown', self.cleanup)
107
108         def doShutdown(self):
109                 while self._timer: # iterate until cleanup is called...
110                         self.runUntilCurrent()
111                         self._insertNewDelayedCalls()
112                         self._shutdownRunning += 1 # without this hack the shutdown takes thirty seconds.. i dont know why
113
114         def now(self):
115                 return self._insimulate and self._now or monotonic_time()+self._shutdownRunning
116
117         def callLater(self, _seconds, _f, *args, **kw):
118                 ret = PosixReactorBase.callLater(self, _seconds, _f, *args, **kw)
119                 if not self._wakeupPending:
120                         self.wakeUp()
121                         self._wakeupPending = True
122                 return ret
123
124         def addReader(self, reader):
125                 if not reader in self._reads:
126                         self._reads[reader] = TwistedSocketNotifier(self, reader, POLLIN)
127
128         def addWriter(self, writer):
129                 if not writer in self._writes:
130                         self._writes[writer] = TwistedSocketNotifier(self, writer, POLLOUT)
131
132         def removeReader(self, reader):
133                 if reader in self._reads:
134                         self._reads.pop(reader).shutdown()
135
136         def removeWriter(self, writer):
137                 if writer in self._writes:
138                         self._writes.pop(writer).shutdown()
139
140         def removeAll(self):
141                 return self._removeAll(self._reads, self._writes)
142
143         def getReaders(self):
144                 return list(self._reads.keys())
145
146         def getWriters(self):
147                 return list(self._writes.keys())
148
149         def simulate(self):
150                 if not self.running:
151                         quitMainloop(6)
152                         return
153
154                 #update time returned by self.seconds
155                 e2reactor._now = monotonic_time()
156
157                 self._wakeupPending = False
158                 self._insimulate = True
159
160                 self.runUntilCurrent()
161
162                 if self._crashCall is not None:
163                         self._crashCall.reset(0)
164
165                 self._insertNewDelayedCalls()
166
167                 pendingTimedCalls = self._pendingTimedCalls
168                 if pendingTimedCalls:
169                         nextTimeout = pendingTimedCalls[0].time
170                         if nextTimeout != self.savedTimeout:
171                                 self.savedTimeout = nextTimeout
172                                 timeout = max(0, nextTimeout - self.seconds())
173                                 self._timer.start(int(timeout * 1010), True)
174                 else:
175                         self._timer.stop()
176
177                 self._insimulate = False
178
179         def cleanup(self):
180                 if self._timer is not None:
181                         self._timer_conn = None
182                         self._timer.stop()
183                         self._timer = None
184
185         def iterate(self, delay=0.0):
186                 self._crashCall = self.callLater(delay, self._crash)
187                 self.run()
188
189         def mainLoop(self):
190                 self.simulate()
191                 runMainloop()
192
193         def _crash(self):
194                 if self._crashCall is not None:
195                         if self._crashCall.active():
196                                 self._crashCall.cancel()
197                         self._crashCall = None
198                 self.running = False
199
200 def install(app=None):
201         """
202         Configure the twisted mainloop to be run inside the e2 mainloop.
203         """
204         from twisted.internet import main
205         reactor = e2reactor()
206         main.installReactor(reactor)