Browse Source

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>
stable-0.8
Shawn O. Pearce 15 years ago
parent
commit
2e961989e4
  1. 8
      org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
  2. 25
      org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java

8
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 { 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 class TransportGitSsh extends SshTransport implements PackTransport {
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 class TransportGitSsh extends SshTransport implements PackTransport {
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 class TransportGitSsh extends SshTransport implements PackTransport {
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 {

25
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 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 class StreamCopyThread extends Thread {
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 class StreamCopyThread extends Thread {
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);

Loading…
Cancel
Save