From 2e961989e42b1fe7e8bd9eaa7a3d2e88a0d1d001 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Sat, 15 May 2010 17:01:49 -0700
Subject: [PATCH] Fix SSH deadlock during OutOfMemoryError

In close() method of SshFetchConnection and SshPushConnection
errorThread.join() can wait forever if JSch will not close the
channel's error stream.  Join with a timeout, and interrupt the
copy thread if its blocked on data that will never arrive.

Bug: 312863
Change-Id: I763081267653153eed9cd7763a015059338c2df8
Reported-by: Dmitry Neverov <dmitry.neverov@gmail.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../jgit/transport/TransportGitSsh.java       |  8 +++---
 .../jgit/util/io/StreamCopyThread.java        | 25 +++++++++++++++++++
 2 files changed, 29 insertions(+), 4 deletions(-)

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 d73e2055a..8df3ea5b2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -232,7 +232,7 @@ public void close() throws IOException {
 	class SshFetchConnection extends BasePackFetchConnection {
 		private ChannelExec channel;
 
-		private Thread errorThread;
+		private StreamCopyThread errorThread;
 
 		private int exitStatus;
 
@@ -275,7 +275,7 @@ public void close() {
 
 			if (errorThread != null) {
 				try {
-					errorThread.join();
+					errorThread.halt();
 				} catch (InterruptedException e) {
 					// Stop waiting and return anyway.
 				} finally {
@@ -300,7 +300,7 @@ public void close() {
 	class SshPushConnection extends BasePackPushConnection {
 		private ChannelExec channel;
 
-		private Thread errorThread;
+		private StreamCopyThread errorThread;
 
 		private int exitStatus;
 
@@ -343,7 +343,7 @@ public void close() {
 
 			if (errorThread != null) {
 				try {
-					errorThread.join();
+					errorThread.halt();
 				} catch (InterruptedException e) {
 					// Stop waiting and return anyway.
 				} finally {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java
index c36835692..f2715aca2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java
@@ -59,6 +59,8 @@ public class StreamCopyThread extends Thread {
 
 	private final AtomicInteger flushCounter = new AtomicInteger(0);
 
+	private volatile boolean done;
+
 	/**
 	 * Create a thread to copy data from an input stream to an output stream.
 	 *
@@ -87,6 +89,26 @@ public void flush() {
 		interrupt();
 	}
 
+	/**
+	 * Request that the thread terminate, and wait for it.
+	 * <p>
+	 * This method signals to the copy thread that it should stop as soon as
+	 * there is no more IO occurring.
+	 *
+	 * @throws InterruptedException
+	 *             the calling thread was interrupted.
+	 */
+	public void halt() throws InterruptedException {
+		for (;;) {
+			join(250 /* milliseconds */);
+			if (isAlive()) {
+				done = true;
+				interrupt();
+			} else
+				break;
+		}
+	}
+
 	@Override
 	public void run() {
 		try {
@@ -96,6 +118,9 @@ public void run() {
 					if (needFlush())
 						dst.flush();
 
+					if (done)
+						break;
+
 					final int n;
 					try {
 						n = src.read(buf);
-- 
GitLab