Exemple #1
0
class ZTcpAcceptor(object):
    def __init__(self, event_loop, listen_addr):
        self._event_loop = event_loop
        self._accept_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._accept_socket.setblocking(False)
        self._accept_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._accept_socket.bind(listen_addr)
        self._accept_channel = ZChannel(event_loop, self._accept_socket.fileno())
        self._accept_channel.set_read_callback(self.handle_read)
        self._new_connection_callback = None
        self._listening = False

    def listen(self):
        self._event_loop.assert_in_loop_thread()
        self._listening = True
        self._accept_socket.listen(socket.SOMAXCONN)
        self._accept_channel.enable_reading()

    def listening(self):
        return self._listening

    def handle_read(self, receive_time):
        self._event_loop.assert_in_loop_thread()
        try:
            sock, peer_addr = self._accept_socket.accept()
            sock.setblocking(False)
            if self._new_connection_callback:
                self._new_connection_callback(sock, peer_addr)
            else:
                conn.close()
        except socket.error as e:
            logging.error('ZTcpAcceptor.handle_read (%s)' %str(e))

    def set_new_connection_callback(self, cb):
        self._new_connection_callback = cb 
Exemple #2
0
class ZTcpConnection(object):
    DISCONNECTED = 0
    CONNECTING = 1
    CONNECTED = 2
    DISCONNECTING = 3
    def __init__(self, event_loop, name, sock, peer_addr):
        self._event_loop = event_loop
        self._name = name
        self._socket = sock
        self._peer_addr = peer_addr
        self._channel = ZChannel(event_loop, sock.fileno())
        self._channel.set_read_callback(self.handle_read)
        self._channel.set_write_callback(self.handle_write)
        self._channel.set_close_callback(self.handle_close)
        self._channel.set_error_callback(self.handle_error)
        self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
        self._high_watermark = 64 * 1024 * 1024
        self._state = self.CONNECTING
        self._connection_callback = None
        self._message_callback = None
        self._close_callback = None
        self._write_complete_callback = None
        self._high_watermark_callback = None
        self._context = None
        self._input_buffer = ZBuffer()
        self._output_buffer = ZBuffer()

    def name(self):
        return self._name

    def get_event_loop(self):
        return self._event_loop

    def set_connection_callback(self, cb):
        self._connection_callback = cb

    def set_message_callback(self, cb):
        self._message_callback = cb

    def set_close_callback(self, cb):
        self._close_callback = cb
    
    def set_write_complete_callback(self, cb):
        self._write_complete_callback = cb

    def set_high_watermark_callback(self, cb):
        self._high_watermark_callback = cb

    def _write_complete_callback_wrapper(self):
        self._write_complete_callback(self)

    def set_context(self, ct):
        self._context = ct

    def get_context(self):
        return self._context

    def send(self, data):
        def send_in_loop_wrapper():
            self.send_in_loop(data)

        if self._state == self.CONNECTED:
            if self._event_loop.is_in_loop_thread():
                self.send_in_loop(data)
            else:
                self._event_loop.run_in_loop(send_in_loop_wrapper)

    def send_in_loop(self, data):
        self._event_loop.assert_in_loop_thread()

        if self._state == self.DISCONNECTED:
            logging.warn('disconnected connection, give up writing')
            return

        siz = len(data)
        remaining = siz 
        has_fatal_error = False
        try:
            if not self._channel.is_writing() and self._output_buffer.readable_bytes() == 0: 
                nwritten = self._socket.send(data)
                remaining = siz - nwritten
                if remaining == 0 and self._write_complete_callback:
                    self._event_loop.queue_in_loop(self._write_complete_callback_wrapper)
        except socket.error as e:
            logging.error('ZTcpConnection.send_in_loop fail to write with error %s' %str(e))
            if e.errno != errno.EWOULDBLOCK:
                if e.errno == errno.EPIPE:
                    has_fatal_error = True

        if not has_fatal_error and remaining > 0:
            logging.info('ZTcpConnection.send_in_loop I am going to write more data')
            old_len = self._output_buffer.readable_bytes()
            if old_len + remaining >= self._high_watermark and \
               old_len < self._high_watermark and \
               self._high_watermark_callback:
                siz = old_len + remaining
                def high_watermark_callback_wrapper():
                    self._high_watermark_callback(self, siz)
                self._event_loop.queue_in_loop(_high_watermark_callback_wrapper)
                self._output_buffer.append(data[nwritten:])
                if not self._channel.is_writting():
                    self._channel.enable_writing()

    def handle_read(self, receive_time):
        self._event_loop.assert_in_loop_thread()
        # FIXME recv_into
        try:
            data = self._socket.recv(65536)
            if data:
                self._input_buffer.append(data)
                self._message_callback(self, self._input_buffer, receive_time) 
            else:
                self.handle_close()
        except socket.error as e:
            logging.error('ZTcpConnection.handle_read fail to read with error %s' %str(e))
            self.handle_error()

    def handle_write(self):
        self._event_loop.assert_in_loop_thread()
        if self._channel.is_writing():
            try:
                n = self._socket.send(self._ouput_buffer.tostring())
                self._output_buffer.retrieve(n)
                if self._output_buffer.readable_bytes() == 0:
                    self._channel.disable_writing()
                    if self._write_complete_callback:
                        self._event_loop.queue_in_loop(self._write_complete_callback_wrapper)

                    if self._state == self.DISCONNECTING:
                        self.shutdown_in_loop()
            except socket.error as e:
                logging.error('ZTcpConnection.handle_write fail to write with error %s' %str(e))
        else:
            logging.info('ZTcpConnection.handle_write connection is down, no more writing')

    def _set_state(self, st):
        self._state = st

    def connected(self):
        return self._state == self.CONNECTED

    def handle_close(self):
        self._event_loop.assert_in_loop_thread()
        assert self._state == self.CONNECTED or self._state == self.DISCONNECTING
        self._set_state(self.DISCONNECTED)
        self._channel.disable_all()
        self._connection_callback(self)
        self._close_callback(self)

    def handle_error(self):
        logging.error('ZTcpConnection.handle_error error happened')

    def shutdown_write(self):
        if self._state == self.CONNECTED:
            self._set_state(self.DISCONNECTING)
            self._event_loop.run_in_loop(self.shutdown_write_in_loop)

    def shutdown(self):
        if self._state == self.CONNECTED:
            self._set_state(self.DISCONNECTING)
            self._event_loop.run_in_loop(self.shutdown_in_loop)

    def shutdown_write_in_loop(self):
        self._event_loop.assert_in_loop_thread()
        # FIXME what if channel is writing ?
        if not self._channel.is_writing():
            self._socket.shutdown(socket.SHUT_WR)

    def shutdown_in_loop(self):
        self._event_loop.assert_in_loop_thread()
        # FIXME what if channel is writing ?
        if not self._channel.is_writing():
            self._socket.shutdown(socket.SHUT_RDWR)

    def set_tcp_no_delay(on):
        if on:
            self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        else:
            self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 0)

    def connection_established(self):
        self._event_loop.assert_in_loop_thread()
        assert self._state == self.CONNECTING
        self._set_state(self.CONNECTED)
        self._channel.tie(self)
        self._channel.enable_reading()
        self._connection_callback(self)

    def connection_destroyed(self):
        self._event_loop.assert_in_loop_thread()
        if self._state == self.CONNECTED:
            self._set_state(self.DISCONNECTED)
            self._channel.disable_all()
            self._connection_callback(self)
        self._channel.unregister()
