add e2reactor
[openblackhole/openblackhole-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, threadable, 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
31         def register(self, fd, eventmask = select.POLLIN | select.POLLERR | select.POLLOUT):
32                 self.dict[fd] = eventmask
33         
34         def unregister(self, fd):
35                 del self.dict[fd]
36         
37         def poll(self, timeout = None):
38                 return getApplication().poll(timeout, self.dict)
39
40 poller = E2SharedPoll()
41
42 class PollReactor(posixbase.PosixReactorBase):
43         """A reactor that uses poll(2)."""
44
45         def _updateRegistration(self, fd):
46                 """Register/unregister an fd with the poller."""
47                 try:
48                         poller.unregister(fd)
49                 except KeyError:
50                         pass
51
52                 mask = 0
53                 if reads.has_key(fd): mask = mask | select.POLLIN
54                 if writes.has_key(fd): mask = mask | select.POLLOUT
55                 if mask != 0:
56                         poller.register(fd, mask)
57                 else:
58                         if selectables.has_key(fd): del selectables[fd]
59
60         def _dictRemove(self, selectable, mdict):
61                 try:
62                         # the easy way
63                         fd = selectable.fileno()
64                         # make sure the fd is actually real.  In some situations we can get
65                         # -1 here.
66                         mdict[fd]
67                 except:
68                         # the hard way: necessary because fileno() may disappear at any
69                         # moment, thanks to python's underlying sockets impl
70                         for fd, fdes in selectables.items():
71                                 if selectable is fdes:
72                                         break
73                         else:
74                                 # Hmm, maybe not the right course of action?  This method can't
75                                 # fail, because it happens inside error detection...
76                                 return
77                 if mdict.has_key(fd):
78                         del mdict[fd]
79                         self._updateRegistration(fd)
80
81         def addReader(self, reader):
82                 """Add a FileDescriptor for notification of data available to read.
83                 """
84                 fd = reader.fileno()
85                 if not reads.has_key(fd):
86                         selectables[fd] = reader
87                         reads[fd] =  1
88                         self._updateRegistration(fd)
89
90         def addWriter(self, writer, writes=writes, selectables=selectables):
91                 """Add a FileDescriptor for notification of data available to write.
92                 """
93                 fd = writer.fileno()
94                 if not writes.has_key(fd):
95                         selectables[fd] = writer
96                         writes[fd] =  1
97                         self._updateRegistration(fd)
98
99         def removeReader(self, reader, reads=reads):
100                 """Remove a Selectable for notification of data available to read.
101                 """
102                 return self._dictRemove(reader, reads)
103
104         def removeWriter(self, writer, writes=writes):
105                 """Remove a Selectable for notification of data available to write.
106                 """
107                 return self._dictRemove(writer, writes)
108
109         def removeAll(self, reads=reads, writes=writes, selectables=selectables):
110                 """Remove all selectables, and return a list of them."""
111                 if self.waker is not None:
112                         self.removeReader(self.waker)
113                 result = selectables.values()
114                 fds = selectables.keys()
115                 reads.clear()
116                 writes.clear()
117                 selectables.clear()
118                 for fd in fds:
119                         poller.unregister(fd)
120                         
121                 if self.waker is not None:
122                         self.addReader(self.waker)
123                 return result
124
125         def doPoll(self, timeout,
126                            reads=reads,
127                            writes=writes,
128                            selectables=selectables,
129                            select=select,
130                            log=log,
131                            POLLIN=select.POLLIN,
132                            POLLOUT=select.POLLOUT):
133                 """Poll the poller for new events."""
134                 if timeout is not None:
135                         timeout = int(timeout * 1000) # convert seconds to milliseconds
136
137                 try:
138                         l = poller.poll(timeout)
139                         if l is None:
140                                 if self.running:
141                                         self.stop()
142                                 l = [ ]
143                 except select.error, e:
144                         if e[0] == errno.EINTR:
145                                 return
146                         else:
147                                 raise
148                 _drdw = self._doReadOrWrite
149                 for fd, event in l:
150                         try:
151                                 selectable = selectables[fd]
152                         except KeyError:
153                                 # Handles the infrequent case where one selectable's
154                                 # handler disconnects another.
155                                 continue
156                         log.callWithLogger(selectable, _drdw, selectable, fd, event, POLLIN, POLLOUT, log)
157
158         doIteration = doPoll
159
160         def _doReadOrWrite(self, selectable, fd, event, POLLIN, POLLOUT, log, 
161                 faildict={
162                         error.ConnectionDone: failure.Failure(error.ConnectionDone()),
163                         error.ConnectionLost: failure.Failure(error.ConnectionLost())
164                 }):
165                 why = None
166                 inRead = False
167                 if event & POLL_DISCONNECTED and not (event & POLLIN):
168                         why = main.CONNECTION_LOST
169                 else:
170                         try:
171                                 if event & POLLIN:
172                                         why = selectable.doRead()
173                                         inRead = True
174                                 if not why and event & POLLOUT:
175                                         why = selectable.doWrite()
176                                         inRead = False
177                                 if not selectable.fileno() == fd:
178                                         why = error.ConnectionFdescWentAway('Filedescriptor went away')
179                                         inRead = False
180                         except:
181                                 log.deferr()
182                                 why = sys.exc_info()[1]
183                 if why:
184                         self._disconnectSelectable(selectable, why, inRead)
185
186
187 def install():
188         """Install the poll() reactor."""
189         
190         p = PollReactor()
191         main.installReactor(p)
192
193 __all__ = ["PollReactor", "install"]