def __init__(self, select_type = 'any', thread_safe = True): """ Constructor. """ # init member vars. self._max_size = -1 self._event_fd = None self._handler_rep = None self._notify_handler = None self._timer_queue = TimerQueue(self) self._payload = 0 self._payload_lock = Lock() self._select_type = select_type self._thread_safe = thread_safe if self._thread_safe: self._token = Token(self) else: self._token = TokenNull(self) self._token_released = False self._activated = False
class Reactor(object): def __init__(self, select_type = 'any', thread_safe = True): """ Constructor. """ # init member vars. self._max_size = -1 self._event_fd = None self._handler_rep = None self._notify_handler = None self._timer_queue = TimerQueue(self) self._payload = 0 self._payload_lock = Lock() self._select_type = select_type self._thread_safe = thread_safe if self._thread_safe: self._token = Token(self) else: self._token = TokenNull(self) self._token_released = False self._activated = False def open(self, max_size = -1): """ Initialize the EpollReactor. @note On Unix platforms, the <max_size> parameter should be as large as the maximum number of file descriptors allowed for a given process. This is necessary since a file descriptor is used to directly index the array of event handlers maintained by the Reactor's handler repository. Direct indexing is used for efficiency reasons. If <max_size> parameter is <= 0, then Reactor will set `ulimit -n` to <max_size> """ self._token.acquire() try: self._max_size = max_size if self._max_size <= 0 and sys.platform != 'win32': self._max_size = os.sysconf('SC_OPEN_MAX') if self._max_size <= 0 and sys.platform != 'win32': return False self._events = [] self._handler_rep = [[None, 0] for i in range(self._max_size)] if self._select_type == 'any': if sys.platform == 'linux2': try: n = EPOLLIN self._select_type = 'epoll' except: try: n = POLLIN self._select_type = 'poll' except: self._select_type = 'select' elif sys.platform[:-1] == 'freebsd': self._select_type = 'kqueue' elif sys.platform == 'win32': self._select_type = 'select' elif sys.platform == 'sunos5': self._select_type = 'poll' else: self._select_type = 'select' ## # Special platform method. if self._select_type == 'epoll': self._event_fd = epoll(self._max_size) self._dispatch_io_event = self._epoll_dispatch_io_event self._reactor_mask_to_os_event = self._reactor_mask_to_epoll_event self._find_handle = self._unix_find_handle self._handle_is_invalid = self._unix_handle_is_invalid self._unbind_handle = self._unix_unbind_handle self._event_mask = self._unix_event_mask self._set_event_mask = self._set_unix_event_mask elif self._select_type == 'kqueue': self._event_fd = kqueue() self._dispatch_io_event = self._kqueue_dispatch_io_event self._reactor_mask_to_os_event = self._reactor_mask_to_kqueue_event self._find_handle = self._unix_find_handle self._handle_is_invalid = self._unix_handle_is_invalid self._unbind_handle = self._unix_unbind_handle self._event_mask = self._unix_event_mask self._set_event_mask = self._set_unix_event_mask elif self._select_type == 'poll': self._event_fd = poll() self._dispatch_io_event = self._poll_dispatch_io_event self._reactor_mask_to_os_event = self._reactor_mask_to_poll_event self._find_handle = self._unix_find_handle self._handle_is_invalid = self._unix_handle_is_invalid self._unbind_handle = self._unix_unbind_handle self._event_mask = self._unix_event_mask self._set_event_mask = self._set_unix_event_mask elif self._select_type == 'select' and sys.platform == 'win32': if self._max_size <= 0: self._max_size = 512 # Python-x.x.x/Modules/selectmodule.c defined. self._handler_rep = {} # {socket:[ehandler, mask]} self._event_fd = Selector(self._max_size) self._dispatch_io_event = self._select_dispatch_io_event self._reactor_mask_to_os_event = self._reactor_mask_to_select_event self._find_handle = self._win32_find_handle self._handle_is_invalid = self._win32_handle_is_invalid self._unbind_handle = self._win32_unbind_handle self._event_mask = self._win32_event_mask self._set_event_mask = self._set_win32_event_mask elif self._select_type == 'select' and sys.platform != 'win32': if self._max_size <= 0: self._max_size = 1024 # Unix platform default defined. self._handler_rep = [[None, 0] for i in range(self._max_size)] self._event_fd = Selector(self._max_size) self._dispatch_io_event = self._select_dispatch_io_event self._reactor_mask_to_os_event = self._reactor_mask_to_select_event self._find_handle = self._unix_find_handle self._handle_is_invalid = self._unix_handle_is_invalid self._unbind_handle = self._unix_unbind_handle self._event_mask = self._unix_event_mask self._set_event_mask = self._set_unix_event_mask else: print("Pyndk-> Unsupport platform or select type [%s]!" % self._select_type) return False if not self._thread_safe: # Optimization self.handle_events = self._handle_events_unsafe self.register_handler = self._register_handler_unsafe self.remove_handler = self._remove_handler_unsafe self.schedule_timer = self._schedule_timer_unsafe self.crontab = self._crontab_unsafe self.reset_timer_interval = self._reset_timer_interval_unsafe self.cancel_timer = self._cancel_timer_unsafe self.payload = self._payload_unsafe self.deactivated = self._deactivated_unsafe self.activated = self._activated_unsafe if not self._event_fd: return False # Optimization self._event_poll = self._event_fd.poll self._event_register = self._event_fd.register self._event_unregister = self._event_fd.unregister self._event_modify = self._event_fd.modify # Notify self._notify_handler = ReactorNotify() self._notify_handler.open() if not self._register_handler_i(self._notify_handler.get_handle(), self._notify_handler, READ_MASK): self._close() return False self._activated = True return True finally: self._token.release() def close(self): """ Close Reactor and release system resource. """ self._token.acquire(True) try: self._close() finally: self._token.release() def deactivated(self, flag = False): """ Activate or deactivate reactor. """ self._token.acquire(True) try: self._activated = flag finally: self._token.release() def activated(self): self._token.acquire() try: return self._activated finally: self._token.release() def lock(self): return self._token def reactor_event_loop(self): while True: if not self.handle_events(): break def handle_events(self, timeout = -1): """ <timeout> is in milliseconds (as float) which identify demultiplex driver will be blocked. On -1 will block until some <fd> activated. """ self._token.acquire() self._token_released = False if not self._activated: return False try: return self._handle_events_i(timeout) finally: if not self._token_released: self._token.release() # Register and remove Handlers. # def register_handler(self, handle, ehandler, event_mask): """ Register handler for I/O events. A handler can be associated with multiple handles. A handle cannot be associated with multiple handlers. """ try: ehandler.set_reactor(self) except: return False self._token.acquire(True) try: if self._payload >= self._max_size: return False return self._register_handler_i(handle, ehandler, event_mask) finally: self._token.release() def remove_handler(self, handle, event_mask): """ Remove <event_mask> from <handle> registration. """ self._token.acquire(True) try: return self._remove_handler_i(handle, event_mask) finally: self._token.release() # Timer management. # def schedule_timer(self, ehandler, arg, delay, interval = 0.0): """ Schedule a timer. Return a timer id (non-negative integeron successfully, return -1 on failed. """ try: ehandler.set_reactor(self) except: pass self._token.acquire(True) try: return self._timer_queue.schedule(ehandler, arg, delay, interval) finally: self._token.release() def crontab(self, ehandler, arg, entry): """ Schedule a crontab timer. Return a timer id (non-negative integeron successfully, return -1 on failed. """ try: ehandler.set_reactor(self) except: pass self._token.acquire(True) try: return self._timer_queue.crontab(ehandler, arg, entry) finally: self._token.release() def reset_timer_interval(self, timer_id, interval): """ Resets the interval of the timer represented by <timer_id> to <interval>. """ # token self._token.acquire(True) try: if type(timer_id) != int: timer_id = self._timer_queue.get_timerid_by_handler(timer_id) return self._timer_queue.reset_interval(timer_id, interval) finally: self._token.release() def cancel_timer(self, timer_id, dont_call_handle_close = True): """ Cancel timer associated with <timer_id> that was returned from the schedule_timer() or crontab() method. On successful cancellation EventHandler::handle_close() will be called with TIMER_MASK if <dont_call_handle_close> is False. <timer_id> can be EventHandler instance. """ self._token.acquire(True) try: if type(timer_id) != int: timer_id = self._timer_queue.get_timerid_by_handler(timer_id) return self._timer_queue.cancel(timer_id, dont_call_handle_close) finally: self._token.release() ## Noitfy def notify(self): self._notify_handler.notify() def payload(self): self._token.acquire() try: return self._payload finally: self._token.acquire.release() ################################################################# # Implementation methods # ################################################################# def _close(self): if self._event_fd: self._event_fd.close() self._event_fd = None if self._notify_handler: self._notify_handler.close() self._notify_handler = None self._max_size = -1 def _handle_events_unsafe(self, timeout = -1): """ Not thread-safe. """ if not self._activated: return False if not self._events: # empty while True: try: ret_val = self._poll_events(timeout) if ret_val: break except OSError as e: if e.args[0] == EINTR: continue print("Pyndk-> %s" % e) err = True except IOError as e: if e.args[0] == EINTR: continue print("Pyndk-> %s" % e) err = True except Exception as e: if e.args[0] == EINTR: continue print("Pyndk-> %s" % e) err = True if not ret_val and err: return False # Dispatch timer handler self._timer_queue.expire() #Dispatch IO event self._dispatch_io_event() return True def _handle_events_i(self, timeout): """ """ if not self._events: # empty while True: try: ret_val = self._poll_events(timeout) if ret_val: break except OSError as e: if e.args[0] == EINTR: continue print("Pyndk-> %s" % e) err = True except IOError as e: if e.args[0] == EINTR: continue print("Pyndk-> %s" % e) err = True except Exception as e: if e.args[0] == EINTR: continue print("Pyndk-> %s" % e) err = True if not ret_val and err: return False # Dispatch timer handler self._timer_queue.expire() #Dispatch IO event self._dispatch_io_event() self._token.release() self._token_released = True return True def _poll_events(self, timeout): wait_time = self._timer_queue.calculate_timeout(timeout) if (wait_time != -1 and timeout == -1) \ or (wait_time != -1 and timeout != -1 and wait_time != timeout): timers_pending = True else: timers_pending = False #print("wait time = %f" % (wait_time)) self._events = self._event_poll(wait_time) if (not self._events) and (not timers_pending): return False return True ################################################################# # Platform special dispatcher # def _epoll_dispatch_io_event(self): """ Dispatch the activated events. """ result = 0 events = self._events find_func = self._find_handle handler_rep = self._handler_rep remove_handler_i = self._remove_handler_i while events and result < _MAX_REACTOR_PROCESS_FDS_ONE_TIME: #event = events.popleft() event = events.pop(0) mask = event[1] if find_func(event[0]): if mask & EPOLLIN: result += 1 mask &= (~EPOLLIN) if not handler_rep[event[0]][0].handle_input(event[0]): remove_handler_i(event[0], READ_MASK) elif mask & EPOLLOUT: result += 1 mask &= (~EPOLLOUT) if not handler_rep[event[0]][0].handle_output(event[0]): remove_handler_i(event[0], WRITE_MASK) elif mask & EPOLLPRI: result += 1 mask &= (~EPOLLPRI) if not handler_rep[event[0]][0].handle_except(event[0]): remove_handler_i(event[0], EXCEPT_MASK) elif mask & (EPOLLHUP | EPOLLERR): result += 1 mask &= (~(EPOLLHUP | EPOLLERR)) remove_handler_i(event[0], ALL_EVENTS_MASK) # end of `if self._find_handle(event[0])' else: continue if mask != 0: events.append((event[0], mask)) # end of `while self._events and result ...' def _poll_dispatch_io_event(self): """ Dispatch the activated events. """ result = 0 events = self._events find_func = self._find_handle handler_rep = self._handler_rep remove_handler_i = self._remove_handler_i while events and result < _MAX_REACTOR_PROCESS_FDS_ONE_TIME: event = events.pop() mask = event[1] if find_func(event[0]): if mask & POLLIN: result += 1 mask &= (~POLLIN) if not handler_rep[event[0]][0].handle_input(event[0]): remove_handler_i(event[0], READ_MASK) elif mask & POLLOUT: result += 1 mask &= (~POLLOUT) if not handler_rep[event[0]][0].handle_output(event[0]): remove_handler_i(event[0], WRITE_MASK) elif mask & POLLPRI: result += 1 mask &= (~POLLPRI) if not handler_rep[event[0]][0].handle_except(event[0]): remove_handler_i(event[0], EXCEPT_MASK) elif mask & (POLLHUP | POLLERR): result += 1 mask &= (~(POLLHUP | POLLERR)) remove_handler_i(event[0], ALL_EVENTS_MASK) # end of `if self._find_handle(event[0])' else: continue if mask != 0: events.append((event[0], mask)) # end of `while self._events and result ...' def _kqueue_dispatch_io_event(self): pass def _select_dispatch_io_event(self): """ Dispatch the activated events. """ (rd_events, wr_events, ex_events) = self._events find_func = self._find_handle handler_rep = self._handler_rep remove_handler_i = self._remove_handler_i for fd in rd_events: if find_func(fd): if not handler_rep[fd][0].handle_input(fd): remove_handler_i(fd, READ_MASK) for fd in wr_events: if find_func(fd): if not handler_rep[fd][0].handle_output(fd): remove_handler_i(fd, WRITE_MASK) for fd in ex_events: if find_func(fd): if not handler_rep[fd].handle_except(fd): remove_handler_i(fd, EXCEPT_MASK) self._events = None def _register_handler_unsafe(self, handle, ehandler, event_mask): """ Not thread-safe. """ try: ehandler.set_reactor(self) except: raise (ValueError, "ehandler should override 'set_reactor' method.") return self._register_handler_i(handle, ehandler, event_mask) def _register_handler_i(self, handle, handler, event_mask): if self._handle_is_invalid(handle): return False if not self._find_handle(handle): self._bind_handle(handle, handler, event_mask) events = self._reactor_mask_to_os_event(event_mask) try: self._event_register(handle, events) except IOError as e: print("Pyndk-> register : [%s]" % e) self._unbind_handle(handle) return False self._payload += 1 else: self._mask_opt(handle, event_mask, ADD_MASK) return True def _remove_handler_unsafe(self, handle, event_mask): """ Not thread-safe. """ return self._remove_handler_i(handle, event_mask) def _remove_handler_i(self, handle, event_mask): if type(handle) != int: handle = handle.get_handle() eh = self._find_handle(handle) if not eh: return False self._mask_opt(handle, event_mask, CLR_MASK) if not (event_mask & DONT_CALL): eh[0].handle_close(handle, event_mask) if self._event_mask(handle) == NULL_MASK: self._unbind_handle(handle) self._payload -= 1 return True def _schedule_timer_unsafe(self, ehandler, arg, delay, interval = 0.0): """ Not thread-safe. """ try: ehandler.set_reactor(self) except: pass return self._timer_queue.schedule(ehandler, arg, delay, interval) def _crontab_unsafe(self, ehandler, arg, entry): """ Not thread-safe. """ try: ehandler.set_reactor(self) except: pass return self._timer_queue.crontab(ehandler, arg, entry) def _reset_timer_interval_unsafe(self, timer_id, interval): """ Not thread-safe. """ if type(timer_id) != int: timer_id = self._timer_queue.get_timerid_by_handler(timer_id) return self._timer_queue.reset_interval(timer_id, interval) def _cancel_timer_unsafe(self, timer_id, dont_call_handle_close = True): """ Not thread-safe. """ if type(timer_id) != int: timer_id = self._timer_queue.get_timerid_by_handler(timer_id) return self._timer_queue.cancel(timer_id, dont_call_handle_close) def _payload_unsafe(self): """ Not thread-safe. """ return self._payload def _deactivated_unsafe(self, flag = False): """ Not thread-safe. """ self._activated = flag def _activated_unsafe(self): """ Not thread-safe. """ return self._activated def _mask_opt(self, handle, mask, opt): new_mask = self._event_mask(handle) if opt == ADD_MASK: new_mask |= mask elif opt == MOD_MASK: new_mask = mask elif opt == CLR_MASK: new_mask &= ~mask self._set_event_mask(handle, new_mask) if new_mask == NULL_MASK: self._event_unregister(handle) return True else: new_mask = self._reactor_mask_to_os_event(new_mask) try: self._event_modify(handle, new_mask) except Exception as e: print("Pyndk-> modify : [%s]" % e) return True def _reactor_mask_to_select_event(self, mask): return mask def _reactor_mask_to_epoll_event(self, mask): events = NULL_MASK if mask & READ_MASK: events |= EPOLLIN if mask & EDGE_MASK: events |= EPOLLET if mask & ACCEPT_MASK: events |= EPOLLIN if mask & CONNECT_MASK: events |= EPOLLIN|EPOLLOUT if mask & WRITE_MASK: events |= EPOLLOUT if mask & EXCEPT_MASK: events |= EPOLLPRI return events def _reactor_mask_to_poll_event(self, mask): events = NULL_MASK if mask & READ_MASK: events |= POLLIN if mask & ACCEPT_MASK: events |= POLLIN if mask & CONNECT_MASK: events |= POLLIN|POLLOUT if mask & WRITE_MASK: events |= POLLOUT if mask & EXCEPT_MASK: events |= POLLPRI return events def _reactor_mask_to_kqueue_event(self, mask): pass def _unix_handle_is_invalid(self, handle): return handle < 0 def _win32_handle_is_invalid(self, handle): return handle == None def _unix_find_handle(self, handle): if self._handler_rep[handle][1] == 0 or not self._handler_rep[handle][0]: return None return self._handler_rep[handle] def _win32_find_handle(self, handle): return self._handler_rep.get(handle, None) def _unix_event_mask(self, handle): return self._handler_rep[handle][1] def _win32_event_mask(self, handle): try: return self._handler_rep[handle][1] except: return 0 def _set_unix_event_mask(self, handle, new_mask): self._handler_rep[handle][1] = new_mask def _set_win32_event_mask(self, handle, new_mask): try: self._handler_rep[handle][1] = new_mask except: self._handler_rep[handle] = [None, new_mask] def _bind_handle(self, handle, ehandler, event_mask): self._handler_rep[handle] = [ehandler, event_mask] def _unix_unbind_handle(self, handle): self._handler_rep[handle] = [None, 0] def _win32_unbind_handle(self, handle): try: del self._handler_rep[handle] except: pass def _incre_payload(self, count = 1): self._payload += count def _decre_payload(self, count = 1): self._payload -= count