PageRenderTime 39ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/module/select/interp_select.py

https://bitbucket.org/pypy/pypy/
Python | 216 lines | 155 code | 31 blank | 30 comment | 31 complexity | 4a93da5963db596814690fe90f11241f MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import errno
  2. from rpython.rlib import _rsocket_rffi as _c, rpoll
  3. from rpython.rtyper.lltypesystem import lltype, rffi
  4. from pypy.interpreter.baseobjspace import W_Root
  5. from pypy.interpreter.error import OperationError, oefmt, wrap_oserror
  6. from pypy.interpreter.gateway import WrappedDefault, interp2app, unwrap_spec
  7. from pypy.interpreter.typedef import TypeDef
  8. defaultevents = rpoll.POLLIN | rpoll.POLLOUT | rpoll.POLLPRI
  9. class Cache:
  10. def __init__(self, space):
  11. self.w_error = space.new_exception_class("select.error")
  12. def poll(space):
  13. """Returns a polling object, which supports registering and
  14. unregistering file descriptors, and then polling them for I/O
  15. events.
  16. """
  17. return Poll()
  18. class Poll(W_Root):
  19. def __init__(self):
  20. self.fddict = {}
  21. self.running = False
  22. @unwrap_spec(events="c_ushort")
  23. def register(self, space, w_fd, events=defaultevents):
  24. fd = space.c_filedescriptor_w(w_fd)
  25. self.fddict[fd] = events
  26. @unwrap_spec(events="c_ushort")
  27. def modify(self, space, w_fd, events):
  28. fd = space.c_filedescriptor_w(w_fd)
  29. if fd not in self.fddict:
  30. raise wrap_oserror(space, OSError(errno.ENOENT, "poll.modify"),
  31. exception_name='w_IOError')
  32. self.fddict[fd] = events
  33. def unregister(self, space, w_fd):
  34. fd = space.c_filedescriptor_w(w_fd)
  35. try:
  36. del self.fddict[fd]
  37. except KeyError:
  38. raise OperationError(space.w_KeyError, space.wrap(fd))
  39. @unwrap_spec(w_timeout=WrappedDefault(None))
  40. def poll(self, space, w_timeout):
  41. if space.is_w(w_timeout, space.w_None):
  42. timeout = -1
  43. else:
  44. # we want to be compatible with cpython and also accept things
  45. # that can be casted to integer (I think)
  46. try:
  47. # compute the integer
  48. w_timeout = space.int(w_timeout)
  49. except OperationError:
  50. raise oefmt(space.w_TypeError,
  51. "timeout must be an integer or None")
  52. timeout = space.c_int_w(w_timeout)
  53. if self.running:
  54. raise oefmt(space.w_RuntimeError, "concurrent poll() invocation")
  55. self.running = True
  56. try:
  57. retval = rpoll.poll(self.fddict, timeout)
  58. except rpoll.PollError as e:
  59. w_errortype = space.fromcache(Cache).w_error
  60. message = e.get_msg()
  61. raise OperationError(w_errortype,
  62. space.newtuple([space.wrap(e.errno),
  63. space.wrap(message)]))
  64. finally:
  65. self.running = False
  66. retval_w = []
  67. for fd, revents in retval:
  68. retval_w.append(space.newtuple([space.wrap(fd),
  69. space.wrap(revents)]))
  70. return space.newlist(retval_w)
  71. pollmethods = {}
  72. for methodname in 'register modify unregister poll'.split():
  73. pollmethods[methodname] = interp2app(getattr(Poll, methodname))
  74. Poll.typedef = TypeDef('select.poll', **pollmethods)
  75. # ____________________________________________________________
  76. def _build_fd_set(space, list_w, ll_list, nfds):
  77. _c.FD_ZERO(ll_list)
  78. fdlist = []
  79. for w_f in list_w:
  80. fd = space.c_filedescriptor_w(w_f)
  81. if fd > nfds:
  82. if _c.MAX_FD_SIZE is not None and fd >= _c.MAX_FD_SIZE:
  83. raise oefmt(space.w_ValueError,
  84. "file descriptor out of range in select()")
  85. nfds = fd
  86. _c.FD_SET(fd, ll_list)
  87. fdlist.append(fd)
  88. return fdlist, nfds
  89. _build_fd_set._always_inline_ = True # get rid of the tuple result
  90. def _unbuild_fd_set(space, list_w, fdlist, ll_list, reslist_w):
  91. for i in range(len(fdlist)):
  92. fd = fdlist[i]
  93. if _c.FD_ISSET(fd, ll_list):
  94. reslist_w.append(list_w[i])
  95. def _call_select(space, iwtd_w, owtd_w, ewtd_w,
  96. ll_inl, ll_outl, ll_errl, ll_timeval):
  97. fdlistin = fdlistout = fdlisterr = None
  98. nfds = -1
  99. if ll_inl:
  100. fdlistin, nfds = _build_fd_set(space, iwtd_w, ll_inl, nfds)
  101. if ll_outl:
  102. fdlistout, nfds = _build_fd_set(space, owtd_w, ll_outl, nfds)
  103. if ll_errl:
  104. fdlisterr, nfds = _build_fd_set(space, ewtd_w, ll_errl, nfds)
  105. res = _c.select(nfds + 1, ll_inl, ll_outl, ll_errl, ll_timeval)
  106. if res < 0:
  107. errno = _c.geterrno()
  108. msg = _c.socket_strerror_str(errno)
  109. w_errortype = space.fromcache(Cache).w_error
  110. raise OperationError(w_errortype, space.newtuple([
  111. space.wrap(errno), space.wrap(msg)]))
  112. resin_w = []
  113. resout_w = []
  114. reserr_w = []
  115. if res > 0:
  116. if fdlistin is not None:
  117. _unbuild_fd_set(space, iwtd_w, fdlistin, ll_inl, resin_w)
  118. if fdlistout is not None:
  119. _unbuild_fd_set(space, owtd_w, fdlistout, ll_outl, resout_w)
  120. if fdlisterr is not None:
  121. _unbuild_fd_set(space, ewtd_w, fdlisterr, ll_errl, reserr_w)
  122. return space.newtuple([space.newlist(resin_w),
  123. space.newlist(resout_w),
  124. space.newlist(reserr_w)])
  125. @unwrap_spec(w_timeout=WrappedDefault(None))
  126. def select(space, w_iwtd, w_owtd, w_ewtd, w_timeout):
  127. """Wait until one or more file descriptors are ready for some kind of I/O.
  128. The first three arguments are sequences of file descriptors to be waited for:
  129. rlist -- wait until ready for reading
  130. wlist -- wait until ready for writing
  131. xlist -- wait for an ``exceptional condition''
  132. If only one kind of condition is required, pass [] for the other lists.
  133. A file descriptor is either a socket or file object, or a small integer
  134. gotten from a fileno() method call on one of those.
  135. The optional 4th argument specifies a timeout in seconds; it may be
  136. a floating point number to specify fractions of seconds. If it is absent
  137. or None, the call will never time out.
  138. The return value is a tuple of three lists corresponding to the first three
  139. arguments; each contains the subset of the corresponding file descriptors
  140. that are ready.
  141. *** IMPORTANT NOTICE ***
  142. On Windows, only sockets are supported; on Unix, all file descriptors.
  143. """
  144. iwtd_w = space.unpackiterable(w_iwtd)
  145. owtd_w = space.unpackiterable(w_owtd)
  146. ewtd_w = space.unpackiterable(w_ewtd)
  147. if space.is_w(w_timeout, space.w_None):
  148. timeout = -1.0
  149. else:
  150. timeout = space.float_w(w_timeout)
  151. ll_inl = lltype.nullptr(_c.fd_set.TO)
  152. ll_outl = lltype.nullptr(_c.fd_set.TO)
  153. ll_errl = lltype.nullptr(_c.fd_set.TO)
  154. ll_timeval = lltype.nullptr(_c.timeval)
  155. try:
  156. if len(iwtd_w) > 0:
  157. ll_inl = lltype.malloc(_c.fd_set.TO, flavor='raw')
  158. if len(owtd_w) > 0:
  159. ll_outl = lltype.malloc(_c.fd_set.TO, flavor='raw')
  160. if len(ewtd_w) > 0:
  161. ll_errl = lltype.malloc(_c.fd_set.TO, flavor='raw')
  162. if timeout >= 0.0:
  163. ll_timeval = rffi.make(_c.timeval)
  164. i = int(timeout)
  165. rffi.setintfield(ll_timeval, 'c_tv_sec', i)
  166. rffi.setintfield(ll_timeval, 'c_tv_usec', int((timeout-i)*1000000))
  167. # Call this as a separate helper to avoid a large piece of code
  168. # in try:finally:. Needed for calling further _always_inline_
  169. # helpers like _build_fd_set().
  170. return _call_select(space, iwtd_w, owtd_w, ewtd_w,
  171. ll_inl, ll_outl, ll_errl, ll_timeval)
  172. finally:
  173. if ll_timeval:
  174. lltype.free(ll_timeval, flavor='raw')
  175. if ll_errl:
  176. lltype.free(ll_errl, flavor='raw')
  177. if ll_outl:
  178. lltype.free(ll_outl, flavor='raw')
  179. if ll_inl:
  180. lltype.free(ll_inl, flavor='raw')