Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 

765 wiersze
25 KiB

  1. @cython.no_gc_clear
  2. cdef class UVProcess(UVHandle):
  3. """Abstract class; wrapper over uv_process_t handle."""
  4. def __cinit__(self):
  5. self.uv_opt_env = NULL
  6. self.uv_opt_args = NULL
  7. self._returncode = None
  8. self._pid = None
  9. self._fds_to_close = set()
  10. self._preexec_fn = None
  11. self._restore_signals = True
  12. cdef _close_process_handle(self):
  13. # XXX: This is a workaround for a libuv bug:
  14. # - https://github.com/libuv/libuv/issues/1933
  15. # - https://github.com/libuv/libuv/pull/551
  16. if self._handle is NULL:
  17. return
  18. self._handle.data = NULL
  19. uv.uv_close(self._handle, __uv_close_process_handle_cb)
  20. self._handle = NULL # close callback will free() the memory
  21. cdef _init(self, Loop loop, list args, dict env,
  22. cwd, start_new_session,
  23. _stdin, _stdout, _stderr, # std* can be defined as macros in C
  24. pass_fds, debug_flags, preexec_fn, restore_signals):
  25. global __forking
  26. global __forking_loop
  27. cdef int err
  28. self._start_init(loop)
  29. self._handle = <uv.uv_handle_t*>PyMem_RawMalloc(
  30. sizeof(uv.uv_process_t))
  31. if self._handle is NULL:
  32. self._abort_init()
  33. raise MemoryError()
  34. # Too early to call _finish_init, but still a lot of work to do.
  35. # Let's set handle.data to NULL, so in case something goes wrong,
  36. # callbacks have a chance to avoid casting *something* into UVHandle.
  37. self._handle.data = NULL
  38. try:
  39. self._init_options(args, env, cwd, start_new_session,
  40. _stdin, _stdout, _stderr)
  41. restore_inheritable = set()
  42. if pass_fds:
  43. for fd in pass_fds:
  44. if not os_get_inheritable(fd):
  45. restore_inheritable.add(fd)
  46. os_set_inheritable(fd, True)
  47. except Exception:
  48. self._abort_init()
  49. raise
  50. if __forking or loop.active_process_handler is not None:
  51. # Our pthread_atfork handlers won't work correctly when
  52. # another loop is forking in another thread (even though
  53. # GIL should help us to avoid that.)
  54. self._abort_init()
  55. raise RuntimeError(
  56. 'Racing with another loop to spawn a process.')
  57. self._errpipe_read, self._errpipe_write = os_pipe()
  58. try:
  59. os_set_inheritable(self._errpipe_write, True)
  60. self._preexec_fn = preexec_fn
  61. self._restore_signals = restore_signals
  62. loop.active_process_handler = self
  63. __forking = 1
  64. __forking_loop = loop
  65. PyOS_BeforeFork()
  66. err = uv.uv_spawn(loop.uvloop,
  67. <uv.uv_process_t*>self._handle,
  68. &self.options)
  69. __forking = 0
  70. __forking_loop = None
  71. loop.active_process_handler = None
  72. PyOS_AfterFork_Parent()
  73. if err < 0:
  74. self._close_process_handle()
  75. self._abort_init()
  76. raise convert_error(err)
  77. self._finish_init()
  78. os_close(self._errpipe_write)
  79. if preexec_fn is not None:
  80. errpipe_data = bytearray()
  81. while True:
  82. # XXX: This is a blocking code that has to be
  83. # rewritten (using loop.connect_read_pipe() or
  84. # otherwise.)
  85. part = os_read(self._errpipe_read, 50000)
  86. errpipe_data += part
  87. if not part or len(errpipe_data) > 50000:
  88. break
  89. finally:
  90. os_close(self._errpipe_read)
  91. try:
  92. os_close(self._errpipe_write)
  93. except OSError:
  94. # Might be already closed
  95. pass
  96. fds_to_close = self._fds_to_close
  97. self._fds_to_close = None
  98. for fd in fds_to_close:
  99. os_close(fd)
  100. for fd in restore_inheritable:
  101. os_set_inheritable(fd, False)
  102. # asyncio caches the PID in BaseSubprocessTransport,
  103. # so that the transport knows what the PID was even
  104. # after the process is finished.
  105. self._pid = (<uv.uv_process_t*>self._handle).pid
  106. # Track the process handle (create a strong ref to it)
  107. # to guarantee that __dealloc__ doesn't happen in an
  108. # uncontrolled fashion. We want to wait until the process
  109. # exits and libuv calls __uvprocess_on_exit_callback,
  110. # which will call `UVProcess._close()`, which will, in turn,
  111. # untrack this handle.
  112. self._loop._track_process(self)
  113. if debug_flags & __PROCESS_DEBUG_SLEEP_AFTER_FORK:
  114. time_sleep(1)
  115. if preexec_fn is not None and errpipe_data:
  116. # preexec_fn has raised an exception. The child
  117. # process must be dead now.
  118. try:
  119. exc_name, exc_msg = errpipe_data.split(b':', 1)
  120. exc_name = exc_name.decode()
  121. exc_msg = exc_msg.decode()
  122. except Exception:
  123. self._close()
  124. raise subprocess_SubprocessError(
  125. 'Bad exception data from child: {!r}'.format(
  126. errpipe_data))
  127. exc_cls = getattr(__builtins__, exc_name,
  128. subprocess_SubprocessError)
  129. exc = subprocess_SubprocessError(
  130. 'Exception occurred in preexec_fn.')
  131. exc.__cause__ = exc_cls(exc_msg)
  132. self._close()
  133. raise exc
  134. cdef _after_fork(self):
  135. # See CPython/_posixsubprocess.c for details
  136. cdef int err
  137. if self._restore_signals:
  138. _Py_RestoreSignals()
  139. PyOS_AfterFork_Child()
  140. err = uv.uv_loop_fork(self._loop.uvloop)
  141. if err < 0:
  142. raise convert_error(err)
  143. if self._preexec_fn is not None:
  144. try:
  145. gc_disable()
  146. self._preexec_fn()
  147. except BaseException as ex:
  148. try:
  149. with open(self._errpipe_write, 'wb') as f:
  150. f.write(str(ex.__class__.__name__).encode())
  151. f.write(b':')
  152. f.write(str(ex.args[0]).encode())
  153. finally:
  154. system._exit(255)
  155. else:
  156. os_close(self._errpipe_write)
  157. else:
  158. os_close(self._errpipe_write)
  159. cdef _close_after_spawn(self, int fd):
  160. if self._fds_to_close is None:
  161. raise RuntimeError(
  162. 'UVProcess._close_after_spawn called after uv_spawn')
  163. self._fds_to_close.add(fd)
  164. def __dealloc__(self):
  165. if self.uv_opt_env is not NULL:
  166. PyMem_RawFree(self.uv_opt_env)
  167. self.uv_opt_env = NULL
  168. if self.uv_opt_args is not NULL:
  169. PyMem_RawFree(self.uv_opt_args)
  170. self.uv_opt_args = NULL
  171. cdef char** __to_cstring_array(self, list arr):
  172. cdef:
  173. int i
  174. ssize_t arr_len = len(arr)
  175. bytes el
  176. char **ret
  177. if UVLOOP_DEBUG:
  178. assert arr_len > 0
  179. ret = <char **>PyMem_RawMalloc((arr_len + 1) * sizeof(char *))
  180. if ret is NULL:
  181. raise MemoryError()
  182. for i in range(arr_len):
  183. el = arr[i]
  184. # NB: PyBytes_AsString doesn't copy the data;
  185. # we have to be careful when the "arr" is GCed,
  186. # and it shouldn't be ever mutated.
  187. ret[i] = PyBytes_AsString(el)
  188. ret[arr_len] = NULL
  189. return ret
  190. cdef _init_options(self, list args, dict env, cwd, start_new_session,
  191. _stdin, _stdout, _stderr):
  192. memset(&self.options, 0, sizeof(uv.uv_process_options_t))
  193. self._init_env(env)
  194. self.options.env = self.uv_opt_env
  195. self._init_args(args)
  196. self.options.file = self.uv_opt_file
  197. self.options.args = self.uv_opt_args
  198. if start_new_session:
  199. self.options.flags |= uv.UV_PROCESS_DETACHED
  200. if cwd is not None:
  201. try:
  202. # Lookup __fspath__ manually, as os.fspath() isn't
  203. # available on Python 3.5.
  204. fspath = type(cwd).__fspath__
  205. except AttributeError:
  206. pass
  207. else:
  208. cwd = fspath(cwd)
  209. if isinstance(cwd, str):
  210. cwd = PyUnicode_EncodeFSDefault(cwd)
  211. if not isinstance(cwd, bytes):
  212. raise ValueError('cwd must be a str or bytes object')
  213. self.__cwd = cwd
  214. self.options.cwd = PyBytes_AsString(self.__cwd)
  215. self.options.exit_cb = &__uvprocess_on_exit_callback
  216. self._init_files(_stdin, _stdout, _stderr)
  217. cdef _init_args(self, list args):
  218. cdef:
  219. bytes path
  220. int an = len(args)
  221. if an < 1:
  222. raise ValueError('cannot spawn a process: args are empty')
  223. self.__args = args.copy()
  224. for i in range(an):
  225. arg = args[i]
  226. if isinstance(arg, str):
  227. self.__args[i] = PyUnicode_EncodeFSDefault(arg)
  228. elif not isinstance(arg, bytes):
  229. raise TypeError('all args must be str or bytes')
  230. path = self.__args[0]
  231. self.uv_opt_file = PyBytes_AsString(path)
  232. self.uv_opt_args = self.__to_cstring_array(self.__args)
  233. cdef _init_env(self, dict env):
  234. if env is not None and len(env):
  235. self.__env = list()
  236. for key in env:
  237. val = env[key]
  238. if isinstance(key, str):
  239. key = PyUnicode_EncodeFSDefault(key)
  240. elif not isinstance(key, bytes):
  241. raise TypeError(
  242. 'all environment vars must be bytes or str')
  243. if isinstance(val, str):
  244. val = PyUnicode_EncodeFSDefault(val)
  245. elif not isinstance(val, bytes):
  246. raise TypeError(
  247. 'all environment values must be bytes or str')
  248. self.__env.append(key + b'=' + val)
  249. self.uv_opt_env = self.__to_cstring_array(self.__env)
  250. else:
  251. self.__env = None
  252. cdef _init_files(self, _stdin, _stdout, _stderr):
  253. self.options.stdio_count = 0
  254. cdef _kill(self, int signum):
  255. cdef int err
  256. self._ensure_alive()
  257. err = uv.uv_process_kill(<uv.uv_process_t*>self._handle, signum)
  258. if err < 0:
  259. raise convert_error(err)
  260. cdef _on_exit(self, int64_t exit_status, int term_signal):
  261. if term_signal:
  262. # From Python docs:
  263. # A negative value -N indicates that the child was
  264. # terminated by signal N (POSIX only).
  265. self._returncode = -term_signal
  266. else:
  267. self._returncode = exit_status
  268. self._close()
  269. cdef _close(self):
  270. try:
  271. if self._loop is not None:
  272. self._loop._untrack_process(self)
  273. finally:
  274. UVHandle._close(self)
  275. DEF _CALL_PIPE_DATA_RECEIVED = 0
  276. DEF _CALL_PIPE_CONNECTION_LOST = 1
  277. DEF _CALL_PROCESS_EXITED = 2
  278. DEF _CALL_CONNECTION_LOST = 3
  279. @cython.no_gc_clear
  280. cdef class UVProcessTransport(UVProcess):
  281. def __cinit__(self):
  282. self._exit_waiters = []
  283. self._protocol = None
  284. self._init_futs = []
  285. self._pending_calls = []
  286. self._stdio_ready = 0
  287. self._stdin = self._stdout = self._stderr = None
  288. self.stdin_proto = self.stdout_proto = self.stderr_proto = None
  289. self._finished = 0
  290. cdef _on_exit(self, int64_t exit_status, int term_signal):
  291. UVProcess._on_exit(self, exit_status, term_signal)
  292. if self._stdio_ready:
  293. self._loop.call_soon(self._protocol.process_exited)
  294. else:
  295. self._pending_calls.append((_CALL_PROCESS_EXITED, None, None))
  296. self._try_finish()
  297. for waiter in self._exit_waiters:
  298. if not waiter.cancelled():
  299. waiter.set_result(self._returncode)
  300. self._exit_waiters.clear()
  301. self._close()
  302. cdef _check_proc(self):
  303. if not self._is_alive() or self._returncode is not None:
  304. raise ProcessLookupError()
  305. cdef _pipe_connection_lost(self, int fd, exc):
  306. if self._stdio_ready:
  307. self._loop.call_soon(self._protocol.pipe_connection_lost, fd, exc)
  308. self._try_finish()
  309. else:
  310. self._pending_calls.append((_CALL_PIPE_CONNECTION_LOST, fd, exc))
  311. cdef _pipe_data_received(self, int fd, data):
  312. if self._stdio_ready:
  313. self._loop.call_soon(self._protocol.pipe_data_received, fd, data)
  314. else:
  315. self._pending_calls.append((_CALL_PIPE_DATA_RECEIVED, fd, data))
  316. cdef _file_redirect_stdio(self, int fd):
  317. fd = os_dup(fd)
  318. os_set_inheritable(fd, True)
  319. self._close_after_spawn(fd)
  320. return fd
  321. cdef _file_devnull(self):
  322. dn = os_open(os_devnull, os_O_RDWR)
  323. os_set_inheritable(dn, True)
  324. self._close_after_spawn(dn)
  325. return dn
  326. cdef _file_outpipe(self):
  327. r, w = __socketpair()
  328. os_set_inheritable(w, True)
  329. self._close_after_spawn(w)
  330. return r, w
  331. cdef _file_inpipe(self):
  332. r, w = __socketpair()
  333. os_set_inheritable(r, True)
  334. self._close_after_spawn(r)
  335. return r, w
  336. cdef _init_files(self, _stdin, _stdout, _stderr):
  337. cdef uv.uv_stdio_container_t *iocnt
  338. UVProcess._init_files(self, _stdin, _stdout, _stderr)
  339. io = [None, None, None]
  340. self.options.stdio_count = 3
  341. self.options.stdio = self.iocnt
  342. if _stdin is not None:
  343. if _stdin == subprocess_PIPE:
  344. r, w = self._file_inpipe()
  345. io[0] = r
  346. self.stdin_proto = WriteSubprocessPipeProto(self, 0)
  347. waiter = self._loop._new_future()
  348. self._stdin = WriteUnixTransport.new(
  349. self._loop, self.stdin_proto, None, waiter)
  350. self._init_futs.append(waiter)
  351. self._stdin._open(w)
  352. self._stdin._init_protocol()
  353. elif _stdin == subprocess_DEVNULL:
  354. io[0] = self._file_devnull()
  355. elif _stdout == subprocess_STDOUT:
  356. raise ValueError(
  357. 'subprocess.STDOUT is supported only by stderr parameter')
  358. else:
  359. io[0] = self._file_redirect_stdio(_stdin)
  360. else:
  361. io[0] = self._file_redirect_stdio(sys.stdin.fileno())
  362. if _stdout is not None:
  363. if _stdout == subprocess_PIPE:
  364. # We can't use UV_CREATE_PIPE here, since 'stderr' might be
  365. # set to 'subprocess.STDOUT', and there is no way to
  366. # emulate that functionality with libuv high-level
  367. # streams API. Therefore, we create pipes for stdout and
  368. # stderr manually.
  369. r, w = self._file_outpipe()
  370. io[1] = w
  371. self.stdout_proto = ReadSubprocessPipeProto(self, 1)
  372. waiter = self._loop._new_future()
  373. self._stdout = ReadUnixTransport.new(
  374. self._loop, self.stdout_proto, None, waiter)
  375. self._init_futs.append(waiter)
  376. self._stdout._open(r)
  377. self._stdout._init_protocol()
  378. elif _stdout == subprocess_DEVNULL:
  379. io[1] = self._file_devnull()
  380. elif _stdout == subprocess_STDOUT:
  381. raise ValueError(
  382. 'subprocess.STDOUT is supported only by stderr parameter')
  383. else:
  384. io[1] = self._file_redirect_stdio(_stdout)
  385. else:
  386. io[1] = self._file_redirect_stdio(sys.stdout.fileno())
  387. if _stderr is not None:
  388. if _stderr == subprocess_PIPE:
  389. r, w = self._file_outpipe()
  390. io[2] = w
  391. self.stderr_proto = ReadSubprocessPipeProto(self, 2)
  392. waiter = self._loop._new_future()
  393. self._stderr = ReadUnixTransport.new(
  394. self._loop, self.stderr_proto, None, waiter)
  395. self._init_futs.append(waiter)
  396. self._stderr._open(r)
  397. self._stderr._init_protocol()
  398. elif _stderr == subprocess_STDOUT:
  399. if io[1] is None:
  400. # shouldn't ever happen
  401. raise RuntimeError('cannot apply subprocess.STDOUT')
  402. newfd = os_dup(io[1])
  403. os_set_inheritable(newfd, True)
  404. self._close_after_spawn(newfd)
  405. io[2] = newfd
  406. elif _stderr == subprocess_DEVNULL:
  407. io[2] = self._file_devnull()
  408. else:
  409. io[2] = self._file_redirect_stdio(_stderr)
  410. else:
  411. io[2] = self._file_redirect_stdio(sys.stderr.fileno())
  412. assert len(io) == 3
  413. for idx in range(3):
  414. iocnt = &self.iocnt[idx]
  415. if io[idx] is not None:
  416. iocnt.flags = uv.UV_INHERIT_FD
  417. iocnt.data.fd = io[idx]
  418. else:
  419. iocnt.flags = uv.UV_IGNORE
  420. cdef _call_connection_made(self, waiter):
  421. try:
  422. self._protocol.connection_made(self)
  423. except Exception as ex:
  424. if waiter is not None and not waiter.cancelled():
  425. waiter.set_exception(ex)
  426. else:
  427. raise
  428. else:
  429. if waiter is not None and not waiter.cancelled():
  430. waiter.set_result(True)
  431. self._stdio_ready = 1
  432. if self._pending_calls:
  433. pending_calls = self._pending_calls.copy()
  434. self._pending_calls.clear()
  435. for (type, fd, arg) in pending_calls:
  436. if type == _CALL_PIPE_CONNECTION_LOST:
  437. self._pipe_connection_lost(fd, arg)
  438. elif type == _CALL_PIPE_DATA_RECEIVED:
  439. self._pipe_data_received(fd, arg)
  440. elif type == _CALL_PROCESS_EXITED:
  441. self._loop.call_soon(self._protocol.process_exited)
  442. elif type == _CALL_CONNECTION_LOST:
  443. self._loop.call_soon(self._protocol.connection_lost, None)
  444. cdef _try_finish(self):
  445. if self._returncode is None or self._finished:
  446. return
  447. if ((self.stdin_proto is None or self.stdin_proto.disconnected) and
  448. (self.stdout_proto is None or
  449. self.stdout_proto.disconnected) and
  450. (self.stderr_proto is None or
  451. self.stderr_proto.disconnected)):
  452. self._finished = 1
  453. if self._stdio_ready:
  454. self._loop.call_soon(self._protocol.connection_lost, None)
  455. else:
  456. self._pending_calls.append((_CALL_CONNECTION_LOST, None, None))
  457. def __stdio_inited(self, waiter, stdio_fut):
  458. exc = stdio_fut.exception()
  459. if exc is not None:
  460. if waiter is None:
  461. raise exc
  462. else:
  463. waiter.set_exception(exc)
  464. else:
  465. self._loop._call_soon_handle(
  466. new_MethodHandle1(self._loop,
  467. "UVProcessTransport._call_connection_made",
  468. <method1_t>self._call_connection_made,
  469. self, waiter))
  470. @staticmethod
  471. cdef UVProcessTransport new(Loop loop, protocol, args, env,
  472. cwd, start_new_session,
  473. _stdin, _stdout, _stderr, pass_fds,
  474. waiter,
  475. debug_flags,
  476. preexec_fn,
  477. restore_signals):
  478. cdef UVProcessTransport handle
  479. handle = UVProcessTransport.__new__(UVProcessTransport)
  480. handle._protocol = protocol
  481. handle._init(loop, args, env, cwd, start_new_session,
  482. __process_convert_fileno(_stdin),
  483. __process_convert_fileno(_stdout),
  484. __process_convert_fileno(_stderr),
  485. pass_fds,
  486. debug_flags,
  487. preexec_fn,
  488. restore_signals)
  489. if handle._init_futs:
  490. handle._stdio_ready = 0
  491. init_fut = aio_gather(*handle._init_futs, loop=loop)
  492. init_fut.add_done_callback(
  493. ft_partial(handle.__stdio_inited, waiter))
  494. else:
  495. handle._stdio_ready = 1
  496. loop._call_soon_handle(
  497. new_MethodHandle1(loop,
  498. "UVProcessTransport._call_connection_made",
  499. <method1_t>handle._call_connection_made,
  500. handle, waiter))
  501. return handle
  502. def get_protocol(self):
  503. return self._protocol
  504. def set_protocol(self, protocol):
  505. self._protocol = protocol
  506. def get_pid(self):
  507. return self._pid
  508. def get_returncode(self):
  509. return self._returncode
  510. def get_pipe_transport(self, fd):
  511. if fd == 0:
  512. return self._stdin
  513. elif fd == 1:
  514. return self._stdout
  515. elif fd == 2:
  516. return self._stderr
  517. def terminate(self):
  518. self._check_proc()
  519. self._kill(uv.SIGTERM)
  520. def kill(self):
  521. self._check_proc()
  522. self._kill(uv.SIGKILL)
  523. def send_signal(self, int signal):
  524. self._check_proc()
  525. self._kill(signal)
  526. def is_closing(self):
  527. return self._closed
  528. def close(self):
  529. if self._returncode is None:
  530. self._kill(uv.SIGKILL)
  531. if self._stdin is not None:
  532. self._stdin.close()
  533. if self._stdout is not None:
  534. self._stdout.close()
  535. if self._stderr is not None:
  536. self._stderr.close()
  537. if self._returncode is not None:
  538. # The process is dead, just close the UV handle.
  539. #
  540. # (If "self._returncode is None", the process should have been
  541. # killed already and we're just waiting for a SIGCHLD; after
  542. # which the transport will be GC'ed and the uvhandle will be
  543. # closed in UVHandle.__dealloc__.)
  544. self._close()
  545. def get_extra_info(self, name, default=None):
  546. return default
  547. def _wait(self):
  548. fut = self._loop._new_future()
  549. if self._returncode is not None:
  550. fut.set_result(self._returncode)
  551. return fut
  552. self._exit_waiters.append(fut)
  553. return fut
  554. class WriteSubprocessPipeProto(aio_BaseProtocol):
  555. def __init__(self, proc, fd):
  556. if UVLOOP_DEBUG:
  557. if type(proc) is not UVProcessTransport:
  558. raise TypeError
  559. if not isinstance(fd, int):
  560. raise TypeError
  561. self.proc = proc
  562. self.fd = fd
  563. self.pipe = None
  564. self.disconnected = False
  565. def connection_made(self, transport):
  566. self.pipe = transport
  567. def __repr__(self):
  568. return ('<%s fd=%s pipe=%r>'
  569. % (self.__class__.__name__, self.fd, self.pipe))
  570. def connection_lost(self, exc):
  571. self.disconnected = True
  572. (<UVProcessTransport>self.proc)._pipe_connection_lost(self.fd, exc)
  573. self.proc = None
  574. def pause_writing(self):
  575. (<UVProcessTransport>self.proc)._protocol.pause_writing()
  576. def resume_writing(self):
  577. (<UVProcessTransport>self.proc)._protocol.resume_writing()
  578. class ReadSubprocessPipeProto(WriteSubprocessPipeProto,
  579. aio_Protocol):
  580. def data_received(self, data):
  581. (<UVProcessTransport>self.proc)._pipe_data_received(self.fd, data)
  582. cdef __process_convert_fileno(object obj):
  583. if obj is None or isinstance(obj, int):
  584. return obj
  585. fileno = obj.fileno()
  586. if not isinstance(fileno, int):
  587. raise TypeError(
  588. '{!r}.fileno() returned non-integer'.format(obj))
  589. return fileno
  590. cdef void __uvprocess_on_exit_callback(uv.uv_process_t *handle,
  591. int64_t exit_status,
  592. int term_signal) with gil:
  593. if __ensure_handle_data(<uv.uv_handle_t*>handle,
  594. "UVProcess exit callback") == 0:
  595. return
  596. cdef UVProcess proc = <UVProcess> handle.data
  597. try:
  598. proc._on_exit(exit_status, term_signal)
  599. except BaseException as ex:
  600. proc._error(ex, False)
  601. cdef __socketpair():
  602. cdef:
  603. int fds[2]
  604. int err
  605. err = system.socketpair(uv.AF_UNIX, uv.SOCK_STREAM, 0, fds)
  606. if err:
  607. exc = convert_error(-err)
  608. raise exc
  609. os_set_inheritable(fds[0], False)
  610. os_set_inheritable(fds[1], False)
  611. return fds[0], fds[1]
  612. cdef void __uv_close_process_handle_cb(uv.uv_handle_t* handle) with gil:
  613. PyMem_RawFree(handle)