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
class SshFetchConnection extends BasePackFetchConnection { | class SshFetchConnection extends BasePackFetchConnection { | ||||
private ChannelExec channel; | private ChannelExec channel; | ||||
private Thread errorThread; | |||||
private StreamCopyThread errorThread; | |||||
private int exitStatus; | private int exitStatus; | ||||
if (errorThread != null) { | if (errorThread != null) { | ||||
try { | try { | ||||
errorThread.join(); | |||||
errorThread.halt(); | |||||
} catch (InterruptedException e) { | } catch (InterruptedException e) { | ||||
// Stop waiting and return anyway. | // Stop waiting and return anyway. | ||||
} finally { | } finally { | ||||
class SshPushConnection extends BasePackPushConnection { | class SshPushConnection extends BasePackPushConnection { | ||||
private ChannelExec channel; | private ChannelExec channel; | ||||
private Thread errorThread; | |||||
private StreamCopyThread errorThread; | |||||
private int exitStatus; | private int exitStatus; | ||||
if (errorThread != null) { | if (errorThread != null) { | ||||
try { | try { | ||||
errorThread.join(); | |||||
errorThread.halt(); | |||||
} catch (InterruptedException e) { | } catch (InterruptedException e) { | ||||
// Stop waiting and return anyway. | // Stop waiting and return anyway. | ||||
} finally { | } finally { |
private final AtomicInteger flushCounter = new AtomicInteger(0); | 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. | * Create a thread to copy data from an input stream to an output stream. | ||||
* | * | ||||
interrupt(); | 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 | @Override | ||||
public void run() { | public void run() { | ||||
try { | try { | ||||
if (needFlush()) | if (needFlush()) | ||||
dst.flush(); | dst.flush(); | ||||
if (done) | |||||
break; | |||||
final int n; | final int n; | ||||
try { | try { | ||||
n = src.read(buf); | n = src.read(buf); |