hgext/chgserver.py
changeset 29545 28aca3fafc2a
parent 29543 d74b8a4fde3b
child 29546 a7513390a9db
equal deleted inserted replaced
29544:024e8f82f3de 29545:28aca3fafc2a
   530                          'chdir': chdir,
   530                          'chdir': chdir,
   531                          'getpager': getpager,
   531                          'getpager': getpager,
   532                          'setenv': setenv,
   532                          'setenv': setenv,
   533                          'setumask': setumask})
   533                          'setumask': setumask})
   534 
   534 
   535 class _requesthandler(commandserver._requesthandler):
       
   536     def _createcmdserver(self, repo, conn, fin, fout):
       
   537         ui = self.server.ui
       
   538         return chgcmdserver(ui, repo, fin, fout, conn,
       
   539                             self.server.hashstate, self.server.baseaddress)
       
   540 
       
   541 def _tempaddress(address):
   535 def _tempaddress(address):
   542     return '%s.%d.tmp' % (address, os.getpid())
   536     return '%s.%d.tmp' % (address, os.getpid())
   543 
   537 
   544 def _hashaddress(address, hashstr):
   538 def _hashaddress(address, hashstr):
   545     return '%s-%s' % (address, hashstr)
   539     return '%s-%s' % (address, hashstr)
   546 
       
   547 class AutoExitMixIn:  # use old-style to comply with SocketServer design
       
   548     lastactive = time.time()
       
   549     idletimeout = 3600  # default 1 hour
       
   550 
       
   551     def startautoexitthread(self):
       
   552         # note: the auto-exit check here is cheap enough to not use a thread,
       
   553         # be done in serve_forever. however SocketServer is hook-unfriendly,
       
   554         # you simply cannot hook serve_forever without copying a lot of code.
       
   555         # besides, serve_forever's docstring suggests using thread.
       
   556         thread = threading.Thread(target=self._autoexitloop)
       
   557         thread.daemon = True
       
   558         thread.start()
       
   559 
       
   560     def _autoexitloop(self, interval=1):
       
   561         while True:
       
   562             time.sleep(interval)
       
   563             if not self.issocketowner():
       
   564                 _log('%s is not owned, exiting.\n' % self.server_address)
       
   565                 break
       
   566             if time.time() - self.lastactive > self.idletimeout:
       
   567                 _log('being idle too long. exiting.\n')
       
   568                 break
       
   569         self.shutdown()
       
   570 
       
   571     def process_request(self, request, address):
       
   572         self.lastactive = time.time()
       
   573         return socketserver.ForkingMixIn.process_request(
       
   574             self, request, address)
       
   575 
       
   576     def server_bind(self):
       
   577         # use a unique temp address so we can stat the file and do ownership
       
   578         # check later
       
   579         tempaddress = _tempaddress(self.server_address)
       
   580         util.bindunixsocket(self.socket, tempaddress)
       
   581         self._socketstat = os.stat(tempaddress)
       
   582         # rename will replace the old socket file if exists atomically. the
       
   583         # old server will detect ownership change and exit.
       
   584         util.rename(tempaddress, self.server_address)
       
   585 
       
   586     def issocketowner(self):
       
   587         try:
       
   588             stat = os.stat(self.server_address)
       
   589             return (stat.st_ino == self._socketstat.st_ino and
       
   590                     stat.st_mtime == self._socketstat.st_mtime)
       
   591         except OSError:
       
   592             return False
       
   593 
       
   594     def unlinksocketfile(self):
       
   595         if not self.issocketowner():
       
   596             return
       
   597         # it is possible to have a race condition here that we may
       
   598         # remove another server's socket file. but that's okay
       
   599         # since that server will detect and exit automatically and
       
   600         # the client will start a new server on demand.
       
   601         try:
       
   602             os.unlink(self.server_address)
       
   603         except OSError as exc:
       
   604             if exc.errno != errno.ENOENT:
       
   605                 raise
       
   606 
   540 
   607 class chgunixservice(commandserver.unixservice):
   541 class chgunixservice(commandserver.unixservice):
   608     def __init__(self, ui, repo, opts):
   542     def __init__(self, ui, repo, opts):
   609         super(chgunixservice, self).__init__(ui, repo=None, opts=opts)
   543         super(chgunixservice, self).__init__(ui, repo=None, opts=opts)
   610         if repo:
   544         if repo:
   651         util.rename(tempaddress, self.baseaddress)
   585         util.rename(tempaddress, self.baseaddress)
   652 
   586 
   653     def _cleanup(self):
   587     def _cleanup(self):
   654         self.server.unlinksocketfile()
   588         self.server.unlinksocketfile()
   655 
   589 
       
   590 class AutoExitMixIn:  # use old-style to comply with SocketServer design
       
   591     lastactive = time.time()
       
   592     idletimeout = 3600  # default 1 hour
       
   593 
       
   594     def startautoexitthread(self):
       
   595         # note: the auto-exit check here is cheap enough to not use a thread,
       
   596         # be done in serve_forever. however SocketServer is hook-unfriendly,
       
   597         # you simply cannot hook serve_forever without copying a lot of code.
       
   598         # besides, serve_forever's docstring suggests using thread.
       
   599         thread = threading.Thread(target=self._autoexitloop)
       
   600         thread.daemon = True
       
   601         thread.start()
       
   602 
       
   603     def _autoexitloop(self, interval=1):
       
   604         while True:
       
   605             time.sleep(interval)
       
   606             if not self.issocketowner():
       
   607                 _log('%s is not owned, exiting.\n' % self.server_address)
       
   608                 break
       
   609             if time.time() - self.lastactive > self.idletimeout:
       
   610                 _log('being idle too long. exiting.\n')
       
   611                 break
       
   612         self.shutdown()
       
   613 
       
   614     def process_request(self, request, address):
       
   615         self.lastactive = time.time()
       
   616         return socketserver.ForkingMixIn.process_request(
       
   617             self, request, address)
       
   618 
       
   619     def server_bind(self):
       
   620         # use a unique temp address so we can stat the file and do ownership
       
   621         # check later
       
   622         tempaddress = _tempaddress(self.server_address)
       
   623         util.bindunixsocket(self.socket, tempaddress)
       
   624         self._socketstat = os.stat(tempaddress)
       
   625         # rename will replace the old socket file if exists atomically. the
       
   626         # old server will detect ownership change and exit.
       
   627         util.rename(tempaddress, self.server_address)
       
   628 
       
   629     def issocketowner(self):
       
   630         try:
       
   631             stat = os.stat(self.server_address)
       
   632             return (stat.st_ino == self._socketstat.st_ino and
       
   633                     stat.st_mtime == self._socketstat.st_mtime)
       
   634         except OSError:
       
   635             return False
       
   636 
       
   637     def unlinksocketfile(self):
       
   638         if not self.issocketowner():
       
   639             return
       
   640         # it is possible to have a race condition here that we may
       
   641         # remove another server's socket file. but that's okay
       
   642         # since that server will detect and exit automatically and
       
   643         # the client will start a new server on demand.
       
   644         try:
       
   645             os.unlink(self.server_address)
       
   646         except OSError as exc:
       
   647             if exc.errno != errno.ENOENT:
       
   648                 raise
       
   649 
       
   650 class _requesthandler(commandserver._requesthandler):
       
   651     def _createcmdserver(self, repo, conn, fin, fout):
       
   652         ui = self.server.ui
       
   653         return chgcmdserver(ui, repo, fin, fout, conn,
       
   654                             self.server.hashstate, self.server.baseaddress)
       
   655 
   656 def uisetup(ui):
   656 def uisetup(ui):
   657     commandserver._servicemap['chgunix'] = chgunixservice
   657     commandserver._servicemap['chgunix'] = chgunixservice
   658 
   658 
   659     # CHGINTERNALMARK is temporarily set by chg client to detect if chg will
   659     # CHGINTERNALMARK is temporarily set by chg client to detect if chg will
   660     # start another chg. drop it to avoid possible side effects.
   660     # start another chg. drop it to avoid possible side effects.