Show More
@@ -34,6 +34,8 b' import os' | |||
|
34 | 34 | import re |
|
35 | 35 | import signal |
|
36 | 36 | import struct |
|
37 | import threading | |
|
38 | import time | |
|
37 | 39 | import traceback |
|
38 | 40 | |
|
39 | 41 | from mercurial.i18n import _ |
@@ -380,20 +382,93 b' class _requesthandler(SocketServer.Strea' | |||
|
380 | 382 | traceback.print_exc(file=sv.cerr) |
|
381 | 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 | 448 | class chgunixservice(commandserver.unixservice): |
|
384 | 449 | def init(self): |
|
385 | 450 | # drop options set for "hg serve --cmdserver" command |
|
386 | 451 | self.ui.setconfig('progress', 'assume-tty', None) |
|
387 | 452 | signal.signal(signal.SIGHUP, self._reloadconfig) |
|
388 |
class cls(SocketServer.ForkingMixIn, |
|
|
453 | class cls(AutoExitMixIn, SocketServer.ForkingMixIn, | |
|
454 | SocketServer.UnixStreamServer): | |
|
389 | 455 | ui = self.ui |
|
390 | 456 | repo = self.repo |
|
391 | 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 | 461 | # avoid writing "listening at" message to stdout before attachio |
|
393 | 462 | # request, which calls setvbuf() |
|
394 | 463 | |
|
395 | 464 | def _reloadconfig(self, signum, frame): |
|
396 | 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 | 473 | def uisetup(ui): |
|
399 | 474 | commandserver._servicemap['chgunix'] = chgunixservice |
General Comments 0
You need to be logged in to leave comments.
Login now