You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

394 lines
13 KiB

  1. cdef class UVHandle:
  2. """A base class for all libuv handles.
  3. Automatically manages memory deallocation and closing.
  4. Important:
  5. 1. call "_ensure_alive()" before calling any libuv functions on
  6. your handles.
  7. 2. call "__ensure_handle_data" in *all* libuv handle callbacks.
  8. """
  9. def __cinit__(self):
  10. self._closed = 0
  11. self._inited = 0
  12. self._has_handle = 1
  13. self._handle = NULL
  14. self._loop = None
  15. self._source_traceback = None
  16. def __init__(self):
  17. raise TypeError(
  18. '{} is not supposed to be instantiated from Python'.format(
  19. self.__class__.__name__))
  20. def __dealloc__(self):
  21. if UVLOOP_DEBUG:
  22. if self._loop is not None:
  23. if self._inited:
  24. self._loop._debug_handles_current.subtract([
  25. self.__class__.__name__])
  26. else:
  27. # No "@cython.no_gc_clear" decorator on this UVHandle
  28. raise RuntimeError(
  29. '{} without @no_gc_clear; loop was set to None by GC'
  30. .format(self.__class__.__name__))
  31. if self._handle is NULL:
  32. return
  33. # -> When we're at this point, something is wrong <-
  34. if self._handle.loop is NULL:
  35. # The handle wasn't initialized with "uv_{handle}_init"
  36. self._closed = 1
  37. self._free()
  38. raise RuntimeError(
  39. '{} is open in __dealloc__ with loop set to NULL'
  40. .format(self.__class__.__name__))
  41. if self._closed:
  42. # So _handle is not NULL and self._closed == 1?
  43. raise RuntimeError(
  44. '{}.__dealloc__: _handle is NULL, _closed == 1'.format(
  45. self.__class__.__name__))
  46. # The handle is dealloced while open. Let's try to close it.
  47. # Situations when this is possible include unhandled exceptions,
  48. # errors during Handle.__cinit__/__init__ etc.
  49. if self._inited:
  50. self._handle.data = NULL
  51. uv.uv_close(self._handle, __uv_close_handle_cb) # void; no errors
  52. self._handle = NULL
  53. self._warn_unclosed()
  54. else:
  55. # The handle was allocated, but not initialized
  56. self._closed = 1
  57. self._free()
  58. cdef _free(self):
  59. if self._handle == NULL:
  60. return
  61. if UVLOOP_DEBUG and self._inited:
  62. self._loop._debug_uv_handles_freed += 1
  63. PyMem_RawFree(self._handle)
  64. self._handle = NULL
  65. cdef _warn_unclosed(self):
  66. if self._source_traceback is not None:
  67. try:
  68. tb = ''.join(tb_format_list(self._source_traceback))
  69. tb = 'object created at (most recent call last):\n{}'.format(
  70. tb.rstrip())
  71. except Exception as ex:
  72. msg = (
  73. 'unclosed resource {!r}; could not serialize '
  74. 'debug traceback: {}: {}'
  75. ).format(self, type(ex).__name__, ex)
  76. else:
  77. msg = 'unclosed resource {!r}; {}'.format(self, tb)
  78. else:
  79. msg = 'unclosed resource {!r}'.format(self)
  80. warnings_warn(msg, ResourceWarning)
  81. cdef inline _abort_init(self):
  82. if self._handle is not NULL:
  83. self._free()
  84. try:
  85. if UVLOOP_DEBUG:
  86. name = self.__class__.__name__
  87. if self._inited:
  88. raise RuntimeError(
  89. '_abort_init: {}._inited is set'.format(name))
  90. if self._closed:
  91. raise RuntimeError(
  92. '_abort_init: {}._closed is set'.format(name))
  93. finally:
  94. self._closed = 1
  95. cdef inline _finish_init(self):
  96. self._inited = 1
  97. if self._has_handle == 1:
  98. self._handle.data = <void*>self
  99. if self._loop._debug:
  100. self._source_traceback = extract_stack()
  101. if UVLOOP_DEBUG:
  102. cls_name = self.__class__.__name__
  103. self._loop._debug_uv_handles_total += 1
  104. self._loop._debug_handles_total.update([cls_name])
  105. self._loop._debug_handles_current.update([cls_name])
  106. cdef inline _start_init(self, Loop loop):
  107. if UVLOOP_DEBUG:
  108. if self._loop is not None:
  109. raise RuntimeError(
  110. '{}._start_init can only be called once'.format(
  111. self.__class__.__name__))
  112. self._loop = loop
  113. cdef inline bint _is_alive(self):
  114. cdef bint res
  115. res = self._closed != 1 and self._inited == 1
  116. if UVLOOP_DEBUG:
  117. if res and self._has_handle == 1:
  118. name = self.__class__.__name__
  119. if self._handle is NULL:
  120. raise RuntimeError(
  121. '{} is alive, but _handle is NULL'.format(name))
  122. if self._loop is None:
  123. raise RuntimeError(
  124. '{} is alive, but _loop is None'.format(name))
  125. if self._handle.loop is not self._loop.uvloop:
  126. raise RuntimeError(
  127. '{} is alive, but _handle.loop is not '
  128. 'initialized'.format(name))
  129. if self._handle.data is not <void*>self:
  130. raise RuntimeError(
  131. '{} is alive, but _handle.data is not '
  132. 'initialized'.format(name))
  133. return res
  134. cdef inline _ensure_alive(self):
  135. if not self._is_alive():
  136. raise RuntimeError(
  137. 'unable to perform operation on {!r}; '
  138. 'the handler is closed'.format(self))
  139. cdef _fatal_error(self, exc, throw, reason=None):
  140. # Fatal error means an error that was returned by the
  141. # underlying libuv handle function. We usually can't
  142. # recover from that, hence we just close the handle.
  143. self._close()
  144. if throw or self._loop is None:
  145. raise exc
  146. else:
  147. self._loop._handle_exception(exc)
  148. cdef _error(self, exc, throw):
  149. # A non-fatal error is usually an error that was caught
  150. # by the handler, but was originated in the client code
  151. # (not in libuv). In this case we either want to simply
  152. # raise or log it.
  153. if throw or self._loop is None:
  154. raise exc
  155. else:
  156. self._loop._handle_exception(exc)
  157. cdef _close(self):
  158. if self._closed == 1:
  159. return
  160. self._closed = 1
  161. if self._handle is NULL:
  162. return
  163. if UVLOOP_DEBUG:
  164. if self._handle.data is NULL:
  165. raise RuntimeError(
  166. '{}._close: _handle.data is NULL'.format(
  167. self.__class__.__name__))
  168. if <object>self._handle.data is not self:
  169. raise RuntimeError(
  170. '{}._close: _handle.data is not UVHandle/self'.format(
  171. self.__class__.__name__))
  172. if uv.uv_is_closing(self._handle):
  173. raise RuntimeError(
  174. '{}._close: uv_is_closing() is true'.format(
  175. self.__class__.__name__))
  176. # We want the handle wrapper (UVHandle) to stay alive until
  177. # the closing callback fires.
  178. Py_INCREF(self)
  179. uv.uv_close(self._handle, __uv_close_handle_cb) # void; no errors
  180. def __repr__(self):
  181. return '<{} closed={} {:#x}>'.format(
  182. self.__class__.__name__,
  183. self._closed,
  184. id(self))
  185. cdef class UVSocketHandle(UVHandle):
  186. def __cinit__(self):
  187. self._fileobj = None
  188. self.__cached_socket = None
  189. cdef _fileno(self):
  190. cdef:
  191. int fd
  192. int err
  193. self._ensure_alive()
  194. err = uv.uv_fileno(self._handle, <uv.uv_os_fd_t*>&fd)
  195. if err < 0:
  196. raise convert_error(err)
  197. return fd
  198. cdef _new_socket(self):
  199. raise NotImplementedError
  200. cdef inline _get_socket(self):
  201. if self.__cached_socket is not None:
  202. return self.__cached_socket
  203. if not self._is_alive():
  204. return None
  205. self.__cached_socket = self._new_socket()
  206. if UVLOOP_DEBUG:
  207. # We don't "dup" for the "__cached_socket".
  208. assert self.__cached_socket.fileno() == self._fileno()
  209. return self.__cached_socket
  210. cdef inline _attach_fileobj(self, object file):
  211. # When we create a TCP/PIPE/etc connection/server based on
  212. # a Python file object, we need to close the file object when
  213. # the uv handle is closed.
  214. socket_inc_io_ref(file)
  215. self._fileobj = file
  216. cdef _close(self):
  217. if self.__cached_socket is not None:
  218. (<PseudoSocket>self.__cached_socket)._fd = -1
  219. UVHandle._close(self)
  220. try:
  221. # This code will only run for transports created from
  222. # Python sockets, i.e. with `loop.create_server(sock=sock)` etc.
  223. if self._fileobj is not None:
  224. if isinstance(self._fileobj, socket_socket):
  225. # Detaching the socket object is the ideal solution:
  226. # * libuv will actually close the FD;
  227. # * detach() call will reset FD for the Python socket
  228. # object, which means that it won't be closed 2nd time
  229. # when the socket object is GCed.
  230. #
  231. # No need to call `socket_dec_io_ref()`, as
  232. # `socket.detach()` ignores `socket._io_refs`.
  233. self._fileobj.detach()
  234. else:
  235. try:
  236. # `socket.close()` will raise an EBADF because libuv
  237. # has already closed the underlying FD.
  238. self._fileobj.close()
  239. except OSError as ex:
  240. if ex.errno != errno_EBADF:
  241. raise
  242. except Exception as ex:
  243. self._loop.call_exception_handler({
  244. 'exception': ex,
  245. 'transport': self,
  246. 'message': f'could not close attached file object '
  247. f'{self._fileobj!r}',
  248. })
  249. finally:
  250. self._fileobj = None
  251. cdef _open(self, int sockfd):
  252. raise NotImplementedError
  253. cdef inline bint __ensure_handle_data(uv.uv_handle_t* handle,
  254. const char* handle_ctx):
  255. cdef Loop loop
  256. if UVLOOP_DEBUG:
  257. if handle.loop is NULL:
  258. raise RuntimeError(
  259. 'handle.loop is NULL in __ensure_handle_data')
  260. if handle.loop.data is NULL:
  261. raise RuntimeError(
  262. 'handle.loop.data is NULL in __ensure_handle_data')
  263. if handle.data is NULL:
  264. loop = <Loop>handle.loop.data
  265. loop.call_exception_handler({
  266. 'message': '{} called with handle.data == NULL'.format(
  267. handle_ctx.decode('latin-1'))
  268. })
  269. return 0
  270. if handle.data is NULL:
  271. # The underlying UVHandle object was GCed with an open uv_handle_t.
  272. loop = <Loop>handle.loop.data
  273. loop.call_exception_handler({
  274. 'message': '{} called after destroying the UVHandle'.format(
  275. handle_ctx.decode('latin-1'))
  276. })
  277. return 0
  278. return 1
  279. cdef void __uv_close_handle_cb(uv.uv_handle_t* handle) with gil:
  280. cdef UVHandle h
  281. if handle.data is NULL:
  282. # The original UVHandle is long dead. Just free the mem of
  283. # the uv_handle_t* handler.
  284. if UVLOOP_DEBUG:
  285. if handle.loop == NULL or handle.loop.data == NULL:
  286. raise RuntimeError(
  287. '__uv_close_handle_cb: handle.loop is invalid')
  288. (<Loop>handle.loop.data)._debug_uv_handles_freed += 1
  289. PyMem_RawFree(handle)
  290. else:
  291. h = <UVHandle>handle.data
  292. try:
  293. if UVLOOP_DEBUG:
  294. if not h._has_handle:
  295. raise RuntimeError(
  296. 'has_handle=0 in __uv_close_handle_cb')
  297. h._loop._debug_handles_closed.update([
  298. h.__class__.__name__])
  299. h._free()
  300. finally:
  301. Py_DECREF(h) # Was INCREFed in UVHandle._close
  302. cdef void __close_all_handles(Loop loop):
  303. uv.uv_walk(loop.uvloop,
  304. __uv_walk_close_all_handles_cb,
  305. <void*>loop) # void
  306. cdef void __uv_walk_close_all_handles_cb(
  307. uv.uv_handle_t* handle, void* arg) with gil:
  308. cdef:
  309. Loop loop = <Loop>arg
  310. UVHandle h
  311. if uv.uv_is_closing(handle):
  312. # The handle is closed or is closing.
  313. return
  314. if handle.data is NULL:
  315. # This shouldn't happen. Ever.
  316. loop.call_exception_handler({
  317. 'message': 'handle.data is NULL in __close_all_handles_cb'
  318. })
  319. return
  320. h = <UVHandle>handle.data
  321. if not h._closed:
  322. h._warn_unclosed()
  323. h._close()