##// END OF EJS Templates
chgserver: auto exit after being idle for too long or lose the socket file...
Jun Wu -
r28223:0a853dc9 default
parent child Browse files
Show More
@@ -34,6 +34,8 b' import os'
34 import re
34 import re
35 import signal
35 import signal
36 import struct
36 import struct
37 import threading
38 import time
37 import traceback
39 import traceback
38
40
39 from mercurial.i18n import _
41 from mercurial.i18n import _
@@ -380,20 +382,93 b' class _requesthandler(SocketServer.Strea'
380 traceback.print_exc(file=sv.cerr)
382 traceback.print_exc(file=sv.cerr)
381 raise
383 raise
382
384
385 def _tempaddress(address):
386 return '%s.%d.tmp' % (address, os.getpid())
387
388 class AutoExitMixIn: # use old-style to comply with SocketServer design
389 lastactive = time.time()
390 idletimeout = 3600 # default 1 hour
391
392 def startautoexitthread(self):
393 # note: the auto-exit check here is cheap enough to not use a thread,
394 # be done in serve_forever. however SocketServer is hook-unfriendly,
395 # you simply cannot hook serve_forever without copying a lot of code.
396 # besides, serve_forever's docstring suggests using thread.
397 thread = threading.Thread(target=self._autoexitloop)
398 thread.daemon = True
399 thread.start()
400
401 def _autoexitloop(self, interval=1):
402 while True:
403 time.sleep(interval)
404 if not self.issocketowner():
405 _log('%s is not owned, exiting.\n' % self.server_address)
406 break
407 if time.time() - self.lastactive > self.idletimeout:
408 _log('being idle too long. exiting.\n')
409 break
410 self.shutdown()
411
412 def process_request(self, request, address):
413 self.lastactive = time.time()
414 return SocketServer.ForkingMixIn.process_request(
415 self, request, address)
416
417 def server_bind(self):
418 # use a unique temp address so we can stat the file and do ownership
419 # check later
420 tempaddress = _tempaddress(self.server_address)
421 self.socket.bind(tempaddress)
422 self._socketstat = os.stat(tempaddress)
423 # rename will replace the old socket file if exists atomically. the
424 # old server will detect ownership change and exit.
425 util.rename(tempaddress, self.server_address)
426
427 def issocketowner(self):
428 try:
429 stat = os.stat(self.server_address)
430 return (stat.st_ino == self._socketstat.st_ino and
431 stat.st_mtime == self._socketstat.st_mtime)
432 except OSError:
433 return False
434
435 def unlinksocketfile(self):
436 if not self.issocketowner():
437 return
438 # it is possible to have a race condition here that we may
439 # remove another server's socket file. but that's okay
440 # since that server will detect and exit automatically and
441 # the client will start a new server on demand.
442 try:
443 os.unlink(self.server_address)
444 except OSError as exc:
445 if exc.errno != errno.ENOENT:
446 raise
447
383 class chgunixservice(commandserver.unixservice):
448 class chgunixservice(commandserver.unixservice):
384 def init(self):
449 def init(self):
385 # drop options set for "hg serve --cmdserver" command
450 # drop options set for "hg serve --cmdserver" command
386 self.ui.setconfig('progress', 'assume-tty', None)
451 self.ui.setconfig('progress', 'assume-tty', None)
387 signal.signal(signal.SIGHUP, self._reloadconfig)
452 signal.signal(signal.SIGHUP, self._reloadconfig)
388 class cls(SocketServer.ForkingMixIn, SocketServer.UnixStreamServer):
453 class cls(AutoExitMixIn, SocketServer.ForkingMixIn,
454 SocketServer.UnixStreamServer):
389 ui = self.ui
455 ui = self.ui
390 repo = self.repo
456 repo = self.repo
391 self.server = cls(self.address, _requesthandler)
457 self.server = cls(self.address, _requesthandler)
458 self.server.idletimeout = self.ui.configint(
459 'chgserver', 'idletimeout', self.server.idletimeout)
460 self.server.startautoexitthread()
392 # avoid writing "listening at" message to stdout before attachio
461 # avoid writing "listening at" message to stdout before attachio
393 # request, which calls setvbuf()
462 # request, which calls setvbuf()
394
463
395 def _reloadconfig(self, signum, frame):
464 def _reloadconfig(self, signum, frame):
396 self.ui = self.server.ui = _renewui(self.ui)
465 self.ui = self.server.ui = _renewui(self.ui)
397
466
467 def run(self):
468 try:
469 self.server.serve_forever()
470 finally:
471 self.server.unlinksocketfile()
472
398 def uisetup(ui):
473 def uisetup(ui):
399 commandserver._servicemap['chgunix'] = chgunixservice
474 commandserver._servicemap['chgunix'] = chgunixservice
General Comments 0
You need to be logged in to leave comments. Login now