Show More
@@ -47,7 +47,7 b' class connectionpool(object):' | |||||
47 | if util.safehasattr(peer, '_cleanup'): |
|
47 | if util.safehasattr(peer, '_cleanup'): | |
48 |
|
48 | |||
49 | class mypeer(peer.__class__): |
|
49 | class mypeer(peer.__class__): | |
50 | def _cleanup(self): |
|
50 | def _cleanup(self, warn=None): | |
51 | # close pipee first so peer.cleanup reading it won't |
|
51 | # close pipee first so peer.cleanup reading it won't | |
52 | # deadlock, if there are other processes with pipeo |
|
52 | # deadlock, if there are other processes with pipeo | |
53 | # open (i.e. us). |
|
53 | # open (i.e. us). |
@@ -148,14 +148,18 b' class doublepipe(object):' | |||||
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: | |
@@ -165,6 +169,17 b' def _cleanuppipes(ui, pipei, pipeo, pipe' | |||||
165 |
|
169 | |||
166 | pipee.close() |
|
170 | pipee.close() | |
167 |
|
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 | ) | |||
|
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. | |
@@ -416,6 +431,7 b' class sshv1peer(wireprotov1peer.wirepeer' | |||||
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. | |
@@ -456,10 +472,11 b' class sshv1peer(wireprotov1peer.wirepeer' | |||||
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( | |
@@ -611,7 +628,7 b' def makepeer(ui, path, proc, stdin, stdo' | |||||
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: | |
@@ -637,7 +654,7 b' def makepeer(ui, path, proc, stdin, stdo' | |||||
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 | ) |
General Comments 0
You need to be logged in to leave comments.
Login now