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