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.
 
 
 
 

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