class SocketEngine (object): sock = None _poll = None _lock = None _sock_dict = None logger = None _rw_timeout = 0 _idle_timeout = 0 _last_checktimeout = None _checktimeout_inv = 0 def __init__ (self, poll, is_blocking=True, debug=True): """ sock: sock to listen """ self._debug = debug self._sock_dict = dict () self._locker = threading.Lock () self._lock = self._locker.acquire self._unlock = self._locker.release self._poll = poll self._cbs = MyList () # (handler, handler_args) self._pending_fd_ops = MyList () # (handler, conn) self._checktimeout_inv = 0 self.get_time = time.time self.is_blocking = is_blocking def set_timeout (self, rw_timeout, idle_timeout): self._rw_timeout = rw_timeout self._idle_timeout = idle_timeout self._last_checktimeout = time.time () temp_timeout = [] if self._rw_timeout: temp_timeout.append (self._rw_timeout) if self._idle_timeout: temp_timeout.append (self._idle_timeout) if len(temp_timeout): self._checktimeout_inv = float (min (temp_timeout)) / 2 else: self._checktimeout_inv = 0 def set_logger (self, logger): self.logger = logger def log_error (self, msg): if self.logger: self.logger.warning (msg, bt_level=1) else: print msg def log_exception (self, e): if self.logger: self.logger.exception (e) else: print e def put_sock (self, sock, readable_cb, readable_cb_args=(), idle_timeout_cb=None, stack=True): return self._put_sock (sock, readable_cb, readable_cb_args, idle_timeout_cb, lock=True) def _put_sock (self, sock, readable_cb, readable_cb_args=(), idle_timeout_cb=None, stack=True, lock=False): conn = Connection (sock, readable_cb=readable_cb, readable_cb_args=readable_cb_args, idle_timeout_cb=idle_timeout_cb) if stack and self._debug: conn.putsock_tb = traceback.extract_stack()[0:-1] if lock: self.watch_conn (conn) else: self._watch_conn (conn) return conn def watch_conn (self, conn): """ assume conn is already manage by server, register into poll """ assert isinstance (conn, Connection) self._lock () self._pending_fd_ops.append ((self._watch_conn, conn)) self._unlock () def _watch_conn (self, conn): self._sock_dict[conn.fd] = conn conn.last_ts = self.get_time () conn.status = ConnState.IDLE if conn.sign == 'r': self._poll.register (conn.fd, 'r', conn.readable_cb, (conn, ) + conn.readable_cb_args) def remove_conn (self, conn): """ remove conntion from server """ self._lock () self._pending_fd_ops.append ((self._remove_conn, conn)) self._unlock () def _remove_conn (self, conn): conn.status = ConnState.EXTENDED_USING fd = conn.fd self._poll.unregister (fd) try: del self._sock_dict[fd] except KeyError: pass def close_conn (self, conn): """ remove an close connection """ self._lock () self._pending_fd_ops.append ((self._close_conn, conn)) self._unlock () def _close_conn (self, conn): fd = conn.fd self._poll.unregister (fd) try: del self._sock_dict[fd] except KeyError: pass conn.close () def _accept_conn (self, sock, readable_cb, readable_cb_args, idle_timeout_cb, new_conn_cb): """ socket will set FD_CLOEXEC upon accepted """ _accept = sock.accept _put_sock = self._put_sock while True: # have to make sure the socket is non-block, so we can accept multiple connection try: (csock, addr) = _accept () try: flags = fcntl.fcntl(csock, fcntl.F_GETFD) fcntl.fcntl(csock, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) except IOError, e: self.log_error ("cannot set FD_CLOEXEC on accepted socket fd, %s" % (str(e))) if self.is_blocking: csock.settimeout (self._rw_timeout or None) else: csock.setblocking (0) if callable (new_conn_cb): csock = new_conn_cb (csock, *readable_cb_args) if not csock: continue _put_sock (csock, readable_cb=readable_cb, readable_cb_args=readable_cb_args, idle_timeout_cb=idle_timeout_cb, stack=False, lock=False) except socket.error, e: if e[0] == errno.EAGAIN: return #no more if e[0] != errno.EINTR: msg = "accept error (unlikely): " + str(e) self.log_error (msg)