| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # File descriptor and timer event helper | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2025-09-03 13:45:13 -04:00
										 |  |  | # Copyright (C) 2016-2025  Kevin O'Connor <kevin@koconnor.net> | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # | 
					
						
							|  |  |  | # This file may be distributed under the terms of the GNU GPLv3 license. | 
					
						
							| 
									
										
										
										
											2021-10-01 19:30:48 -04:00
										 |  |  | import os, gc, select, math, time, logging, queue | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  | import greenlet | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  | import chelper, util | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  | _NOW = 0. | 
					
						
							|  |  |  | _NEVER = 9999999999999999. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | class ReactorTimer: | 
					
						
							|  |  |  |     def __init__(self, callback, waketime): | 
					
						
							|  |  |  |         self.callback = callback | 
					
						
							|  |  |  |         self.waketime = waketime | 
					
						
							| 
									
										
										
										
											2025-09-03 13:45:13 -04:00
										 |  |  |         self.timer_is_running = False | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  | class ReactorCompletion: | 
					
						
							|  |  |  |     class sentinel: pass | 
					
						
							|  |  |  |     def __init__(self, reactor): | 
					
						
							|  |  |  |         self.reactor = reactor | 
					
						
							|  |  |  |         self.result = self.sentinel | 
					
						
							| 
									
										
										
										
											2020-02-19 10:49:56 -05:00
										 |  |  |         self.waiting = [] | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |     def test(self): | 
					
						
							|  |  |  |         return self.result is not self.sentinel | 
					
						
							|  |  |  |     def complete(self, result): | 
					
						
							|  |  |  |         self.result = result | 
					
						
							| 
									
										
										
										
											2020-02-19 10:49:56 -05:00
										 |  |  |         for wait in self.waiting: | 
					
						
							|  |  |  |             self.reactor.update_timer(wait.timer, self.reactor.NOW) | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |     def wait(self, waketime=_NEVER, waketime_result=None): | 
					
						
							|  |  |  |         if self.result is self.sentinel: | 
					
						
							| 
									
										
										
										
											2020-02-19 10:49:56 -05:00
										 |  |  |             wait = greenlet.getcurrent() | 
					
						
							|  |  |  |             self.waiting.append(wait) | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |             self.reactor.pause(waketime) | 
					
						
							| 
									
										
										
										
											2020-02-19 10:49:56 -05:00
										 |  |  |             self.waiting.remove(wait) | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |             if self.result is self.sentinel: | 
					
						
							|  |  |  |                 return waketime_result | 
					
						
							|  |  |  |         return self.result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  | class ReactorCallback: | 
					
						
							| 
									
										
										
										
											2018-12-20 20:56:41 +02:00
										 |  |  |     def __init__(self, reactor, callback, waketime): | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         self.reactor = reactor | 
					
						
							| 
									
										
										
										
											2018-12-20 20:56:41 +02:00
										 |  |  |         self.timer = reactor.register_timer(self.invoke, waketime) | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         self.callback = callback | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |         self.completion = ReactorCompletion(reactor) | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |     def invoke(self, eventtime): | 
					
						
							|  |  |  |         self.reactor.unregister_timer(self.timer) | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |         res = self.callback(eventtime) | 
					
						
							|  |  |  |         self.completion.complete(res) | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         return self.reactor.NEVER | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | class ReactorFileHandler: | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |     def __init__(self, fd, read_callback, write_callback): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.fd = fd | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |         self.read_callback = read_callback | 
					
						
							|  |  |  |         self.write_callback = write_callback | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  | class ReactorGreenlet(greenlet.greenlet): | 
					
						
							|  |  |  |     def __init__(self, run): | 
					
						
							|  |  |  |         greenlet.greenlet.__init__(self, run=run) | 
					
						
							|  |  |  |         self.timer = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-07 22:41:58 -04:00
										 |  |  | class ReactorMutex: | 
					
						
							|  |  |  |     def __init__(self, reactor, is_locked): | 
					
						
							|  |  |  |         self.reactor = reactor | 
					
						
							|  |  |  |         self.is_locked = is_locked | 
					
						
							|  |  |  |         self.next_pending = False | 
					
						
							|  |  |  |         self.queue = [] | 
					
						
							|  |  |  |         self.lock = self.__enter__ | 
					
						
							|  |  |  |         self.unlock = self.__exit__ | 
					
						
							|  |  |  |     def test(self): | 
					
						
							|  |  |  |         return self.is_locked | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         if not self.is_locked: | 
					
						
							|  |  |  |             self.is_locked = True | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         g = greenlet.getcurrent() | 
					
						
							|  |  |  |         self.queue.append(g) | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             self.reactor.pause(self.reactor.NEVER) | 
					
						
							|  |  |  |             if self.next_pending and self.queue[0] is g: | 
					
						
							|  |  |  |                 self.next_pending = False | 
					
						
							|  |  |  |                 self.queue.pop(0) | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |     def __exit__(self, type=None, value=None, tb=None): | 
					
						
							|  |  |  |         if not self.queue: | 
					
						
							|  |  |  |             self.is_locked = False | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.next_pending = True | 
					
						
							|  |  |  |         self.reactor.update_timer(self.queue[0].timer, self.reactor.NOW) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | class SelectReactor: | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |     NOW = _NOW | 
					
						
							|  |  |  |     NEVER = _NEVER | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |     def __init__(self, gc_checking=False): | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         # Main code | 
					
						
							|  |  |  |         self._process = False | 
					
						
							|  |  |  |         self.monotonic = chelper.get_ffi()[1].get_monotonic | 
					
						
							| 
									
										
										
										
											2020-09-17 01:59:18 -04:00
										 |  |  |         # Python garbage collection | 
					
						
							|  |  |  |         self._check_gc = gc_checking | 
					
						
							|  |  |  |         self._last_gc_times = [0., 0., 0.] | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         # Timers | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self._timers = [] | 
					
						
							|  |  |  |         self._next_timer = self.NEVER | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         # Callbacks | 
					
						
							|  |  |  |         self._pipe_fds = None | 
					
						
							| 
									
										
										
										
											2020-06-12 10:11:57 -04:00
										 |  |  |         self._async_queue = queue.Queue() | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         # File descriptors | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._dummy_fd_hdl = ReactorFileHandler(-1, (lambda e: None), | 
					
						
							|  |  |  |                                                 (lambda e: None)) | 
					
						
							|  |  |  |         self._fds = {} | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |         self._read_fds = [] | 
					
						
							|  |  |  |         self._write_fds = [] | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._READ = 1 | 
					
						
							|  |  |  |         self._WRITE = 2 | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         # Greenlets | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         self._g_dispatch = None | 
					
						
							|  |  |  |         self._greenlets = [] | 
					
						
							| 
									
										
										
										
											2020-09-16 21:40:01 -04:00
										 |  |  |         self._all_greenlets = [] | 
					
						
							| 
									
										
										
										
											2020-09-17 01:59:18 -04:00
										 |  |  |     def get_gc_stats(self): | 
					
						
							|  |  |  |         return tuple(self._last_gc_times) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Timers | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |     def update_timer(self, timer_handler, waketime): | 
					
						
							| 
									
										
										
										
											2025-09-03 13:45:13 -04:00
										 |  |  |         if timer_handler.timer_is_running: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |         timer_handler.waketime = waketime | 
					
						
							|  |  |  |         self._next_timer = min(self._next_timer, waketime) | 
					
						
							|  |  |  |     def register_timer(self, callback, waketime=NEVER): | 
					
						
							| 
									
										
										
										
											2019-06-09 14:38:01 -04:00
										 |  |  |         timer_handler = ReactorTimer(callback, waketime) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         timers = list(self._timers) | 
					
						
							| 
									
										
										
										
											2019-06-09 14:38:01 -04:00
										 |  |  |         timers.append(timer_handler) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self._timers = timers | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |         self._next_timer = min(self._next_timer, waketime) | 
					
						
							| 
									
										
										
										
											2019-06-09 14:38:01 -04:00
										 |  |  |         return timer_handler | 
					
						
							|  |  |  |     def unregister_timer(self, timer_handler): | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |         timer_handler.waketime = self.NEVER | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         timers = list(self._timers) | 
					
						
							| 
									
										
										
										
											2019-06-09 14:38:01 -04:00
										 |  |  |         timers.pop(timers.index(timer_handler)) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self._timers = timers | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |     def _check_timers(self, eventtime, busy): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         if eventtime < self._next_timer: | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |             if busy: | 
					
						
							|  |  |  |                 return 0. | 
					
						
							|  |  |  |             if self._check_gc: | 
					
						
							|  |  |  |                 gi = gc.get_count() | 
					
						
							|  |  |  |                 if gi[0] >= 700: | 
					
						
							|  |  |  |                     # Reactor looks idle and gc is due - run it | 
					
						
							| 
									
										
										
										
											2020-09-17 01:59:18 -04:00
										 |  |  |                     gc_level = 0 | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |                     if gi[1] >= 10: | 
					
						
							| 
									
										
										
										
											2020-09-17 01:59:18 -04:00
										 |  |  |                         gc_level = 1 | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |                         if gi[2] >= 10: | 
					
						
							| 
									
										
										
										
											2020-09-17 01:59:18 -04:00
										 |  |  |                             gc_level = 2 | 
					
						
							|  |  |  |                     self._last_gc_times[gc_level] = eventtime | 
					
						
							|  |  |  |                     gc.collect(gc_level) | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |                     return 0. | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             return min(1., max(.001, self._next_timer - eventtime)) | 
					
						
							|  |  |  |         self._next_timer = self.NEVER | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         g_dispatch = self._g_dispatch | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         for t in self._timers: | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |             waketime = t.waketime | 
					
						
							|  |  |  |             if eventtime >= waketime: | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |                 t.waketime = self.NEVER | 
					
						
							| 
									
										
										
										
											2025-09-03 13:45:13 -04:00
										 |  |  |                 t.timer_is_running = True | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |                 t.waketime = waketime = t.callback(eventtime) | 
					
						
							| 
									
										
										
										
											2025-09-03 13:45:13 -04:00
										 |  |  |                 t.timer_is_running = False | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |                 if g_dispatch is not self._g_dispatch: | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |                     self._next_timer = min(self._next_timer, waketime) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |                     self._end_greenlet(g_dispatch) | 
					
						
							|  |  |  |                     return 0. | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |             self._next_timer = min(self._next_timer, waketime) | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |         return 0. | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |     # Callbacks and Completions | 
					
						
							|  |  |  |     def completion(self): | 
					
						
							|  |  |  |         return ReactorCompletion(self) | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |     def register_callback(self, callback, waketime=NOW): | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |         rcb = ReactorCallback(self, callback, waketime) | 
					
						
							|  |  |  |         return rcb.completion | 
					
						
							|  |  |  |     # Asynchronous (from another thread) callbacks and completions | 
					
						
							|  |  |  |     def register_async_callback(self, callback, waketime=NOW): | 
					
						
							|  |  |  |         self._async_queue.put_nowait( | 
					
						
							|  |  |  |             (ReactorCallback, (self, callback, waketime))) | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2021-10-01 19:10:11 -04:00
										 |  |  |             os.write(self._pipe_fds[1], b'.') | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |         except os.error: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |     def async_complete(self, completion, result): | 
					
						
							|  |  |  |         self._async_queue.put_nowait((completion.complete, (result,))) | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2021-10-01 19:10:11 -04:00
										 |  |  |             os.write(self._pipe_fds[1], b'.') | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         except os.error: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |     def _got_pipe_signal(self, eventtime): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             os.read(self._pipe_fds[0], 4096) | 
					
						
							|  |  |  |         except os.error: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |                 func, args = self._async_queue.get_nowait() | 
					
						
							| 
									
										
										
										
											2020-06-12 10:11:57 -04:00
										 |  |  |             except queue.Empty: | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |                 break | 
					
						
							| 
									
										
										
										
											2019-06-26 14:05:10 -04:00
										 |  |  |             func(*args) | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |     def _setup_async_callbacks(self): | 
					
						
							|  |  |  |         self._pipe_fds = os.pipe() | 
					
						
							|  |  |  |         util.set_nonblock(self._pipe_fds[0]) | 
					
						
							|  |  |  |         util.set_nonblock(self._pipe_fds[1]) | 
					
						
							|  |  |  |         self.register_fd(self._pipe_fds[0], self._got_pipe_signal) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |     # Greenlets | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  |     def _sys_pause(self, waketime): | 
					
						
							|  |  |  |         # Pause using system sleep for when reactor not running | 
					
						
							|  |  |  |         delay = waketime - self.monotonic() | 
					
						
							|  |  |  |         if delay > 0.: | 
					
						
							|  |  |  |             time.sleep(delay) | 
					
						
							|  |  |  |         return self.monotonic() | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |     def pause(self, waketime): | 
					
						
							|  |  |  |         g = greenlet.getcurrent() | 
					
						
							|  |  |  |         if g is not self._g_dispatch: | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  |             if self._g_dispatch is None: | 
					
						
							|  |  |  |                 return self._sys_pause(waketime) | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |             # Switch to _check_timers (via g.timer.callback return) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |             return self._g_dispatch.switch(waketime) | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |         # Pausing the dispatch greenlet - prepare a new greenlet to do dispatch | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         if self._greenlets: | 
					
						
							|  |  |  |             g_next = self._greenlets.pop() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             g_next = ReactorGreenlet(run=self._dispatch_loop) | 
					
						
							| 
									
										
										
										
											2020-09-16 21:40:01 -04:00
										 |  |  |             self._all_greenlets.append(g_next) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         g_next.parent = g.parent | 
					
						
							|  |  |  |         g.timer = self.register_timer(g.switch, waketime) | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |         self._next_timer = self.NOW | 
					
						
							|  |  |  |         # Switch to _dispatch_loop (via _end_greenlet or direct) | 
					
						
							|  |  |  |         eventtime = g_next.switch() | 
					
						
							|  |  |  |         # This greenlet activated from g.timer.callback (via _check_timers) | 
					
						
							|  |  |  |         return eventtime | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |     def _end_greenlet(self, g_old): | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         # Cache this greenlet for later use | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         self._greenlets.append(g_old) | 
					
						
							|  |  |  |         self.unregister_timer(g_old.timer) | 
					
						
							|  |  |  |         g_old.timer = None | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |         # Switch to _check_timers (via g_old.timer.callback return) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         self._g_dispatch.switch(self.NEVER) | 
					
						
							| 
									
										
										
										
											2019-06-19 09:32:40 -04:00
										 |  |  |         # This greenlet reactivated from pause() - return to main dispatch loop | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         self._g_dispatch = g_old | 
					
						
							| 
									
										
										
										
											2019-06-07 22:41:58 -04:00
										 |  |  |     # Mutexes | 
					
						
							|  |  |  |     def mutex(self, is_locked=False): | 
					
						
							|  |  |  |         return ReactorMutex(self, is_locked) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # File descriptors | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |     def register_fd(self, fd, read_callback, write_callback=None): | 
					
						
							|  |  |  |         file_handler = ReactorFileHandler(fd, read_callback, write_callback) | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._fds[fd] = file_handler | 
					
						
							| 
									
										
										
										
											2023-03-15 02:03:47 +01:00
										 |  |  |         self.set_fd_wake(file_handler, True, False) | 
					
						
							| 
									
										
										
										
											2019-06-09 14:38:01 -04:00
										 |  |  |         return file_handler | 
					
						
							|  |  |  |     def unregister_fd(self, file_handler): | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self.set_fd_wake(file_handler, False, False) | 
					
						
							|  |  |  |         del self._fds[file_handler.fd] | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |     def set_fd_wake(self, file_handler, is_readable=True, is_writeable=False): | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         fd = file_handler.fd | 
					
						
							|  |  |  |         if fd in self._read_fds: | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |             if not is_readable: | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |                 self._read_fds.remove(fd) | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |         elif is_readable: | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |             self._read_fds.append(fd) | 
					
						
							|  |  |  |         if fd in self._write_fds: | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |             if not is_writeable: | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |                 self._write_fds.remove(fd) | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |         elif is_writeable: | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |             self._write_fds.append(fd) | 
					
						
							|  |  |  |     def _check_fds(self, eventtime, hdls): | 
					
						
							|  |  |  |         g_dispatch = self._g_dispatch | 
					
						
							|  |  |  |         for fd, event in hdls: | 
					
						
							|  |  |  |             hdl = self._fds.get(fd, self._dummy_fd_hdl) | 
					
						
							|  |  |  |             if event & self._READ: | 
					
						
							|  |  |  |                 hdl.read_callback(eventtime) | 
					
						
							|  |  |  |                 if g_dispatch is not self._g_dispatch: | 
					
						
							|  |  |  |                     self._end_greenlet(g_dispatch) | 
					
						
							|  |  |  |                     return self.monotonic() | 
					
						
							|  |  |  |             if event & self._WRITE: | 
					
						
							|  |  |  |                 hdl.write_callback(eventtime) | 
					
						
							|  |  |  |                 if g_dispatch is not self._g_dispatch: | 
					
						
							|  |  |  |                     self._end_greenlet(g_dispatch) | 
					
						
							|  |  |  |                     return self.monotonic() | 
					
						
							|  |  |  |         return eventtime | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Main loop | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |     def _dispatch_loop(self): | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._g_dispatch = greenlet.getcurrent() | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |         busy = True | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |         eventtime = self.monotonic() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         while self._process: | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |             timeout = self._check_timers(eventtime, busy) | 
					
						
							|  |  |  |             busy = False | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |             res = select.select(self._read_fds, self._write_fds, [], timeout) | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |             eventtime = self.monotonic() | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |             if res[0] or res[1]: | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |                 busy = True | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |                 hdls = ([(fd, self._READ) for fd in res[0]] | 
					
						
							|  |  |  |                         + [(fd, self._WRITE) for fd in res[1]]) | 
					
						
							|  |  |  |                 eventtime = self._check_fds(eventtime, hdls) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         self._g_dispatch = None | 
					
						
							|  |  |  |     def run(self): | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:19 -04:00
										 |  |  |         if self._pipe_fds is None: | 
					
						
							|  |  |  |             self._setup_async_callbacks() | 
					
						
							| 
									
										
										
										
											2017-04-13 13:12:46 -04:00
										 |  |  |         self._process = True | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         g_next = ReactorGreenlet(run=self._dispatch_loop) | 
					
						
							| 
									
										
										
										
											2020-09-16 21:40:01 -04:00
										 |  |  |         self._all_greenlets.append(g_next) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         g_next.switch() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def end(self): | 
					
						
							|  |  |  |         self._process = False | 
					
						
							| 
									
										
										
										
											2020-09-16 12:15:19 -04:00
										 |  |  |     def finalize(self): | 
					
						
							| 
									
										
										
										
											2020-09-16 21:40:01 -04:00
										 |  |  |         self._g_dispatch = None | 
					
						
							|  |  |  |         self._greenlets = [] | 
					
						
							|  |  |  |         for g in self._all_greenlets: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 g.throw() | 
					
						
							|  |  |  |             except: | 
					
						
							|  |  |  |                 logging.exception("reactor finalize greenlet terminate") | 
					
						
							|  |  |  |         self._all_greenlets = [] | 
					
						
							| 
									
										
										
										
											2020-09-16 12:15:19 -04:00
										 |  |  |         if self._pipe_fds is not None: | 
					
						
							|  |  |  |             os.close(self._pipe_fds[0]) | 
					
						
							|  |  |  |             os.close(self._pipe_fds[1]) | 
					
						
							|  |  |  |             self._pipe_fds = None | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | class PollReactor(SelectReactor): | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |     def __init__(self, gc_checking=False): | 
					
						
							|  |  |  |         SelectReactor.__init__(self, gc_checking) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self._poll = select.poll() | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._READ = select.POLLIN | select.POLLHUP | 
					
						
							|  |  |  |         self._WRITE = select.POLLOUT | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # File descriptors | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |     def register_fd(self, fd, read_callback, write_callback=None): | 
					
						
							|  |  |  |         file_handler = ReactorFileHandler(fd, read_callback, write_callback) | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._fds[fd] = file_handler | 
					
						
							|  |  |  |         self._poll.register(file_handler.fd, select.POLLIN | select.POLLHUP) | 
					
						
							| 
									
										
										
										
											2019-06-09 14:38:01 -04:00
										 |  |  |         return file_handler | 
					
						
							|  |  |  |     def unregister_fd(self, file_handler): | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._poll.unregister(file_handler.fd) | 
					
						
							|  |  |  |         del self._fds[file_handler.fd] | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |     def set_fd_wake(self, file_handler, is_readable=True, is_writeable=False): | 
					
						
							|  |  |  |         flags = select.POLLHUP | 
					
						
							|  |  |  |         if is_readable: | 
					
						
							|  |  |  |             flags |= select.POLLIN | 
					
						
							|  |  |  |         if is_writeable: | 
					
						
							|  |  |  |             flags |= select.POLLOUT | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._poll.modify(file_handler.fd, flags) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Main loop | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |     def _dispatch_loop(self): | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._g_dispatch = greenlet.getcurrent() | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |         busy = True | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |         eventtime = self.monotonic() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         while self._process: | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |             timeout = self._check_timers(eventtime, busy) | 
					
						
							|  |  |  |             busy = False | 
					
						
							| 
									
										
										
										
											2017-01-10 10:55:46 -05:00
										 |  |  |             res = self._poll.poll(int(math.ceil(timeout * 1000.))) | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |             eventtime = self.monotonic() | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |             if res: | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |                 busy = True | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |                 eventtime = self._check_fds(eventtime, res) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         self._g_dispatch = None | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | class EPollReactor(SelectReactor): | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |     def __init__(self, gc_checking=False): | 
					
						
							|  |  |  |         SelectReactor.__init__(self, gc_checking) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self._epoll = select.epoll() | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._READ = select.EPOLLIN | select.EPOLLHUP | 
					
						
							|  |  |  |         self._WRITE = select.EPOLLOUT | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # File descriptors | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |     def register_fd(self, fd, read_callback, write_callback=None): | 
					
						
							|  |  |  |         file_handler = ReactorFileHandler(fd, read_callback, write_callback) | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._fds[fd] = file_handler | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self._epoll.register(fd, select.EPOLLIN | select.EPOLLHUP) | 
					
						
							| 
									
										
										
										
											2019-06-09 14:38:01 -04:00
										 |  |  |         return file_handler | 
					
						
							|  |  |  |     def unregister_fd(self, file_handler): | 
					
						
							|  |  |  |         self._epoll.unregister(file_handler.fd) | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         del self._fds[file_handler.fd] | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |     def set_fd_wake(self, file_handler, is_readable=True, is_writeable=False): | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         flags = select.EPOLLHUP | 
					
						
							| 
									
										
										
										
											2022-05-23 21:23:56 -04:00
										 |  |  |         if is_readable: | 
					
						
							|  |  |  |             flags |= select.EPOLLIN | 
					
						
							|  |  |  |         if is_writeable: | 
					
						
							|  |  |  |             flags |= select.EPOLLOUT | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._epoll.modify(file_handler.fd, flags) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Main loop | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |     def _dispatch_loop(self): | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |         self._g_dispatch = greenlet.getcurrent() | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |         busy = True | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |         eventtime = self.monotonic() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         while self._process: | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |             timeout = self._check_timers(eventtime, busy) | 
					
						
							|  |  |  |             busy = False | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             res = self._epoll.poll(timeout) | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |             eventtime = self.monotonic() | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |             if res: | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |                 busy = True | 
					
						
							| 
									
										
										
										
											2025-09-05 13:34:48 -04:00
										 |  |  |                 eventtime = self._check_fds(eventtime, res) | 
					
						
							| 
									
										
										
										
											2016-11-15 19:56:27 -05:00
										 |  |  |         self._g_dispatch = None | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Use the poll based reactor if it is available | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     select.poll | 
					
						
							|  |  |  |     Reactor = PollReactor | 
					
						
							|  |  |  | except: | 
					
						
							|  |  |  |     Reactor = SelectReactor |