From 0dc93a2f4ab0c9ef8293c825c393fe0bad8b110a Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Sat, 20 Mar 2010 15:33:26 -0700
Subject: [PATCH] Fix EGit deadlock listing branches of SSH remote

When listing branches, EGit only reads the advertisement and
then disconnects.  When it closes down the pack channel the remote
side is waiting for the client to send our list of commands, or a
flush-pkt to let it know there is nothing to do.

However if an error thread is open watching the SSH stderr stream,
we ask for it to finish before we send the flush-pkt.  Unfortunately
the thread won't terminate until the main output stream closes,
which is waiting for the flush-pkt.  A classic network deadlock.

If the output stream needs a flush-pkt we send it before we wait
for the error stream to close.  If the flush-pkt is rejected, we
close down the output stream early, assuming that the remote side
is broken and we will get error information soon.

Change-Id: I8d078a339077756220c113f49d206b1bf295d434
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../jgit/transport/BasePackConnection.java    | 23 ++++++++++++++++++-
 .../jgit/transport/TransportGitSsh.java       |  4 ++++
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
index a2c572c60..7b6d3e121 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
@@ -282,8 +282,10 @@ private PackProtocolException duplicateAdvertisement(final String name) {
 	public void close() {
 		if (out != null) {
 			try {
-				if (outNeedsEnd)
+				if (outNeedsEnd) {
+					outNeedsEnd = false;
 					pckOut.end();
+				}
 				out.close();
 			} catch (IOException err) {
 				// Ignore any close errors.
@@ -314,4 +316,23 @@ public void close() {
 			}
 		}
 	}
+
+	/** Tell the peer we are disconnecting, if it cares to know. */
+	protected void endOut() {
+		if (outNeedsEnd && out != null) {
+			try {
+				outNeedsEnd = false;
+				pckOut.end();
+			} catch (IOException e) {
+				try {
+					out.close();
+				} catch (IOException err) {
+					// Ignore any close errors.
+				} finally {
+					out = null;
+					pckOut = null;
+				}
+			}
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index d4d4f5412..fb2038b86 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -264,6 +264,8 @@ class SshFetchConnection extends BasePackFetchConnection {
 
 		@Override
 		public void close() {
+			endOut();
+
 			if (errorThread != null) {
 				try {
 					errorThread.join();
@@ -331,6 +333,8 @@ class SshPushConnection extends BasePackPushConnection {
 
 		@Override
 		public void close() {
+			endOut();
+
 			if (errorThread != null) {
 				try {
 					errorThread.join();
-- 
GitLab