Exemple #3
0
class ZEventLoop(object):
    def __init__(self):
        self._poller = default_poller(self)
        self._tid = current_thread().ident
        self._looping = False
        self._quit = False
        self._doing_pending_jobs = False
        self._pending_jobs = []
        self._mutex = Lock()
        self._handling_event = False
        # FIXME Hack for Windows
        if system() != 'Windows': 
            (rfd, wfd) = pipe()
            self._wakeup_rfd = rfd 
            self._wakeup_wfd = wfd
            self._wakeup_channel = ZChannel(self, self._wakeup_rfd)
            self._wakeup_channel.set_read_callback(self._handle_read)
            self._wakeup_channel.set_close_callback(self._handle_close)
            self._wakeup_channel.enable_reading()

    def loop(self):
        assert not self._looping 
        self.assert_in_loop_thread()
        self._looping = True
       
        active_channels = []
        while not self._quit:
            del active_channels[:]
            try:
                poll_return_time = self._poller.poll(active_channels, 1)
                self._handling_event = True
                for ch in active_channels:
                    ch.handle_event(poll_return_time)
                self._handling_event = False 
                self._do_pending_jobs()
            except Exception as e:
                exc_type, exc_value, exc_traceback = exc_info()
                logging.error('ZEventLoop.loop encouting error %s' 
                              %(''.join(format_exception(exc_type, exc_value, exc_traceback))))

    def quit(self):
        self._quit = True
        if not self.is_in_loop_thread():
            self._wakeup()

    def run_in_loop(self, cb):
        if self.is_in_loop_thread():
            cb()
        else:
            self.queue_in_loop(cb)

    def queue_in_loop(self, cb):
        with self._mutex:
            self._pending_jobs.append(cb)

        if not self.is_in_loop_thread() or self._doing_pending_jobs:
            self._wakeup()

    def update_channel(self, channel):
        assert channel.owner_loop() == self
        self.assert_in_loop_thread()
        self._poller.update(channel)

    def unregister_channel(self, channel):
        assert channel.owner_loop() == self
        self.assert_in_loop_thread()
        assert channel.has_no_events()
        self._poller.unregister(channel)

    def assert_in_loop_thread(self):
        assert self.is_in_loop_thread()

    def is_in_loop_thread(self):
        return self._tid == current_thread().ident

    def _wakeup(self):
        if system() != 'Windows':
            cnt = write(self._wakeup_wfd, '1')
            if cnt != 1:
                logging.error('ZEventLoop._wakeup() failed to wakeup')

    def _handle_read(self, receive_time):
        data = read(self._wakeup_rfd, 1)
        if int(data) != 1:
            logging.error('ZEventLoop._handle_read() failed to read')

    def _handle_close(self):
        def unregister_wakeup_channel():
            logging.error('ZTcpAcceptor._handle_close unregister wakeup channel')
            self.unregister_channel(self._wakeup_channel)
        self.run_in_loop(unregister_wakeup_channel)

    def _do_pending_jobs(self):
        self._doing_pending_jobs = True
        jobs = [] 
        with self._mutex:
            jobs.extend(self._pending_jobs)
            del self._pending_jobs[:]

        for job in jobs:
            job()
        self._doing_pending_jobs = False