HTTPCLIENT-725: use deadline instead of adjusted timeout interval
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@607424 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
540984183f
commit
6830f05aab
|
@ -30,6 +30,7 @@
|
|||
|
||||
package org.apache.http.impl.conn.tsccm;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Queue;
|
||||
|
@ -171,19 +172,18 @@ public class ConnPoolByRoute extends AbstractConnPool {
|
|||
int maxTotalConnections = HttpConnectionManagerParams
|
||||
.getMaxTotalConnections(this.params);
|
||||
|
||||
BasicPoolEntry entry = null;
|
||||
Date deadline = null;
|
||||
if (timeout > 0) {
|
||||
deadline = new Date(System.currentTimeMillis() + timeout);
|
||||
}
|
||||
|
||||
BasicPoolEntry entry = null;
|
||||
try {
|
||||
poolLock.lock();
|
||||
|
||||
RouteSpecificPool rospl = getRoutePool(route, true);
|
||||
WaitingThread waitingThread = null;
|
||||
|
||||
boolean useTimeout = (timeout > 0);
|
||||
long timeToWait = timeout;
|
||||
long startWait = 0;
|
||||
long endWait = 0;
|
||||
|
||||
while (entry == null) {
|
||||
|
||||
if (isShutDown) {
|
||||
|
@ -213,34 +213,28 @@ public class ConnPoolByRoute extends AbstractConnPool {
|
|||
entry = createEntry(rospl, operator);
|
||||
|
||||
} else {
|
||||
// TODO: keep track of which routes have waiting threads,
|
||||
// so they avoid being sacrificed before necessary
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Need to wait for connection. " + route);
|
||||
}
|
||||
|
||||
if (waitingThread == null) {
|
||||
//@@@ use factory method?
|
||||
waitingThread = new WaitingThread
|
||||
(poolLock.newCondition(), rospl);
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
if (useTimeout && timeToWait <= 0) {
|
||||
throw new ConnectionPoolTimeoutException
|
||||
("Timeout waiting for connection");
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Need to wait for connection. " + route);
|
||||
}
|
||||
|
||||
if (waitingThread == null) {
|
||||
//@@@ use factory method?
|
||||
waitingThread = new WaitingThread
|
||||
(poolLock.newCondition(), rospl);
|
||||
}
|
||||
|
||||
if (useTimeout) {
|
||||
startWait = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
rospl.queueThread(waitingThread);
|
||||
waitingThreads.add(waitingThread);
|
||||
success = waitingThread.await(timeToWait); //@@@, TimeUnit.MILLISECONDS); or deadline
|
||||
success = waitingThread.await(deadline);
|
||||
|
||||
} finally {
|
||||
// In case of 'success', we were woken up by the
|
||||
// connection pool and should now have a connection
|
||||
// waiting for us, or else we're shutting down.
|
||||
// Just continue in the loop, both cases are checked.
|
||||
if (!success) {
|
||||
// Either we timed out, experienced a
|
||||
// "spurious wakeup", or were interrupted by
|
||||
|
@ -249,15 +243,13 @@ public class ConnPoolByRoute extends AbstractConnPool {
|
|||
rospl.removeThread(waitingThread);
|
||||
waitingThreads.remove(waitingThread);
|
||||
}
|
||||
// In case of 'success', we were woken up by the
|
||||
// connection pool and should now have a connection
|
||||
// waiting for us, or else we're shutting down.
|
||||
// Just continue in the loop, both cases are checked.
|
||||
}
|
||||
|
||||
if (useTimeout) {
|
||||
endWait = System.currentTimeMillis();
|
||||
timeToWait -= (endWait - startWait);
|
||||
}
|
||||
// check for spurious wakeup vs. timeout
|
||||
if (!success && (deadline != null) &&
|
||||
(deadline.getTime() <= System.currentTimeMillis())) {
|
||||
throw new ConnectionPoolTimeoutException
|
||||
("Timeout waiting for connection");
|
||||
}
|
||||
}
|
||||
} // while no entry
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
package org.apache.http.impl.conn.tsccm;
|
||||
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
|
||||
|
@ -78,73 +79,6 @@ public class WaitingThread {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Blocks the calling thread.
|
||||
* This method returns when the thread is notified or interrupted,
|
||||
* if a timeout occurrs, or if there is a spurious wakeup.
|
||||
* <br/>
|
||||
* This method assumes external synchronization.
|
||||
*
|
||||
* @param timeout the timeout in milliseconds, or 0 for no timeout
|
||||
*
|
||||
* @return <code>true</code> if the condition was satisfied,
|
||||
* <code>false</code> in case of a timeout.
|
||||
* Typically, a call to {@link #wakeup} is used to indicate
|
||||
* that the condition was satisfied. Since the condition can
|
||||
* be accessed from outside, this cannot be guaranteed though.
|
||||
*
|
||||
* @throws InterruptedException if the waiting thread was interrupted
|
||||
*
|
||||
* @see #wakeup
|
||||
*/
|
||||
public boolean await(long timeout)
|
||||
throws InterruptedException {
|
||||
|
||||
//@@@ check timeout for negative, or assume overflow?
|
||||
//@@@ for now, leave the check to the condition
|
||||
|
||||
// This is only a sanity check. We cannot not synchronize here,
|
||||
// the lock would not be released on calling cond.await() below.
|
||||
if (this.waiter != null) {
|
||||
throw new IllegalStateException
|
||||
("A thread is already waiting on this object." +
|
||||
"\ncaller: " + Thread.currentThread() +
|
||||
"\nwaiter: " + this.waiter);
|
||||
}
|
||||
|
||||
this.waiter = Thread.currentThread();
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
success = this.cond.await(timeout, TimeUnit.MILLISECONDS);
|
||||
} finally {
|
||||
this.waiter = null;
|
||||
}
|
||||
return success;
|
||||
|
||||
} // await
|
||||
|
||||
|
||||
/**
|
||||
* Wakes up the waiting thread.
|
||||
* <br/>
|
||||
* This method assumes external synchronization.
|
||||
*/
|
||||
public void wakeup() {
|
||||
|
||||
// If external synchronization and pooling works properly,
|
||||
// this cannot happen. Just a sanity check.
|
||||
if (this.waiter == null) {
|
||||
throw new IllegalStateException
|
||||
("Nobody waiting on this object.");
|
||||
}
|
||||
|
||||
// One condition might be shared by several WaitingThread instances.
|
||||
// It probably isn't, but just in case: wake all, not just one.
|
||||
this.cond.signalAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the condition.
|
||||
*
|
||||
|
@ -179,4 +113,73 @@ public class WaitingThread {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Blocks the calling thread.
|
||||
* This method returns when the thread is notified or interrupted,
|
||||
* if a timeout occurrs, or if there is a spurious wakeup.
|
||||
* <br/>
|
||||
* This method assumes external synchronization.
|
||||
*
|
||||
* @param deadline when to time out, or <code>null</code> for no timeout
|
||||
*
|
||||
* @return <code>true</code> if the condition was satisfied,
|
||||
* <code>false</code> in case of a timeout.
|
||||
* Typically, a call to {@link #wakeup} is used to indicate
|
||||
* that the condition was satisfied. Since the condition is
|
||||
* accessible outside, this cannot be guaranteed though.
|
||||
*
|
||||
* @throws InterruptedException if the waiting thread was interrupted
|
||||
*
|
||||
* @see #wakeup
|
||||
*/
|
||||
public boolean await(Date deadline)
|
||||
throws InterruptedException {
|
||||
|
||||
// This is only a sanity check. We cannot synchronize here,
|
||||
// the lock would not be released on calling cond.await() below.
|
||||
if (this.waiter != null) {
|
||||
throw new IllegalStateException
|
||||
("A thread is already waiting on this object." +
|
||||
"\ncaller: " + Thread.currentThread() +
|
||||
"\nwaiter: " + this.waiter);
|
||||
}
|
||||
|
||||
this.waiter = Thread.currentThread();
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
if (deadline != null) {
|
||||
success = this.cond.awaitUntil(deadline);
|
||||
} else {
|
||||
this.cond.await();
|
||||
success = true;
|
||||
}
|
||||
} finally {
|
||||
this.waiter = null;
|
||||
}
|
||||
return success;
|
||||
|
||||
} // await
|
||||
|
||||
|
||||
/**
|
||||
* Wakes up the waiting thread.
|
||||
* <br/>
|
||||
* This method assumes external synchronization.
|
||||
*/
|
||||
public void wakeup() {
|
||||
|
||||
// If external synchronization and pooling works properly,
|
||||
// this cannot happen. Just a sanity check.
|
||||
if (this.waiter == null) {
|
||||
throw new IllegalStateException
|
||||
("Nobody waiting on this object.");
|
||||
}
|
||||
|
||||
// One condition might be shared by several WaitingThread instances.
|
||||
// It probably isn't, but just in case: wake all, not just one.
|
||||
this.cond.signalAll();
|
||||
}
|
||||
|
||||
|
||||
} // class WaitingThread
|
||||
|
|
Loading…
Reference in New Issue