eventloop.py 8.5 KB

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