PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/_multiprocessing/interp_connection.py

https://bitbucket.org/pypy/pypy/
Python | 542 lines | 451 code | 82 blank | 9 comment | 99 complexity | b3c8d8af1f5645e0b135b70991dbc3d9 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys
  2. from errno import EINTR
  3. from rpython.rlib import rpoll, rsocket
  4. from rpython.rlib.rarithmetic import intmask
  5. from rpython.rtyper.lltypesystem import lltype, rffi
  6. from pypy.interpreter.baseobjspace import W_Root
  7. from pypy.interpreter.error import OperationError, oefmt, wrap_oserror
  8. from pypy.interpreter.gateway import (
  9. WrappedDefault, interp2app, interpindirect2app, unwrap_spec)
  10. from pypy.interpreter.typedef import GetSetProperty, TypeDef
  11. READABLE, WRITABLE = range(1, 3)
  12. PY_SSIZE_T_MAX = sys.maxint
  13. PY_SSIZE_T_MIN = -sys.maxint - 1
  14. class State(object):
  15. def __init__(self, space):
  16. pass
  17. def init(self, space):
  18. w_builtins = space.getbuiltinmodule('__builtin__')
  19. w_module = space.call_method(
  20. w_builtins, '__import__', space.wrap("multiprocessing"))
  21. self.w_BufferTooShort = space.getattr(w_module, space.wrap("BufferTooShort"))
  22. self.w_picklemodule = space.call_method(
  23. w_builtins, '__import__', space.wrap("pickle"))
  24. def BufferTooShort(space, w_data):
  25. w_BufferTooShort = space.fromcache(State).w_BufferTooShort
  26. return OperationError(w_BufferTooShort, w_data)
  27. def w_handle(space, handle):
  28. return space.wrap(rffi.cast(rffi.INTPTR_T, handle))
  29. class W_BaseConnection(W_Root):
  30. BUFFER_SIZE = 1024
  31. buffer = lltype.nullptr(rffi.CCHARP.TO)
  32. def __init__(self, space, flags):
  33. self.flags = flags
  34. self.buffer = lltype.malloc(rffi.CCHARP.TO, self.BUFFER_SIZE,
  35. flavor='raw')
  36. self.register_finalizer(space)
  37. def _finalize_(self):
  38. buf = self.buffer
  39. if buf:
  40. self.buffer = lltype.nullptr(rffi.CCHARP.TO)
  41. lltype.free(buf, flavor='raw')
  42. try:
  43. self.do_close()
  44. except OSError:
  45. pass
  46. # Abstract methods
  47. def do_close(self):
  48. raise NotImplementedError
  49. def is_valid(self):
  50. return False
  51. def do_send_string(self, space, buf, offset, size):
  52. raise NotImplementedError
  53. def do_recv_string(self, space, buflength, maxlength):
  54. raise NotImplementedError
  55. def do_poll(self, space, timeout):
  56. raise NotImplementedError
  57. def close(self):
  58. self.do_close()
  59. def closed_get(self, space):
  60. return space.newbool(not self.is_valid())
  61. def readable_get(self, space):
  62. return space.newbool(bool(self.flags & READABLE))
  63. def writable_get(self, space):
  64. return space.newbool(bool(self.flags & WRITABLE))
  65. def _repr(self, space, handle):
  66. conn_type = ["read-only", "write-only", "read-write"][self.flags - 1]
  67. return space.wrap("<%s %s, handle %d>" % (
  68. conn_type, space.type(self).getname(space), handle))
  69. def descr_repr(self, space):
  70. raise NotImplementedError
  71. def _check_readable(self, space):
  72. if not self.flags & READABLE:
  73. raise oefmt(space.w_IOError, "connection is write-only")
  74. def _check_writable(self, space):
  75. if not self.flags & WRITABLE:
  76. raise oefmt(space.w_IOError, "connection is read-only")
  77. @unwrap_spec(offset='index', size='index')
  78. def send_bytes(self, space, w_buf, offset=0, size=PY_SSIZE_T_MIN):
  79. buf = space.getarg_w('s*', w_buf).as_str()
  80. length = len(buf)
  81. self._check_writable(space)
  82. if offset < 0:
  83. raise oefmt(space.w_ValueError, "offset is negative")
  84. if length < offset:
  85. raise oefmt(space.w_ValueError, "buffer length < offset")
  86. if size == PY_SSIZE_T_MIN:
  87. size = length - offset
  88. elif size < 0:
  89. raise oefmt(space.w_ValueError, "size is negative")
  90. elif offset + size > length:
  91. raise oefmt(space.w_ValueError, "buffer length > offset + size")
  92. self.do_send_string(space, buf, offset, size)
  93. @unwrap_spec(maxlength='index')
  94. def recv_bytes(self, space, maxlength=PY_SSIZE_T_MAX):
  95. self._check_readable(space)
  96. if maxlength < 0:
  97. raise oefmt(space.w_ValueError, "maxlength < 0")
  98. res, newbuf = self.do_recv_string(
  99. space, self.BUFFER_SIZE, maxlength)
  100. try:
  101. if newbuf:
  102. return space.newbytes(rffi.charpsize2str(newbuf, res))
  103. else:
  104. return space.newbytes(rffi.charpsize2str(self.buffer, res))
  105. finally:
  106. if newbuf:
  107. rffi.free_charp(newbuf)
  108. @unwrap_spec(offset='index')
  109. def recv_bytes_into(self, space, w_buffer, offset=0):
  110. rwbuffer = space.writebuf_w(w_buffer)
  111. length = rwbuffer.getlength()
  112. res, newbuf = self.do_recv_string(
  113. space, length - offset, PY_SSIZE_T_MAX)
  114. try:
  115. if newbuf:
  116. raise BufferTooShort(space, space.newbytes(
  117. rffi.charpsize2str(newbuf, res)))
  118. rwbuffer.setslice(offset, rffi.charpsize2str(self.buffer, res))
  119. finally:
  120. if newbuf:
  121. rffi.free_charp(newbuf)
  122. return space.wrap(res)
  123. def send(self, space, w_obj):
  124. self._check_writable(space)
  125. w_picklemodule = space.fromcache(State).w_picklemodule
  126. w_protocol = space.getattr(
  127. w_picklemodule, space.wrap("HIGHEST_PROTOCOL"))
  128. w_pickled = space.call_method(
  129. w_picklemodule, "dumps", w_obj, w_protocol)
  130. buf = space.str_w(w_pickled)
  131. self.do_send_string(space, buf, 0, len(buf))
  132. def recv(self, space):
  133. self._check_readable(space)
  134. res, newbuf = self.do_recv_string(
  135. space, self.BUFFER_SIZE, PY_SSIZE_T_MAX)
  136. try:
  137. if newbuf:
  138. w_received = space.newbytes(rffi.charpsize2str(newbuf, res))
  139. else:
  140. w_received = space.newbytes(rffi.charpsize2str(self.buffer, res))
  141. finally:
  142. if newbuf:
  143. rffi.free_charp(newbuf)
  144. w_builtins = space.getbuiltinmodule('__builtin__')
  145. w_picklemodule = space.fromcache(State).w_picklemodule
  146. w_unpickled = space.call_method(
  147. w_picklemodule, "loads", w_received)
  148. return w_unpickled
  149. @unwrap_spec(w_timeout=WrappedDefault(0.0))
  150. def poll(self, space, w_timeout):
  151. self._check_readable(space)
  152. if space.is_w(w_timeout, space.w_None):
  153. timeout = -1.0 # block forever
  154. else:
  155. timeout = space.float_w(w_timeout)
  156. if timeout < 0.0:
  157. timeout = 0.0
  158. return space.newbool(self.do_poll(space, timeout))
  159. W_BaseConnection.typedef = TypeDef(
  160. 'BaseConnection',
  161. __repr__ = interpindirect2app(W_BaseConnection.descr_repr),
  162. closed = GetSetProperty(W_BaseConnection.closed_get),
  163. readable = GetSetProperty(W_BaseConnection.readable_get),
  164. writable = GetSetProperty(W_BaseConnection.writable_get),
  165. send_bytes = interp2app(W_BaseConnection.send_bytes),
  166. recv_bytes = interp2app(W_BaseConnection.recv_bytes),
  167. recv_bytes_into = interp2app(W_BaseConnection.recv_bytes_into),
  168. send = interp2app(W_BaseConnection.send),
  169. recv = interp2app(W_BaseConnection.recv),
  170. poll = interp2app(W_BaseConnection.poll),
  171. close = interp2app(W_BaseConnection.close),
  172. )
  173. class W_FileConnection(W_BaseConnection):
  174. INVALID_HANDLE_VALUE = -1
  175. fd = INVALID_HANDLE_VALUE
  176. if sys.platform == 'win32':
  177. def WRITE(self, data):
  178. from rpython.rlib._rsocket_rffi import send, geterrno
  179. length = send(self.fd, data, len(data), 0)
  180. if length < 0:
  181. raise WindowsError(geterrno(), "send")
  182. return length
  183. def READ(self, size):
  184. from rpython.rlib._rsocket_rffi import socketrecv, geterrno
  185. with rffi.scoped_alloc_buffer(size) as buf:
  186. length = socketrecv(self.fd, buf.raw, buf.size, 0)
  187. if length < 0:
  188. raise WindowsError(geterrno(), "recv")
  189. return buf.str(length)
  190. def CLOSE(self):
  191. from rpython.rlib._rsocket_rffi import socketclose
  192. socketclose(self.fd)
  193. else:
  194. def WRITE(self, data):
  195. import os
  196. return os.write(self.fd, data)
  197. def READ(self, length):
  198. import os
  199. return os.read(self.fd, length)
  200. def CLOSE(self):
  201. import os
  202. try:
  203. os.close(self.fd)
  204. except OSError:
  205. pass
  206. def __init__(self, space, fd, flags):
  207. if fd == self.INVALID_HANDLE_VALUE or fd < 0:
  208. raise oefmt(space.w_IOError, "invalid handle %d", fd)
  209. W_BaseConnection.__init__(self, space, flags)
  210. self.fd = fd
  211. @unwrap_spec(fd=int, readable=bool, writable=bool)
  212. def descr_new_file(space, w_subtype, fd, readable=True, writable=True):
  213. flags = (readable and READABLE) | (writable and WRITABLE)
  214. self = space.allocate_instance(W_FileConnection, w_subtype)
  215. W_FileConnection.__init__(self, space, fd, flags)
  216. return space.wrap(self)
  217. def descr_repr(self, space):
  218. return self._repr(space, self.fd)
  219. def fileno(self, space):
  220. return space.wrap(self.fd)
  221. def is_valid(self):
  222. return self.fd != self.INVALID_HANDLE_VALUE
  223. def do_close(self):
  224. if self.is_valid():
  225. self.CLOSE()
  226. self.fd = self.INVALID_HANDLE_VALUE
  227. def do_send_string(self, space, buf, offset, size):
  228. # Since str2charp copies the buf anyway, always combine the
  229. # "header" and the "body" of the message and send them at once.
  230. message = lltype.malloc(rffi.CCHARP.TO, size + 4, flavor='raw')
  231. try:
  232. length = rffi.r_uint(rsocket.htonl(
  233. rffi.cast(lltype.Unsigned, size)))
  234. rffi.cast(rffi.UINTP, message)[0] = length
  235. i = size - 1
  236. while i >= 0:
  237. message[4 + i] = buf[offset + i]
  238. i -= 1
  239. self._sendall(space, message, size + 4)
  240. finally:
  241. lltype.free(message, flavor='raw')
  242. def do_recv_string(self, space, buflength, maxlength):
  243. with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as length_ptr:
  244. self._recvall(space, rffi.cast(rffi.CCHARP, length_ptr), 4)
  245. length = intmask(rsocket.ntohl(
  246. rffi.cast(lltype.Unsigned, length_ptr[0])))
  247. if length > maxlength: # bad message, close connection
  248. self.flags &= ~READABLE
  249. if self.flags == 0:
  250. self.close()
  251. raise oefmt(space.w_IOError, "bad message length")
  252. if length <= buflength:
  253. self._recvall(space, self.buffer, length)
  254. return length, lltype.nullptr(rffi.CCHARP.TO)
  255. else:
  256. newbuf = lltype.malloc(rffi.CCHARP.TO, length, flavor='raw')
  257. self._recvall(space, newbuf, length)
  258. return length, newbuf
  259. def _sendall(self, space, message, size):
  260. while size > 0:
  261. # XXX inefficient
  262. data = rffi.charpsize2str(message, size)
  263. try:
  264. count = self.WRITE(data)
  265. except OSError as e:
  266. if e.errno == EINTR:
  267. space.getexecutioncontext().checksignals()
  268. continue
  269. raise wrap_oserror(space, e)
  270. size -= count
  271. message = rffi.ptradd(message, count)
  272. def _recvall(self, space, buf, length):
  273. length = intmask(length)
  274. remaining = length
  275. while remaining > 0:
  276. try:
  277. data = self.READ(remaining)
  278. except OSError as e:
  279. if e.errno == EINTR:
  280. space.getexecutioncontext().checksignals()
  281. continue
  282. raise wrap_oserror(space, e)
  283. count = len(data)
  284. if count == 0:
  285. if remaining == length:
  286. raise OperationError(space.w_EOFError, space.w_None)
  287. else:
  288. raise oefmt(space.w_IOError,
  289. "got end of file during message")
  290. # XXX inefficient
  291. for i in range(count):
  292. buf[i] = data[i]
  293. remaining -= count
  294. buf = rffi.ptradd(buf, count)
  295. if sys.platform == 'win32':
  296. def _check_fd(self):
  297. return self.fd >= 0
  298. else:
  299. def _check_fd(self):
  300. return self.fd >= 0 and self.fd < rpoll.FD_SETSIZE
  301. def do_poll(self, space, timeout):
  302. if not self._check_fd():
  303. raise oefmt(space.w_IOError, "handle out of range in select()")
  304. r, w, e = rpoll.select([self.fd], [], [], timeout, handle_eintr=True)
  305. return bool(r)
  306. W_FileConnection.typedef = TypeDef(
  307. '_multiprocessing.Connection', W_BaseConnection.typedef,
  308. __new__ = interp2app(W_FileConnection.descr_new_file.im_func),
  309. fileno = interp2app(W_FileConnection.fileno),
  310. )
  311. class W_PipeConnection(W_BaseConnection):
  312. if sys.platform == 'win32':
  313. from rpython.rlib.rwin32 import INVALID_HANDLE_VALUE
  314. def __init__(self, space, handle, flags):
  315. W_BaseConnection.__init__(self, space, flags)
  316. self.handle = handle
  317. @unwrap_spec(readable=bool, writable=bool)
  318. def descr_new_pipe(space, w_subtype, w_handle, readable=True,
  319. writable=True):
  320. from pypy.module._multiprocessing.interp_win32 import handle_w
  321. handle = handle_w(space, w_handle)
  322. flags = (readable and READABLE) | (writable and WRITABLE)
  323. self = space.allocate_instance(W_PipeConnection, w_subtype)
  324. W_PipeConnection.__init__(self, space, handle, flags)
  325. return space.wrap(self)
  326. def descr_repr(self, space):
  327. return self._repr(space, rffi.cast(rffi.INTPTR_T, self.handle))
  328. def is_valid(self):
  329. return self.handle != self.INVALID_HANDLE_VALUE
  330. def fileno(self, space):
  331. return w_handle(space, self.handle)
  332. def do_close(self):
  333. from rpython.rlib.rwin32 import CloseHandle
  334. if self.is_valid():
  335. CloseHandle(self.handle)
  336. self.handle = self.INVALID_HANDLE_VALUE
  337. def do_send_string(self, space, buf, offset, size):
  338. from pypy.module._multiprocessing.interp_win32 import (
  339. _WriteFile, ERROR_NO_SYSTEM_RESOURCES)
  340. from rpython.rlib import rwin32
  341. with rffi.scoped_view_charp(buf) as charp:
  342. written_ptr = lltype.malloc(rffi.CArrayPtr(rwin32.DWORD).TO, 1,
  343. flavor='raw')
  344. try:
  345. result = _WriteFile(
  346. self.handle, rffi.ptradd(charp, offset),
  347. size, written_ptr, rffi.NULL)
  348. if (result == 0 and
  349. rwin32.GetLastError_saved() == ERROR_NO_SYSTEM_RESOURCES):
  350. raise oefmt(space.w_ValueError,
  351. "Cannot send %d bytes over connection", size)
  352. finally:
  353. lltype.free(written_ptr, flavor='raw')
  354. def do_recv_string(self, space, buflength, maxlength):
  355. from pypy.module._multiprocessing.interp_win32 import (
  356. _ReadFile, _PeekNamedPipe, ERROR_BROKEN_PIPE, ERROR_MORE_DATA)
  357. from rpython.rlib import rwin32
  358. from pypy.interpreter.error import wrap_windowserror
  359. read_ptr = lltype.malloc(rffi.CArrayPtr(rwin32.DWORD).TO, 1,
  360. flavor='raw')
  361. left_ptr = lltype.malloc(rffi.CArrayPtr(rwin32.DWORD).TO, 1,
  362. flavor='raw')
  363. try:
  364. result = _ReadFile(self.handle,
  365. self.buffer, min(self.BUFFER_SIZE, buflength),
  366. read_ptr, rffi.NULL)
  367. if result:
  368. return intmask(read_ptr[0]), lltype.nullptr(rffi.CCHARP.TO)
  369. err = rwin32.GetLastError_saved()
  370. if err == ERROR_BROKEN_PIPE:
  371. raise OperationError(space.w_EOFError, space.w_None)
  372. elif err != ERROR_MORE_DATA:
  373. raise wrap_windowserror(space, WindowsError(err, "_ReadFile"))
  374. # More data...
  375. if not _PeekNamedPipe(self.handle, rffi.NULL, 0,
  376. lltype.nullptr(rwin32.LPDWORD.TO),
  377. lltype.nullptr(rwin32.LPDWORD.TO),
  378. left_ptr):
  379. raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
  380. length = intmask(read_ptr[0] + left_ptr[0])
  381. if length > maxlength: # bad message, close connection
  382. self.flags &= ~READABLE
  383. if self.flags == 0:
  384. self.close()
  385. raise oefmt(space.w_IOError, "bad message length")
  386. newbuf = lltype.malloc(rffi.CCHARP.TO, length + 1, flavor='raw')
  387. for i in range(read_ptr[0]):
  388. newbuf[i] = self.buffer[i]
  389. result = _ReadFile(self.handle,
  390. rffi.ptradd(newbuf, read_ptr[0]), left_ptr[0],
  391. read_ptr, rffi.NULL)
  392. if not result:
  393. rffi.free_charp(newbuf)
  394. raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
  395. assert read_ptr[0] == left_ptr[0]
  396. return length, newbuf
  397. finally:
  398. lltype.free(read_ptr, flavor='raw')
  399. lltype.free(left_ptr, flavor='raw')
  400. def do_poll(self, space, timeout):
  401. from pypy.module._multiprocessing.interp_win32 import (
  402. _PeekNamedPipe, _GetTickCount, _Sleep)
  403. from rpython.rlib import rwin32
  404. from pypy.interpreter.error import wrap_windowserror
  405. bytes_ptr = lltype.malloc(rffi.CArrayPtr(rwin32.DWORD).TO, 1,
  406. flavor='raw')
  407. try:
  408. if not _PeekNamedPipe(self.handle, rffi.NULL, 0,
  409. lltype.nullptr(rwin32.LPDWORD.TO),
  410. bytes_ptr,
  411. lltype.nullptr(rwin32.LPDWORD.TO)):
  412. raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
  413. bytes = bytes_ptr[0]
  414. finally:
  415. lltype.free(bytes_ptr, flavor='raw')
  416. if timeout == 0.0:
  417. return bytes > 0
  418. block = timeout < 0
  419. if not block:
  420. # XXX does not check for overflow
  421. deadline = intmask(_GetTickCount()) + int(1000 * timeout + 0.5)
  422. else:
  423. deadline = 0
  424. _Sleep(0)
  425. delay = 1
  426. while True:
  427. bytes_ptr = lltype.malloc(rffi.CArrayPtr(rwin32.DWORD).TO, 1,
  428. flavor='raw')
  429. try:
  430. if not _PeekNamedPipe(self.handle, rffi.NULL, 0,
  431. lltype.nullptr(rwin32.LPDWORD.TO),
  432. bytes_ptr,
  433. lltype.nullptr(rwin32.LPDWORD.TO)):
  434. raise wrap_windowserror(space,
  435. rwin32.lastSavedWindowsError())
  436. bytes = bytes_ptr[0]
  437. finally:
  438. lltype.free(bytes_ptr, flavor='raw')
  439. if bytes > 0:
  440. return True
  441. if not block:
  442. now = intmask(_GetTickCount())
  443. if now > deadline:
  444. return False
  445. diff = deadline - now
  446. if delay > diff:
  447. delay = diff
  448. else:
  449. delay += 1
  450. if delay >= 20:
  451. delay = 20
  452. _Sleep(delay)
  453. # check for signals
  454. # PyErr_CheckSignals()
  455. if sys.platform == 'win32':
  456. W_PipeConnection.typedef = TypeDef(
  457. '_multiprocessing.PipeConnection', W_BaseConnection.typedef,
  458. __new__ = interp2app(W_PipeConnection.descr_new_pipe.im_func),
  459. fileno = interp2app(W_PipeConnection.fileno),
  460. )