1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
"""
An RPython implementation of select.poll() based on rffi.
Note that this is not a drop-in replacement: the interface is
simplified - instead of a polling object there is only a poll()
function that directly takes a dictionary as argument.
"""
from errno import EINTR
from rpython.rlib import _rsocket_rffi as _c
from rpython.rlib.rarithmetic import r_uint
from rpython.rtyper.lltypesystem import lltype, rffi
# ____________________________________________________________
# events
#
eventnames = '''POLLIN POLLPRI POLLOUT POLLERR POLLHUP POLLNVAL
POLLRDNORM POLLRDBAND POLLWRNORM POLLWEBAND POLLMSG
FD_SETSIZE'''.split()
eventnames = [name for name in eventnames
if _c.constants.get(name) is not None]
for name in eventnames:
globals()[name] = _c.constants[name]
class PollError(Exception):
def __init__(self, errno):
self.errno = errno
def get_msg(self):
return _c.socket_strerror_str(self.errno)
class SelectError(Exception):
def __init__(self, errno):
self.errno = errno
def get_msg(self):
return _c.socket_strerror_str(self.errno)
# ____________________________________________________________
# poll() for POSIX systems
#
if hasattr(_c, 'poll'):
def poll(fddict, timeout=-1):
"""'fddict' maps file descriptors to interesting events.
'timeout' is an integer in milliseconds, and NOT a float
number of seconds, but it's the same in CPython. Use -1 for infinite.
Returns a list [(fd, events)].
"""
numfd = len(fddict)
pollfds = lltype.malloc(_c.pollfdarray, numfd, flavor='raw')
try:
i = 0
for fd, events in fddict.iteritems():
rffi.setintfield(pollfds[i], 'c_fd', fd)
rffi.setintfield(pollfds[i], 'c_events', events)
i += 1
assert i == numfd
ret = _c.poll(pollfds, numfd, timeout)
if ret < 0:
raise PollError(_c.geterrno())
retval = []
for i in range(numfd):
pollfd = pollfds[i]
fd = rffi.cast(lltype.Signed, pollfd.c_fd)
revents = rffi.cast(lltype.Signed, pollfd.c_revents)
if revents:
retval.append((fd, revents))
finally:
lltype.free(pollfds, flavor='raw')
return retval
def select(inl, outl, excl, timeout=-1.0, handle_eintr=False):
nfds = 0
if inl:
ll_inl = lltype.malloc(_c.fd_set.TO, flavor='raw')
_c.FD_ZERO(ll_inl)
for i in inl:
_c.FD_SET(i, ll_inl)
if i > nfds:
nfds = i
else:
ll_inl = lltype.nullptr(_c.fd_set.TO)
if outl:
ll_outl = lltype.malloc(_c.fd_set.TO, flavor='raw')
_c.FD_ZERO(ll_outl)
for i in outl:
_c.FD_SET(i, ll_outl)
if i > nfds:
nfds = i
else:
ll_outl = lltype.nullptr(_c.fd_set.TO)
if excl:
ll_excl = lltype.malloc(_c.fd_set.TO, flavor='raw')
_c.FD_ZERO(ll_excl)
for i in excl:
_c.FD_SET(i, ll_excl)
if i > nfds:
nfds = i
else:
ll_excl = lltype.nullptr(_c.fd_set.TO)
if timeout < 0:
ll_timeval = lltype.nullptr(_c.timeval)
while True:
res = _c.select(nfds + 1, ll_inl, ll_outl, ll_excl, ll_timeval)
if not handle_eintr or res >= 0 or _c.geterrno() != EINTR:
break
else:
sec = int(timeout)
usec = int((timeout - sec) * 10**6)
ll_timeval = rffi.make(_c.timeval)
rffi.setintfield(ll_timeval, 'c_tv_sec', sec)
rffi.setintfield(ll_timeval, 'c_tv_usec', usec)
res = _c.select(nfds + 1, ll_inl, ll_outl, ll_excl, ll_timeval)
if handle_eintr and res < 0 and _c.geterrno() == EINTR:
res = 0 # interrupted, act as timed out
try:
if res == -1:
raise SelectError(_c.geterrno())
if res == 0:
return ([], [], [])
else:
return (
[i for i in inl if _c.FD_ISSET(i, ll_inl)],
[i for i in outl if _c.FD_ISSET(i, ll_outl)],
[i for i in excl if _c.FD_ISSET(i, ll_excl)])
finally:
if ll_inl:
lltype.free(ll_inl, flavor='raw')
if ll_outl:
lltype.free(ll_outl, flavor='raw')
if ll_excl:
lltype.free(ll_excl, flavor='raw')
if ll_timeval:
lltype.free(ll_timeval, flavor='raw')
# ____________________________________________________________
# poll() for Win32
#
if hasattr(_c, 'WSAEventSelect'):
# WSAWaitForMultipleEvents is broken. If you wish to try it,
# rename the function to poll() and run test_exchange in test_rpoll
def _poll(fddict, timeout=-1):
"""'fddict' maps file descriptors to interesting events.
'timeout' is an integer in milliseconds, and NOT a float
number of seconds, but it's the same in CPython. Use -1 for infinite.
Returns a list [(fd, events)].
"""
numfd = len(fddict)
numevents = 0
socketevents = lltype.malloc(_c.WSAEVENT_ARRAY, numfd, flavor='raw')
try:
eventdict = {}
for fd, events in fddict.iteritems():
# select desired events
wsaEvents = 0
if events & _c.POLLIN:
wsaEvents |= _c.FD_READ | _c.FD_ACCEPT | _c.FD_CLOSE
if events & _c.POLLOUT:
wsaEvents |= _c.FD_WRITE | _c.FD_CONNECT | _c.FD_CLOSE
# if no events then ignore socket
if wsaEvents == 0:
continue
# select socket for desired events
event = _c.WSACreateEvent()
if _c.WSAEventSelect(fd, event, wsaEvents) != 0:
raise PollError(_c.geterrno())
eventdict[fd] = event
socketevents[numevents] = event
numevents += 1
assert numevents <= numfd
# if no sockets then return immediately
# XXX commented out by arigo - we just want to sleep for
# 'timeout' milliseconds in this case, which is what
# I hope WSAWaitForMultipleEvents will do, no?
#if numevents == 0:
# return []
# prepare timeout
if timeout < 0:
timeout = _c.INFINITE
# XXX does not correctly report write status of a port
ret = _c.WSAWaitForMultipleEvents(numevents, socketevents,
False, timeout, False)
if ret == _c.WSA_WAIT_TIMEOUT:
return []
if ret == r_uint(_c.WSA_WAIT_FAILED):
raise PollError(_c.geterrno())
retval = []
info = rffi.make(_c.WSANETWORKEVENTS)
for fd, event in eventdict.iteritems():
if _c.WSAEnumNetworkEvents(fd, event, info) < 0:
continue
revents = 0
if info.c_lNetworkEvents & _c.FD_READ:
revents |= _c.POLLIN
if info.c_lNetworkEvents & _c.FD_ACCEPT:
revents |= _c.POLLIN
if info.c_lNetworkEvents & _c.FD_WRITE:
revents |= _c.POLLOUT
if info.c_lNetworkEvents & _c.FD_CONNECT:
if info.c_iErrorCode[_c.FD_CONNECT_BIT]:
revents |= _c.POLLERR
else:
revents |= _c.POLLOUT
if info.c_lNetworkEvents & _c.FD_CLOSE:
if info.c_iErrorCode[_c.FD_CLOSE_BIT]:
revents |= _c.POLLERR
else:
if fddict[fd] & _c.POLLIN:
revents |= _c.POLLIN
if fddict[fd] & _c.POLLOUT:
revents |= _c.POLLOUT
if revents:
retval.append((fd, revents))
lltype.free(info, flavor='raw')
finally:
for fd, event in eventdict.iteritems():
_c.WSAEventSelect(fd, event, 0)
_c.WSACloseEvent(event)
lltype.free(socketevents, flavor='raw')
return retval
|