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