comparison mercurial/sshpeer.py @ 46701:db8037e38085

sshpeer: add a develwarning if an sshpeer is not closed explicitly The warning is disabled until the next commit, because fixing it results in a noisy diff due to indentation changes. Differential Revision: https://phab.mercurial-scm.org/D9998
author Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
date Mon, 15 Feb 2021 14:40:17 -0500
parents 0738bc25d6ac
children a4c19a162615
comparison
equal deleted inserted replaced
46700:0509cee38757 46701:db8037e38085
146 146
147 def flush(self): 147 def flush(self):
148 return self._main.flush() 148 return self._main.flush()
149 149
150 150
151 def _cleanuppipes(ui, pipei, pipeo, pipee): 151 def _cleanuppipes(ui, pipei, pipeo, pipee, warn):
152 """Clean up pipes used by an SSH connection.""" 152 """Clean up pipes used by an SSH connection."""
153 if pipeo: 153 didsomething = False
154 if pipeo and not pipeo.closed:
155 didsomething = True
154 pipeo.close() 156 pipeo.close()
155 if pipei: 157 if pipei and not pipei.closed:
158 didsomething = True
156 pipei.close() 159 pipei.close()
157 160
158 if pipee: 161 if pipee and not pipee.closed:
162 didsomething = True
159 # Try to read from the err descriptor until EOF. 163 # Try to read from the err descriptor until EOF.
160 try: 164 try:
161 for l in pipee: 165 for l in pipee:
162 ui.status(_(b'remote: '), l) 166 ui.status(_(b'remote: '), l)
163 except (IOError, ValueError): 167 except (IOError, ValueError):
164 pass 168 pass
165 169
166 pipee.close() 170 pipee.close()
171
172 if didsomething and warn is not None:
173 # Encourage explicit close of sshpeers. Closing via __del__ is
174 # not very predictable when exceptions are thrown, which has led
175 # to deadlocks due to a peer get gc'ed in a fork
176 # We add our own stack trace, because the stacktrace when called
177 # from __del__ is useless.
178 if False: # enabled in next commit
179 ui.develwarn(
180 b'missing close on SSH connection created at:\n%s' % warn
181 )
167 182
168 183
169 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None): 184 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None):
170 """Create an SSH connection to a server. 185 """Create an SSH connection to a server.
171 186
414 self._pipeo = stdin 429 self._pipeo = stdin
415 self._pipei = stdout 430 self._pipei = stdout
416 self._pipee = stderr 431 self._pipee = stderr
417 self._caps = caps 432 self._caps = caps
418 self._autoreadstderr = autoreadstderr 433 self._autoreadstderr = autoreadstderr
434 self._initstack = b''.join(util.getstackframes(1))
419 435
420 # Commands that have a "framed" response where the first line of the 436 # Commands that have a "framed" response where the first line of the
421 # response contains the length of that response. 437 # response contains the length of that response.
422 _FRAMED_COMMANDS = { 438 _FRAMED_COMMANDS = {
423 b'batch', 439 b'batch',
454 470
455 def _abort(self, exception): 471 def _abort(self, exception):
456 self._cleanup() 472 self._cleanup()
457 raise exception 473 raise exception
458 474
459 def _cleanup(self): 475 def _cleanup(self, warn=None):
460 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee) 476 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee, warn=warn)
461 477
462 __del__ = _cleanup 478 def __del__(self):
479 self._cleanup(warn=self._initstack)
463 480
464 def _sendrequest(self, cmd, args, framed=False): 481 def _sendrequest(self, cmd, args, framed=False):
465 if self.ui.debugflag and self.ui.configbool( 482 if self.ui.debugflag and self.ui.configbool(
466 b'devel', b'debug.peer-request' 483 b'devel', b'debug.peer-request'
467 ): 484 ):
609 testing. 626 testing.
610 """ 627 """
611 try: 628 try:
612 protoname, caps = _performhandshake(ui, stdin, stdout, stderr) 629 protoname, caps = _performhandshake(ui, stdin, stdout, stderr)
613 except Exception: 630 except Exception:
614 _cleanuppipes(ui, stdout, stdin, stderr) 631 _cleanuppipes(ui, stdout, stdin, stderr, warn=None)
615 raise 632 raise
616 633
617 if protoname == wireprototypes.SSHV1: 634 if protoname == wireprototypes.SSHV1:
618 return sshv1peer( 635 return sshv1peer(
619 ui, 636 ui,
635 stderr, 652 stderr,
636 caps, 653 caps,
637 autoreadstderr=autoreadstderr, 654 autoreadstderr=autoreadstderr,
638 ) 655 )
639 else: 656 else:
640 _cleanuppipes(ui, stdout, stdin, stderr) 657 _cleanuppipes(ui, stdout, stdin, stderr, warn=None)
641 raise error.RepoError( 658 raise error.RepoError(
642 _(b'unknown version of SSH protocol: %s') % protoname 659 _(b'unknown version of SSH protocol: %s') % protoname
643 ) 660 )
644 661
645 662