Skip to content

Commit

Permalink
Graceful shutdown; close #179
Browse files Browse the repository at this point in the history
  • Loading branch information
clowwindy committed Sep 11, 2014
1 parent 964d761 commit 327c70e
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 23 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setup(
name="shadowsocks",
version="2.2.0",
version="2.2.1",
license='MIT',
description="A fast tunnel proxy that help you get through firewalls",
author='clowwindy',
Expand Down
2 changes: 1 addition & 1 deletion shadowsocks/asyncdns.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def add_to_loop(self, loop):
socket.SOL_UDP)
self._sock.setblocking(False)
loop.add(self._sock, eventloop.POLL_IN)
loop.add_handler(self.handle_events)
loop.add_handler(self.handle_events, ref=False)

def _call_callback(self, hostname, ip, error=None):
callbacks = self._hostname_to_cb.get(hostname, [])
Expand Down
17 changes: 12 additions & 5 deletions shadowsocks/eventloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def __init__(self):
'package')
self._fd_to_f = {}
self._handlers = []
self.stopping = False
self._ref_handlers = []
logging.debug('using event model: %s', model)

def poll(self, timeout=None):
Expand All @@ -189,17 +189,24 @@ def modify(self, f, mode):
fd = f.fileno()
self._impl.modify_fd(fd, mode)

def add_handler(self, handler):
def add_handler(self, handler, ref=True):
self._handlers.append(handler)
if ref:
# when all ref handlers are removed, loop stops
self._ref_handlers.append(handler)

def remove_handler(self, handler):
self._handlers.remove(handler)
if handler in self._ref_handlers:
self._ref_handlers.remove(handler)

def run(self):
while not self.stopping:
while self._ref_handlers:
try:
events = self.poll(1)
except (OSError, IOError) as e:
if errno_from_exception(e) == errno.EPIPE:
if errno_from_exception(e) in (errno.EPIPE, errno.EINTR):
# Happens when the client closes the connection
logging.error('poll:%s', e)
continue
else:
logging.error('poll:%s', e)
Expand Down
18 changes: 11 additions & 7 deletions shadowsocks/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import tcprelay
import udprelay
import asyncdns
import signal


def main():
Expand Down Expand Up @@ -67,13 +68,14 @@ def main():
udp_servers.append(udprelay.UDPRelay(a_config, dns_resolver, False))

def run_server():
def child_handler(signum, _):
logging.warn('received SIGQUIT, doing graceful shutting down..')
map(lambda s: s.close(next_tick=True), tcp_servers + udp_servers)
signal.signal(signal.SIGQUIT, child_handler)
try:
loop = eventloop.EventLoop()
dns_resolver.add_to_loop(loop)
for tcp_server in tcp_servers:
tcp_server.add_to_loop(loop)
for udp_server in udp_servers:
udp_server.add_to_loop(loop)
map(lambda s: s.add_to_loop(loop), tcp_servers + udp_servers)
loop.run()
except (KeyboardInterrupt, IOError, OSError) as e:
logging.error(e)
Expand All @@ -97,11 +99,13 @@ def run_server():
if not is_child:
def handler(signum, _):
for pid in children:
os.kill(pid, signum)
os.waitpid(pid, 0)
try:
os.kill(pid, signum)
except OSError: # child may already exited
pass
sys.exit()
import signal
signal.signal(signal.SIGTERM, handler)
signal.signal(signal.SIGQUIT, handler)

# master
for a_tcp_server in tcp_servers:
Expand Down
14 changes: 11 additions & 3 deletions shadowsocks/tcprelay.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,15 @@ def _handle_events(self, events):
if now - self._last_time > TIMEOUT_PRECISION:
self._sweep_timeout()
self._last_time = now

def close(self):
if self._closed:
if self._server_socket:
self._eventloop.remove(self._server_socket)
self._server_socket.close()
self._server_socket = None
if not self._fd_to_handlers:
self._eventloop.remove_handler(self._handle_events)

def close(self, next_tick=False):
self._closed = True
self._server_socket.close()
if not next_tick:
self._server_socket.close()
13 changes: 9 additions & 4 deletions shadowsocks/udprelay.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,17 @@ def _handle_events(self, events):
logging.error('UDP client_socket err')
self._handle_client(sock)
now = time.time()
if now - self._last_time > 3.5:
if now - self._last_time > 3:
self._cache.sweep()
if now - self._last_time > 7:
self._client_fd_to_server_addr.sweep()
self._last_time = now
if self._closed:
self._server_socket.close()
for sock in self._sockets:
sock.close()
self._eventloop.remove_handler(self._handle_events)

def close(self):
def close(self, next_tick=False):
self._closed = True
self._server_socket.close()
if not next_tick:
self._server_socket.close()
4 changes: 2 additions & 2 deletions shadowsocks/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def get_config(is_local):
longopts = ['fast-open']
else:
shortopts = 'hs:p:k:m:c:t:vq'
longopts = ['fast-open', 'workers:']
longopts = ['fast-open', 'workers=']
try:
config_path = find_config()
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
Expand Down Expand Up @@ -134,7 +134,7 @@ def get_config(is_local):
elif key == '--fast-open':
config['fast_open'] = True
elif key == '--workers':
config['workers'] = value
config['workers'] = int(value)
elif key == '-h':
if is_local:
print_local_help()
Expand Down

0 comments on commit 327c70e

Please sign in to comment.