create global (real) config entries, update setup on hotplug (untested), fix apply...
[enigma2.git] / e2reactor.py
1 # enigma2 reactor: based on pollreactor, which is
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """
7 Maintainer: U{Felix Domke<mailto:tmbinc@elitedvb.net>}
8 """
9
10 # System imports
11 import select, errno, sys
12
13 # Twisted imports
14 from twisted.python import log, failure
15 from twisted.internet import main, posixbase, error
16 #from twisted.internet.pollreactor import PollReactor, poller
17
18 from enigma import getApplication
19
20 # globals
21 reads = {}
22 writes = {}
23 selectables = {}
24
25 POLL_DISCONNECTED = (select.POLLHUP | select.POLLERR | select.POLLNVAL)
26
27 class E2SharedPoll:
28         def __init__(self):
29                 self.dict = { }
30                 self.eApp = getApplication()
31
32         def register(self, fd, eventmask = select.POLLIN | select.POLLERR | select.POLLOUT):
33                 self.dict[fd] = eventmask
34         
35         def unregister(self, fd):
36                 del self.dict[fd]
37         
38         def poll(self, timeout = None):
39                 r = self.eApp.poll(timeout, self.dict)
40                 return r
41
42 poller = E2SharedPoll()
43
44 class PollReactor(posixbase.PosixReactorBase):
45         """A reactor that uses poll(2)."""
46
47         def _updateRegistration(self, fd):
48                 """Register/unregister an fd with the poller."""
49                 try:
50                         poller.unregister(fd)
51                 except KeyError:
52                         pass
53
54                 mask = 0
55                 if reads.has_key(fd): mask = mask | select.POLLIN
56                 if writes.has_key(fd): mask = mask | select.POLLOUT
57                 if mask != 0:
58                         poller.register(fd, mask)
59                 else:
60                         if selectables.has_key(fd): del selectables[fd]
61                 
62                 
63                 poller.eApp.interruptPoll()
64
65         def _dictRemove(self, selectable, mdict):
66                 try:
67                         # the easy way
68                         fd = selectable.fileno()
69                         # make sure the fd is actually real.  In some situations we can get
70                         # -1 here.
71                         mdict[fd]
72                 except:
73                         # the hard way: necessary because fileno() may disappear at any
74                         # moment, thanks to python's underlying sockets impl
75                         for fd, fdes in selectables.items():
76                                 if selectable is fdes:
77                                         break
78                         else:
79                                 # Hmm, maybe not the right course of action?  This method can't
80                                 # fail, because it happens inside error detection...
81                                 return
82                 if mdict.has_key(fd):
83                         del mdict[fd]
84                         self._updateRegistration(fd)
85
86         def addReader(self, reader):
87                 """Add a FileDescriptor for notification of data available to read.
88                 """
89                 fd = reader.fileno()
90                 if not reads.has_key(fd):
91                         selectables[fd] = reader
92                         reads[fd] =  1
93                         self._updateRegistration(fd)
94
95         def addWriter(self, writer, writes=writes, selectables=selectables):
96                 """Add a FileDescriptor for notification of data available to write.
97                 """
98                 fd = writer.fileno()
99                 if not writes.has_key(fd):
100                         selectables[fd] = writer
101                         writes[fd] =  1
102                         self._updateRegistration(fd)
103
104         def removeReader(self, reader, reads=reads):
105                 """Remove a Selectable for notification of data available to read.
106                 """
107                 return self._dictRemove(reader, reads)
108
109         def removeWriter(self, writer, writes=writes):
110                 """Remove a Selectable for notification of data available to write.
111                 """
112                 return self._dictRemove(writer, writes)
113
114         def removeAll(self, reads=reads, writes=writes, selectables=selectables):
115                 """Remove all selectables, and return a list of them."""
116                 if self.waker is not None:
117                         self.removeReader(self.waker)
118                 result = selectables.values()
119                 fds = selectables.keys()
120                 reads.clear()
121                 writes.clear()
122                 selectables.clear()
123                 for fd in fds:
124                         poller.unregister(fd)
125                         
126                 if self.waker is not None:
127                         self.addReader(self.waker)
128                 return result
129
130         def doPoll(self, timeout,
131                            reads=reads,
132                            writes=writes,
133                            selectables=selectables,
134                            select=select,
135                            log=log,
136                            POLLIN=select.POLLIN,
137                            POLLOUT=select.POLLOUT):
138                 """Poll the poller for new events."""
139                 
140                 if timeout is not None:
141                         timeout = int(timeout * 1000) # convert seconds to milliseconds
142
143                 try:
144                         l = poller.poll(timeout)
145                         if l is None:
146                                 if self.running:
147                                         self.stop()
148                                 l = [ ]
149                 except select.error, e:
150                         if e[0] == errno.EINTR:
151                                 return
152                         else:
153                                 raise
154                 _drdw = self._doReadOrWrite
155                 for fd, event in l:
156                         try:
157                                 selectable = selectables[fd]
158                         except KeyError:
159                                 # Handles the infrequent case where one selectable's
160                                 # handler disconnects another.
161                                 continue
162                         log.callWithLogger(selectable, _drdw, selectable, fd, event, POLLIN, POLLOUT, log)
163
164         doIteration = doPoll
165
166         def _doReadOrWrite(self, selectable, fd, event, POLLIN, POLLOUT, log, 
167                 faildict={
168                         error.ConnectionDone: failure.Failure(error.ConnectionDone()),
169                         error.ConnectionLost: failure.Failure(error.ConnectionLost())
170                 }):
171                 why = None
172                 inRead = False
173                 if event & POLL_DISCONNECTED and not (event & POLLIN):
174                         why = main.CONNECTION_LOST
175                 else:
176                         try:
177                                 if event & POLLIN:
178                                         why = selectable.doRead()
179                                         inRead = True
180                                 if not why and event & POLLOUT:
181                                         why = selectable.doWrite()
182                                         inRead = False
183                                 if not selectable.fileno() == fd:
184                                         why = error.ConnectionFdescWentAway('Filedescriptor went away')
185                                         inRead = False
186                         except:
187                                 log.deferr()
188                                 why = sys.exc_info()[1]
189                 if why:
190                         self._disconnectSelectable(selectable, why, inRead)
191
192         def callLater(self, *args, **kwargs):
193                 poller.eApp.interruptPoll()
194                 return posixbase.PosixReactorBase.callLater(self, *args, **kwargs)
195
196 def install():
197         """Install the poll() reactor."""
198         
199         p = PollReactor()
200         main.installReactor(p)
201
202 __all__ = ["PollReactor", "install"]