HTTPCLIENT-1127: a better fix for the deal-lock between SingleClientConnManager.releaseConnection() and SingleClientConnManager.shutdown(); previous fix may have broken ThreadSafeClientConnManager
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1180361 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
6bd3da869d
commit
28de736437
|
@ -112,7 +112,7 @@ public abstract class AbstractClientConnAdapter implements ManagedClientConnecti
|
||||||
* Detaches this adapter from the wrapped connection.
|
* Detaches this adapter from the wrapped connection.
|
||||||
* This adapter becomes useless.
|
* This adapter becomes useless.
|
||||||
*/
|
*/
|
||||||
protected void detach() {
|
protected synchronized void detach() {
|
||||||
wrappedConnection = null;
|
wrappedConnection = null;
|
||||||
duration = Long.MAX_VALUE;
|
duration = Long.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
@ -300,29 +300,25 @@ public abstract class AbstractClientConnAdapter implements ManagedClientConnecti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void releaseConnection() {
|
public synchronized void releaseConnection() {
|
||||||
synchronized (connManager) {
|
if (released) {
|
||||||
if (released) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
released = true;
|
|
||||||
connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
}
|
||||||
|
released = true;
|
||||||
|
connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void abortConnection() {
|
public synchronized void abortConnection() {
|
||||||
synchronized (connManager) {
|
if (released) {
|
||||||
if (released) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
released = true;
|
|
||||||
unmarkReusable();
|
|
||||||
try {
|
|
||||||
shutdown();
|
|
||||||
} catch (IOException ignore) {
|
|
||||||
}
|
|
||||||
connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
}
|
||||||
|
released = true;
|
||||||
|
unmarkReusable();
|
||||||
|
try {
|
||||||
|
shutdown();
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getAttribute(final String id) {
|
public Object getAttribute(final String id) {
|
||||||
|
|
|
@ -105,7 +105,7 @@ public abstract class AbstractPooledConnAdapter extends AbstractClientConnAdapte
|
||||||
* This adapter becomes useless.
|
* This adapter becomes useless.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void detach() {
|
protected synchronized void detach() {
|
||||||
poolEntry = null;
|
poolEntry = null;
|
||||||
super.detach();
|
super.detach();
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,19 +82,19 @@ public class SingleClientConnManager implements ClientConnectionManager {
|
||||||
|
|
||||||
/** The one and only entry in this pool. */
|
/** The one and only entry in this pool. */
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
protected PoolEntry uniquePoolEntry;
|
protected volatile PoolEntry uniquePoolEntry;
|
||||||
|
|
||||||
/** The currently issued managed connection, if any. */
|
/** The currently issued managed connection, if any. */
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
protected ConnAdapter managedConn;
|
protected volatile ConnAdapter managedConn;
|
||||||
|
|
||||||
/** The time of the last connection release, or -1. */
|
/** The time of the last connection release, or -1. */
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
protected long lastReleaseTime;
|
protected volatile long lastReleaseTime;
|
||||||
|
|
||||||
/** The time the last released connection expires and shouldn't be reused. */
|
/** The time the last released connection expires and shouldn't be reused. */
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
protected long connectionExpiresTime;
|
protected volatile long connectionExpiresTime;
|
||||||
|
|
||||||
/** Indicates whether this connection manager is shut down. */
|
/** Indicates whether this connection manager is shut down. */
|
||||||
protected volatile boolean isShutDown;
|
protected volatile boolean isShutDown;
|
||||||
|
@ -205,7 +205,7 @@ public class SingleClientConnManager implements ClientConnectionManager {
|
||||||
* @return a connection that can be used to communicate
|
* @return a connection that can be used to communicate
|
||||||
* along the given route
|
* along the given route
|
||||||
*/
|
*/
|
||||||
public synchronized ManagedClientConnection getConnection(HttpRoute route, Object state) {
|
public ManagedClientConnection getConnection(HttpRoute route, Object state) {
|
||||||
if (route == null) {
|
if (route == null) {
|
||||||
throw new IllegalArgumentException("Route may not be null.");
|
throw new IllegalArgumentException("Route may not be null.");
|
||||||
}
|
}
|
||||||
|
@ -215,47 +215,49 @@ public class SingleClientConnManager implements ClientConnectionManager {
|
||||||
log.debug("Get connection for route " + route);
|
log.debug("Get connection for route " + route);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (managedConn != null)
|
synchronized (this) {
|
||||||
throw new IllegalStateException(MISUSE_MESSAGE);
|
if (managedConn != null)
|
||||||
|
throw new IllegalStateException(MISUSE_MESSAGE);
|
||||||
|
|
||||||
// check re-usability of the connection
|
// check re-usability of the connection
|
||||||
boolean recreate = false;
|
boolean recreate = false;
|
||||||
boolean shutdown = false;
|
boolean shutdown = false;
|
||||||
|
|
||||||
// Kill the connection if it expired.
|
// Kill the connection if it expired.
|
||||||
closeExpiredConnections();
|
closeExpiredConnections();
|
||||||
|
|
||||||
if (uniquePoolEntry.connection.isOpen()) {
|
if (uniquePoolEntry.connection.isOpen()) {
|
||||||
RouteTracker tracker = uniquePoolEntry.tracker;
|
RouteTracker tracker = uniquePoolEntry.tracker;
|
||||||
shutdown = (tracker == null || // can happen if method is aborted
|
shutdown = (tracker == null || // can happen if method is aborted
|
||||||
!tracker.toRoute().equals(route));
|
!tracker.toRoute().equals(route));
|
||||||
} else {
|
} else {
|
||||||
// If the connection is not open, create a new PoolEntry,
|
// If the connection is not open, create a new PoolEntry,
|
||||||
// as the connection may have been marked not reusable,
|
// as the connection may have been marked not reusable,
|
||||||
// due to aborts -- and the PoolEntry should not be reused
|
// due to aborts -- and the PoolEntry should not be reused
|
||||||
// either. There's no harm in recreating an entry if
|
// either. There's no harm in recreating an entry if
|
||||||
// the connection is closed.
|
// the connection is closed.
|
||||||
recreate = true;
|
recreate = true;
|
||||||
}
|
|
||||||
|
|
||||||
if (shutdown) {
|
|
||||||
recreate = true;
|
|
||||||
try {
|
|
||||||
uniquePoolEntry.shutdown();
|
|
||||||
} catch (IOException iox) {
|
|
||||||
log.debug("Problem shutting down connection.", iox);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shutdown) {
|
||||||
|
recreate = true;
|
||||||
|
try {
|
||||||
|
uniquePoolEntry.shutdown();
|
||||||
|
} catch (IOException iox) {
|
||||||
|
log.debug("Problem shutting down connection.", iox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recreate)
|
||||||
|
uniquePoolEntry = new PoolEntry();
|
||||||
|
|
||||||
|
managedConn = new ConnAdapter(uniquePoolEntry, route);
|
||||||
|
|
||||||
|
return managedConn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recreate)
|
|
||||||
uniquePoolEntry = new PoolEntry();
|
|
||||||
|
|
||||||
managedConn = new ConnAdapter(uniquePoolEntry, route);
|
|
||||||
|
|
||||||
return managedConn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void releaseConnection(
|
public void releaseConnection(
|
||||||
ManagedClientConnection conn,
|
ManagedClientConnection conn,
|
||||||
long validDuration, TimeUnit timeUnit) {
|
long validDuration, TimeUnit timeUnit) {
|
||||||
assertStillUp();
|
assertStillUp();
|
||||||
|
@ -271,51 +273,55 @@ public class SingleClientConnManager implements ClientConnectionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnAdapter sca = (ConnAdapter) conn;
|
ConnAdapter sca = (ConnAdapter) conn;
|
||||||
if (sca.poolEntry == null)
|
synchronized (sca) {
|
||||||
return; // already released
|
if (sca.poolEntry == null)
|
||||||
ClientConnectionManager manager = sca.getManager();
|
return; // already released
|
||||||
if (manager != null && manager != this) {
|
ClientConnectionManager manager = sca.getManager();
|
||||||
throw new IllegalArgumentException
|
if (manager != null && manager != this) {
|
||||||
("Connection not obtained from this manager.");
|
throw new IllegalArgumentException
|
||||||
}
|
("Connection not obtained from this manager.");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// make sure that the response has been read completely
|
// make sure that the response has been read completely
|
||||||
if (sca.isOpen() && (this.alwaysShutDown ||
|
if (sca.isOpen() && (this.alwaysShutDown ||
|
||||||
!sca.isMarkedReusable())
|
!sca.isMarkedReusable())
|
||||||
) {
|
) {
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug
|
log.debug
|
||||||
("Released connection open but not reusable.");
|
("Released connection open but not reusable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure this connection will not be re-used
|
// make sure this connection will not be re-used
|
||||||
// we might have gotten here because of a shutdown trigger
|
// we might have gotten here because of a shutdown trigger
|
||||||
// shutdown of the adapter also clears the tracked route
|
// shutdown of the adapter also clears the tracked route
|
||||||
sca.shutdown();
|
sca.shutdown();
|
||||||
|
}
|
||||||
|
} catch (IOException iox) {
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
log.debug("Exception shutting down released connection.",
|
||||||
|
iox);
|
||||||
|
} finally {
|
||||||
|
sca.detach();
|
||||||
|
synchronized (this) {
|
||||||
|
managedConn = null;
|
||||||
|
lastReleaseTime = System.currentTimeMillis();
|
||||||
|
if(validDuration > 0)
|
||||||
|
connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
|
||||||
|
else
|
||||||
|
connectionExpiresTime = Long.MAX_VALUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException iox) {
|
|
||||||
if (log.isDebugEnabled())
|
|
||||||
log.debug("Exception shutting down released connection.",
|
|
||||||
iox);
|
|
||||||
} finally {
|
|
||||||
sca.detach();
|
|
||||||
managedConn = null;
|
|
||||||
lastReleaseTime = System.currentTimeMillis();
|
|
||||||
if(validDuration > 0)
|
|
||||||
connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
|
|
||||||
else
|
|
||||||
connectionExpiresTime = Long.MAX_VALUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void closeExpiredConnections() {
|
public void closeExpiredConnections() {
|
||||||
if(System.currentTimeMillis() >= connectionExpiresTime) {
|
long time = connectionExpiresTime;
|
||||||
|
if (System.currentTimeMillis() >= time) {
|
||||||
closeIdleConnections(0, TimeUnit.MILLISECONDS);
|
closeIdleConnections(0, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void closeIdleConnections(long idletime, TimeUnit tunit) {
|
public void closeIdleConnections(long idletime, TimeUnit tunit) {
|
||||||
assertStillUp();
|
assertStillUp();
|
||||||
|
|
||||||
// idletime can be 0 or negative, no problem there
|
// idletime can be 0 or negative, no problem there
|
||||||
|
@ -323,35 +329,40 @@ public class SingleClientConnManager implements ClientConnectionManager {
|
||||||
throw new IllegalArgumentException("Time unit must not be null.");
|
throw new IllegalArgumentException("Time unit must not be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
|
synchronized (this) {
|
||||||
final long cutoff =
|
if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
|
||||||
System.currentTimeMillis() - tunit.toMillis(idletime);
|
final long cutoff =
|
||||||
if (lastReleaseTime <= cutoff) {
|
System.currentTimeMillis() - tunit.toMillis(idletime);
|
||||||
try {
|
if (lastReleaseTime <= cutoff) {
|
||||||
uniquePoolEntry.close();
|
try {
|
||||||
} catch (IOException iox) {
|
uniquePoolEntry.close();
|
||||||
// ignore
|
} catch (IOException iox) {
|
||||||
log.debug("Problem closing idle connection.", iox);
|
// ignore
|
||||||
|
log.debug("Problem closing idle connection.", iox);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void shutdown() {
|
public void shutdown() {
|
||||||
|
|
||||||
this.isShutDown = true;
|
this.isShutDown = true;
|
||||||
|
|
||||||
if (managedConn != null)
|
ConnAdapter conn = managedConn;
|
||||||
managedConn.detach();
|
if (conn != null)
|
||||||
|
conn.detach();
|
||||||
|
|
||||||
try {
|
synchronized (this) {
|
||||||
if (uniquePoolEntry != null) // and connection open?
|
try {
|
||||||
uniquePoolEntry.shutdown();
|
if (uniquePoolEntry != null) // and connection open?
|
||||||
} catch (IOException iox) {
|
uniquePoolEntry.shutdown();
|
||||||
// ignore
|
} catch (IOException iox) {
|
||||||
log.debug("Problem while shutting down manager.", iox);
|
// ignore
|
||||||
} finally {
|
log.debug("Problem while shutting down manager.", iox);
|
||||||
uniquePoolEntry = null;
|
} finally {
|
||||||
|
uniquePoolEntry = null;
|
||||||
|
managedConn = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,15 +370,19 @@ public class SingleClientConnManager implements ClientConnectionManager {
|
||||||
* @deprecated no longer used
|
* @deprecated no longer used
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected synchronized void revokeConnection() {
|
protected void revokeConnection() {
|
||||||
if (managedConn == null)
|
ConnAdapter conn = managedConn;
|
||||||
|
if (conn == null)
|
||||||
return;
|
return;
|
||||||
managedConn.detach();
|
conn.detach();
|
||||||
try {
|
|
||||||
uniquePoolEntry.shutdown();
|
synchronized (this) {
|
||||||
} catch (IOException iox) {
|
try {
|
||||||
// ignore
|
uniquePoolEntry.shutdown();
|
||||||
log.debug("Problem while shutting down connection.", iox);
|
} catch (IOException iox) {
|
||||||
|
// ignore
|
||||||
|
log.debug("Problem while shutting down connection.", iox);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue