Skip to content
Snippets Groups Projects
Commit 2e961989 authored by Shawn Pearce's avatar Shawn Pearce
Browse files

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: default avatarDmitry Neverov <dmitry.neverov@gmail.com>
Signed-off-by: default avatarShawn O. Pearce <spearce@spearce.org>
parent b3247ba5
No related branches found
No related tags found
No related merge requests found
...@@ -232,7 +232,7 @@ public void close() throws IOException { ...@@ -232,7 +232,7 @@ public void close() throws IOException {
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;
...@@ -275,7 +275,7 @@ public void close() { ...@@ -275,7 +275,7 @@ public void close() {
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 {
...@@ -300,7 +300,7 @@ public void close() { ...@@ -300,7 +300,7 @@ public void close() {
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;
...@@ -343,7 +343,7 @@ public void close() { ...@@ -343,7 +343,7 @@ public void close() {
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 {
......
...@@ -59,6 +59,8 @@ public class StreamCopyThread extends Thread { ...@@ -59,6 +59,8 @@ public class StreamCopyThread extends Thread {
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.
* *
...@@ -87,6 +89,26 @@ public void flush() { ...@@ -87,6 +89,26 @@ public void flush() {
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 {
...@@ -96,6 +118,9 @@ public void run() { ...@@ -96,6 +118,9 @@ public void run() {
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);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment