eventloop.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import _thread
  2. import time
  3. _is_debug = False
  4. class EventTask:
  5. """
  6. Task Item of EventLoop
  7. """
  8. _func = None
  9. _callback = None
  10. _args = None
  11. _period_ms = None
  12. _is_periodic = False
  13. _last_call_time = 0
  14. _delay_ms = None
  15. def __init__(self, func, callback, args, period_ms, delay_ms):
  16. """
  17. :param func: function to be called
  18. :param callback: callback function
  19. :param args: arguments of func
  20. :param period_ms: period of periodic task
  21. """
  22. self._func = func
  23. self._callback = callback
  24. self._args = args
  25. self._period_ms = period_ms
  26. self._delay_ms = delay_ms
  27. if not delay_ms is None:
  28. if period_ms is None:
  29. period_ms = 0
  30. self._last_call_time = time.tick_ms() - period_ms + delay_ms
  31. _debug('last_call_time for delay:', self._last_call_time)
  32. if period_ms != None:
  33. self._is_periodic = True
  34. class EventLoop:
  35. """
  36. Event Loop
  37. """
  38. _tasks: dict[str, EventTask] = {}
  39. _period_ms = 100
  40. _thread_stack = 0
  41. _need_stop = False
  42. _started = False
  43. _uuid = 0
  44. def __init__(self, period_ms=100, thread_stack=0):
  45. """
  46. :param period_ms: period of loop
  47. :param thread_stack: stack size of thread
  48. """
  49. self._period_ms = period_ms
  50. self._thread_stack = thread_stack
  51. def _add_task(self, task_name, func, callback, args, period_ms, delay_ms):
  52. if task_name == None:
  53. self._uuid += 1
  54. task_name = str(self._uuid)
  55. _debug('add_task', task_name)
  56. _debug('func', func)
  57. _debug('callback', callback)
  58. _debug('args', args)
  59. _debug('period_ms', period_ms)
  60. _debug('delay_ms', delay_ms)
  61. new_task = EventTask(func, callback, args, period_ms, delay_ms)
  62. self._tasks[task_name] = new_task
  63. def start_new_task(self,
  64. func, args,
  65. is_periodic=True,
  66. period_ms=1000,
  67. callback=None,
  68. task_name=None,
  69. delay_ms=None
  70. ):
  71. """
  72. Add a task to EventLoop
  73. :param task_name: name of task
  74. :param func: function to be called
  75. :param period_ms: period of task
  76. :param callback: callback function
  77. :param args: arguments of func
  78. """
  79. if is_periodic:
  80. self._add_task(task_name, func, callback,
  81. args, period_ms, delay_ms)
  82. else:
  83. self._add_task(task_name, func, callback, args, None, delay_ms)
  84. def start_new_task_once(self,
  85. func, args,
  86. callback=None,
  87. task_name=None,
  88. delay_ms=None):
  89. """
  90. Add a task to EventLoop, run once
  91. :param task_name: name of task
  92. :param func: function to be called
  93. :param callback: callback function
  94. :param args: arguments of func
  95. """
  96. self.start_new_task(func, args, False, None,
  97. callback, task_name, delay_ms)
  98. def start_new_task_periodic(self, func, args, period_ms=1000, callback=None, task_name=None, delay_ms=None):
  99. """
  100. Add a task to EventLoop, run periodically
  101. :param task_name: name of task
  102. :param func: function to be called
  103. :param period_ms: period of task
  104. :param callback: callback function
  105. :param args: arguments of func
  106. """
  107. self.start_new_task(func, args, True, period_ms,
  108. callback, task_name, delay_ms)
  109. def remove_task(self, task_name):
  110. """
  111. Remove a task from EventLoop
  112. :param task_name: name of task
  113. """
  114. self._tasks.remove(task_name)
  115. def _run_task(self, task: EventTask):
  116. _res = task._func(*task._args)
  117. if task._callback != None:
  118. task._callback(_res)
  119. def _run_thread(self):
  120. while not self._need_stop:
  121. tick = time.tick_ms()
  122. for task_name, task in self._tasks.items():
  123. if tick - task._last_call_time > task._period_ms:
  124. _debug('run_task', task_name)
  125. _debug('tick', tick)
  126. _debug('last_call_time', task._last_call_time)
  127. self._run_task(task)
  128. task._last_call_time = tick
  129. if not task._is_periodic:
  130. self.remove_task(task_name)
  131. if self._need_stop:
  132. break
  133. time.sleep_ms(self._period_ms)
  134. self._started = False
  135. def start(self):
  136. """
  137. Start EventLoop
  138. """
  139. if self._started:
  140. print('EventLoop already started')
  141. return
  142. self._need_stop = False
  143. last_stack_size = _thread.stack_size(self._thread_stack)
  144. _thread.start_new_thread(self._run_thread, ())
  145. last_stack_size = _thread.stack_size(last_stack_size)
  146. self._started = True
  147. def stop(self):
  148. """
  149. Stop EventLoop
  150. """
  151. if not self._started:
  152. print('EventLoop not started')
  153. return
  154. self._need_stop = True
  155. while self._started:
  156. time.sleep(0.1)
  157. time.sleep(1)
  158. def set_debug(enable: bool):
  159. global _is_debug
  160. _is_debug = enable
  161. def _debug(*args):
  162. if _is_debug:
  163. print('\x1b[33m[eventloop]', *args, "\x1b[0m")
  164. g_default_event_loop: EventLoop = None
  165. def _get_default_event_loop():
  166. global g_default_event_loop
  167. if g_default_event_loop == None:
  168. g_default_event_loop = EventLoop()
  169. g_default_event_loop.start()
  170. return g_default_event_loop
  171. def start_new_task(func, args,
  172. is_periodic=True,
  173. period_ms=1000,
  174. callback=None,
  175. task_name=None,
  176. delay_ms=None
  177. ):
  178. """
  179. Add a task to EventLoop
  180. :param task_name: name of task
  181. :param func: function to be called
  182. :param period_ms: period of task
  183. :param callback: callback function
  184. :param args: arguments of func
  185. """
  186. eventloop = _get_default_event_loop()
  187. eventloop.start_new_task(func, args, is_periodic,
  188. period_ms, callback, task_name, delay_ms)
  189. def start_new_task_once(func, args,
  190. callback=None,
  191. task_name=None,
  192. delay_ms=None
  193. ):
  194. """
  195. Add a task to EventLoop, run once
  196. :param task_name: name of task
  197. :param func: function to be called
  198. :param callback: callback function
  199. :param args: arguments of func
  200. """
  201. eventloop = _get_default_event_loop()
  202. eventloop.start_new_task_once(func, args, callback, task_name, delay_ms)
  203. def start_new_task_periodic(func, args,
  204. period_ms=1000,
  205. callback=None,
  206. task_name=None,
  207. delay_ms=None
  208. ):
  209. """
  210. Add a task to EventLoop, run periodically
  211. :param task_name: name of task
  212. :param func: function to be called
  213. :param period_ms: period of task
  214. :param callback: callback function
  215. :param args: arguments of func
  216. """
  217. eventloop = _get_default_event_loop()
  218. eventloop.start_new_task_periodic(
  219. func, args, period_ms, callback, task_name, delay_ms)
  220. def remove_task(task_name):
  221. """
  222. Remove a task from EventLoop
  223. :param task_name: name of task
  224. """
  225. eventloop = _get_default_event_loop()
  226. eventloop.remove_task(task_name)
  227. def stop():
  228. """
  229. Stop EventLoop
  230. """
  231. if g_default_event_loop == None:
  232. return
  233. _debug('stop default eventloop')
  234. eventloop = _get_default_event_loop()
  235. eventloop.stop()
  236. def start():
  237. """
  238. Start EventLoop
  239. """
  240. eventloop = _get_default_event_loop()
  241. eventloop.start()
  242. def __del__():
  243. stop()