From 2e961989e42b1fe7e8bd9eaa7a3d2e88a0d1d001 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" 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 Signed-off-by: Shawn O. Pearce --- .../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 class TransportGitSsh extends SshTransport implements PackTransport { class SshFetchConnection extends BasePackFetchConnection { private ChannelExec channel; - private Thread errorThread; + private StreamCopyThread errorThread; private int exitStatus; @@ -275,7 +275,7 @@ public class TransportGitSsh extends SshTransport implements PackTransport { if (errorThread != null) { try { - errorThread.join(); + errorThread.halt(); } catch (InterruptedException e) { // Stop waiting and return anyway. } finally { @@ -300,7 +300,7 @@ public class TransportGitSsh extends SshTransport implements PackTransport { class SshPushConnection extends BasePackPushConnection { private ChannelExec channel; - private Thread errorThread; + private StreamCopyThread errorThread; private int exitStatus; @@ -343,7 +343,7 @@ public class TransportGitSsh extends SshTransport implements PackTransport { 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 class StreamCopyThread extends Thread { interrupt(); } + /** + * Request that the thread terminate, and wait for it. + *

+ * 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 class StreamCopyThread extends Thread { if (needFlush()) dst.flush(); + if (done) + break; + final int n; try { n = src.read(buf);