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.
 
 
 
 

435 line
12 KiB

  1. @cython.no_gc_clear
  2. @cython.freelist(DEFAULT_FREELIST_SIZE)
  3. cdef class Handle:
  4. def __cinit__(self):
  5. self._cancelled = 0
  6. self.cb_type = 0
  7. self._source_traceback = None
  8. cdef inline _set_loop(self, Loop loop):
  9. self.loop = loop
  10. if UVLOOP_DEBUG:
  11. loop._debug_cb_handles_total += 1
  12. loop._debug_cb_handles_count += 1
  13. if loop._debug:
  14. self._source_traceback = extract_stack()
  15. cdef inline _set_context(self, object context):
  16. if context is None:
  17. context = Context_CopyCurrent()
  18. self.context = context
  19. def __dealloc__(self):
  20. if UVLOOP_DEBUG and self.loop is not None:
  21. self.loop._debug_cb_handles_count -= 1
  22. if self.loop is None:
  23. raise RuntimeError('Handle.loop is None in Handle.__dealloc__')
  24. def __init__(self):
  25. raise TypeError(
  26. '{} is not supposed to be instantiated from Python'.format(
  27. self.__class__.__name__))
  28. cdef inline _run(self):
  29. cdef:
  30. int cb_type
  31. object callback
  32. if self._cancelled:
  33. return
  34. cb_type = self.cb_type
  35. # Since _run is a cdef and there's no BoundMethod,
  36. # we guard 'self' manually (since the callback
  37. # might cause GC of the handle.)
  38. Py_INCREF(self)
  39. try:
  40. assert self.context is not None
  41. Context_Enter(self.context)
  42. if cb_type == 1:
  43. callback = self.arg1
  44. if callback is None:
  45. raise RuntimeError(
  46. 'cannot run Handle; callback is not set')
  47. args = self.arg2
  48. if args is None:
  49. callback()
  50. else:
  51. callback(*args)
  52. elif cb_type == 2:
  53. (<method_t>self.callback)(self.arg1)
  54. elif cb_type == 3:
  55. (<method1_t>self.callback)(self.arg1, self.arg2)
  56. elif cb_type == 4:
  57. (<method2_t>self.callback)(self.arg1, self.arg2, self.arg3)
  58. elif cb_type == 5:
  59. (<method3_t>self.callback)(
  60. self.arg1, self.arg2, self.arg3, self.arg4)
  61. else:
  62. raise RuntimeError('invalid Handle.cb_type: {}'.format(
  63. cb_type))
  64. except (KeyboardInterrupt, SystemExit):
  65. raise
  66. except BaseException as ex:
  67. if cb_type == 1:
  68. msg = 'Exception in callback {}'.format(callback)
  69. else:
  70. msg = 'Exception in callback {}'.format(self.meth_name)
  71. context = {
  72. 'message': msg,
  73. 'exception': ex,
  74. 'handle': self,
  75. }
  76. if self._source_traceback is not None:
  77. context['source_traceback'] = self._source_traceback
  78. self.loop.call_exception_handler(context)
  79. finally:
  80. context = self.context
  81. Py_DECREF(self)
  82. Context_Exit(context)
  83. cdef _cancel(self):
  84. self._cancelled = 1
  85. self.callback = NULL
  86. self.arg1 = self.arg2 = self.arg3 = self.arg4 = None
  87. cdef _format_handle(self):
  88. # Mirrors `asyncio.base_events._format_handle`.
  89. if self.cb_type == 1 and self.arg1 is not None:
  90. cb = self.arg1
  91. if isinstance(getattr(cb, '__self__', None), aio_Task):
  92. try:
  93. return repr(cb.__self__)
  94. except (AttributeError, TypeError, ValueError) as ex:
  95. # Cython generates empty __code__ objects for coroutines
  96. # that can crash asyncio.Task.__repr__ with an
  97. # AttributeError etc. Guard against that.
  98. self.loop.call_exception_handler({
  99. 'message': 'exception in Task.__repr__',
  100. 'task': cb.__self__,
  101. 'exception': ex,
  102. 'handle': self,
  103. })
  104. return repr(self)
  105. # Public API
  106. def __repr__(self):
  107. info = [self.__class__.__name__]
  108. if self._cancelled:
  109. info.append('cancelled')
  110. if self.cb_type == 1 and self.arg1 is not None:
  111. func = self.arg1
  112. # Cython can unset func.__qualname__/__name__, hence the checks.
  113. if hasattr(func, '__qualname__') and func.__qualname__:
  114. cb_name = func.__qualname__
  115. elif hasattr(func, '__name__') and func.__name__:
  116. cb_name = func.__name__
  117. else:
  118. cb_name = repr(func)
  119. info.append(cb_name)
  120. elif self.meth_name is not None:
  121. info.append(self.meth_name)
  122. if self._source_traceback is not None:
  123. frame = self._source_traceback[-1]
  124. info.append('created at {}:{}'.format(frame[0], frame[1]))
  125. return '<' + ' '.join(info) + '>'
  126. def cancel(self):
  127. self._cancel()
  128. def cancelled(self):
  129. return self._cancelled
  130. @cython.no_gc_clear
  131. @cython.freelist(DEFAULT_FREELIST_SIZE)
  132. cdef class TimerHandle:
  133. def __cinit__(self, Loop loop, object callback, object args,
  134. uint64_t delay, object context):
  135. self.loop = loop
  136. self.callback = callback
  137. self.args = args
  138. self._cancelled = 0
  139. if UVLOOP_DEBUG:
  140. self.loop._debug_cb_timer_handles_total += 1
  141. self.loop._debug_cb_timer_handles_count += 1
  142. if context is None:
  143. context = Context_CopyCurrent()
  144. self.context = context
  145. if loop._debug:
  146. self._debug_info = (
  147. format_callback_name(callback),
  148. extract_stack()
  149. )
  150. else:
  151. self._debug_info = None
  152. self.timer = UVTimer.new(
  153. loop, <method_t>self._run, self, delay)
  154. self.timer.start()
  155. self._when = self.timer.get_when() * 1e-3
  156. # Only add to loop._timers when `self.timer` is successfully created
  157. loop._timers.add(self)
  158. property _source_traceback:
  159. def __get__(self):
  160. if self._debug_info is not None:
  161. return self._debug_info[1]
  162. def __dealloc__(self):
  163. if UVLOOP_DEBUG:
  164. self.loop._debug_cb_timer_handles_count -= 1
  165. if self.timer is not None:
  166. raise RuntimeError('active TimerHandle is deallacating')
  167. cdef _cancel(self):
  168. if self._cancelled == 1:
  169. return
  170. self._cancelled = 1
  171. self._clear()
  172. cdef inline _clear(self):
  173. if self.timer is None:
  174. return
  175. self.callback = None
  176. self.args = None
  177. try:
  178. self.loop._timers.remove(self)
  179. finally:
  180. self.timer._close()
  181. self.timer = None # let the UVTimer handle GC
  182. cdef _run(self):
  183. if self._cancelled == 1:
  184. return
  185. if self.callback is None:
  186. raise RuntimeError('cannot run TimerHandle; callback is not set')
  187. callback = self.callback
  188. args = self.args
  189. # Since _run is a cdef and there's no BoundMethod,
  190. # we guard 'self' manually.
  191. Py_INCREF(self)
  192. if self.loop._debug:
  193. started = time_monotonic()
  194. try:
  195. assert self.context is not None
  196. Context_Enter(self.context)
  197. if args is not None:
  198. callback(*args)
  199. else:
  200. callback()
  201. except (KeyboardInterrupt, SystemExit):
  202. raise
  203. except BaseException as ex:
  204. context = {
  205. 'message': 'Exception in callback {}'.format(callback),
  206. 'exception': ex,
  207. 'handle': self,
  208. }
  209. if self._debug_info is not None:
  210. context['source_traceback'] = self._debug_info[1]
  211. self.loop.call_exception_handler(context)
  212. else:
  213. if self.loop._debug:
  214. delta = time_monotonic() - started
  215. if delta > self.loop.slow_callback_duration:
  216. aio_logger.warning(
  217. 'Executing %r took %.3f seconds',
  218. self, delta)
  219. finally:
  220. context = self.context
  221. Py_DECREF(self)
  222. Context_Exit(context)
  223. self._clear()
  224. # Public API
  225. def __repr__(self):
  226. info = [self.__class__.__name__]
  227. if self._cancelled:
  228. info.append('cancelled')
  229. if self._debug_info is not None:
  230. callback_name = self._debug_info[0]
  231. source_traceback = self._debug_info[1]
  232. else:
  233. callback_name = None
  234. source_traceback = None
  235. if callback_name is not None:
  236. info.append(callback_name)
  237. elif self.callback is not None:
  238. info.append(format_callback_name(self.callback))
  239. if source_traceback is not None:
  240. frame = source_traceback[-1]
  241. info.append('created at {}:{}'.format(frame[0], frame[1]))
  242. return '<' + ' '.join(info) + '>'
  243. def cancelled(self):
  244. return self._cancelled
  245. def cancel(self):
  246. self._cancel()
  247. def when(self):
  248. return self._when
  249. cdef format_callback_name(func):
  250. if hasattr(func, '__qualname__'):
  251. cb_name = getattr(func, '__qualname__')
  252. elif hasattr(func, '__name__'):
  253. cb_name = getattr(func, '__name__')
  254. else:
  255. cb_name = repr(func)
  256. return cb_name
  257. cdef new_Handle(Loop loop, object callback, object args, object context):
  258. cdef Handle handle
  259. handle = Handle.__new__(Handle)
  260. handle._set_loop(loop)
  261. handle._set_context(context)
  262. handle.cb_type = 1
  263. handle.arg1 = callback
  264. handle.arg2 = args
  265. return handle
  266. cdef new_MethodHandle(Loop loop, str name, method_t callback, object context,
  267. object bound_to):
  268. cdef Handle handle
  269. handle = Handle.__new__(Handle)
  270. handle._set_loop(loop)
  271. handle._set_context(context)
  272. handle.cb_type = 2
  273. handle.meth_name = name
  274. handle.callback = <void*> callback
  275. handle.arg1 = bound_to
  276. return handle
  277. cdef new_MethodHandle1(Loop loop, str name, method1_t callback, object context,
  278. object bound_to, object arg):
  279. cdef Handle handle
  280. handle = Handle.__new__(Handle)
  281. handle._set_loop(loop)
  282. handle._set_context(context)
  283. handle.cb_type = 3
  284. handle.meth_name = name
  285. handle.callback = <void*> callback
  286. handle.arg1 = bound_to
  287. handle.arg2 = arg
  288. return handle
  289. cdef new_MethodHandle2(Loop loop, str name, method2_t callback, object context,
  290. object bound_to, object arg1, object arg2):
  291. cdef Handle handle
  292. handle = Handle.__new__(Handle)
  293. handle._set_loop(loop)
  294. handle._set_context(context)
  295. handle.cb_type = 4
  296. handle.meth_name = name
  297. handle.callback = <void*> callback
  298. handle.arg1 = bound_to
  299. handle.arg2 = arg1
  300. handle.arg3 = arg2
  301. return handle
  302. cdef new_MethodHandle3(Loop loop, str name, method3_t callback, object context,
  303. object bound_to, object arg1, object arg2, object arg3):
  304. cdef Handle handle
  305. handle = Handle.__new__(Handle)
  306. handle._set_loop(loop)
  307. handle._set_context(context)
  308. handle.cb_type = 5
  309. handle.meth_name = name
  310. handle.callback = <void*> callback
  311. handle.arg1 = bound_to
  312. handle.arg2 = arg1
  313. handle.arg3 = arg2
  314. handle.arg4 = arg3
  315. return handle
  316. cdef extract_stack():
  317. """Replacement for traceback.extract_stack() that only does the
  318. necessary work for asyncio debug mode.
  319. """
  320. try:
  321. f = sys_getframe()
  322. # sys._getframe() might raise ValueError if being called without a frame, e.g.
  323. # from Cython or similar C extensions.
  324. except ValueError:
  325. return None
  326. if f is None:
  327. return
  328. try:
  329. stack = tb_StackSummary.extract(tb_walk_stack(f),
  330. limit=DEBUG_STACK_DEPTH,
  331. lookup_lines=False)
  332. finally:
  333. f = None
  334. stack.reverse()
  335. return stack