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:
Roland Weber 2007-12-29 15:02:49 +00:00
parent 540984183f
commit 6830f05aab
2 changed files with 98 additions and 103 deletions

View File

@ -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

View File

@ -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