diff --git a/src/main/java/hudson/remoting/Request.java b/src/main/java/hudson/remoting/Request.java index 2ff2edf..be9bb9b 100644 --- a/src/main/java/hudson/remoting/Request.java +++ b/src/main/java/hudson/remoting/Request.java @@ -25,6 +25,7 @@ package hudson.remoting; import java.io.IOException; import java.io.Serializable; +import java.sql.Timestamp; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -136,19 +137,70 @@ abstract class Request extends C // so that thread dump would give us more useful information. Thread t = Thread.currentThread(); final String name = t.getName(); + int interruptCount = 0; + final String markerStr = " interrupthack: " + name + ": "; try { // wait until the response arrives t.setName(name+" / waiting for "+channel); - while(response==null && !channel.isInClosed()) + int nextTimeout = -1; + InterruptedException lastInterruptedEx = null; + while(response==null && !channel.isInClosed()) { // I don't know exactly when this can happen, as pendingCalls are cleaned up by Channel, // but in production I've observed that in rare occasion it can block forever, even after a channel // is gone. So be defensive against that. - wait(30*1000); + try { + if (nextTimeout >= 0) { + int tmpTimeout = nextTimeout; + nextTimeout = -1; + wait(tmpTimeout); + } else { + wait(30*1000); + } + + // unsure about necessity of this check, but it should do no harm either + if (lastInterruptedEx != null && (future == null || future.isCancelled() || future.isDone())) { + final String logStr = (new Timestamp(System.currentTimeMillis())).toString() + markerStr + + "old InterruptException re-throw triggered by future"; + System.out.println(logStr); + logger.severe(logStr); + throw lastInterruptedEx; + } + + } catch (InterruptedException ex) { + ++interruptCount; + if (response != null || channel.isInClosed() || channel.isOutClosed() + || future == null || future.isCancelled() || future.isDone()) { + + final String logStr = (new Timestamp(System.currentTimeMillis())).toString() + markerStr + + "re-throwing valid InterruptedException '" + ex.getMessage() + "', count " + interruptCount + + " (response " + (response != null) + ", chans closed in/out " + channel.isInClosed() + '/' + channel.isOutClosed() + + ", future " + (future==null ? "null" : "cancel/done " + future.isCancelled() + '/' + future.isDone()) + + ')'; + System.out.println(logStr); + logger.severe(logStr); + throw ex; + } + else { + lastInterruptedEx = ex; + final String logStr = (new Timestamp(System.currentTimeMillis())).toString() + markerStr + + "ignoring spurious InterruptedException #" + interruptCount; + System.out.println(logStr); + logger.severe(logStr); + nextTimeout = 1000; + } + } + } if (response==null) // channel is closed and we still don't have a response throw new RequestAbortedException(null); } finally { + if (interruptCount > 0) { + final String logStr = (new Timestamp(System.currentTimeMillis())).toString() + markerStr + + "finally block interruptedException count " + interruptCount; + System.out.println(logStr); + logger.severe(logStr); + } t.setName(name); }