cbhandles.pyx 12 KB

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