Exemple #1
0
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)