Skip to content

Commit d5fcfff

Browse files
committed
Use tri-state return value to make duplicate releases harmless
PerRoutePool.release now signals “not tracked” with the sentinel NOT_TRACKED (-1). H2SharingConnPool.release delegates to the underlying pool only when the result is 0 and silently ignores the sentinel, so a second release no longer triggers IllegalStateException. Tests have been updated to expect the new sentinel and remain green.
1 parent b49b1fc commit d5fcfff

2 files changed

Lines changed: 29 additions & 24 deletions

File tree

httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/H2SharingConnPool.java

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public class H2SharingConnPool<T, C extends HttpConnection> implements ManagedCo
7979
private final ConcurrentMap<T, PerRoutePool<T, C>> perRouteCache;
8080
private final AtomicBoolean closed;
8181

82+
private static final long NOT_TRACKED = -1L;
83+
8284
public H2SharingConnPool(final ManagedConnPool<T, C> pool) {
8385
this.pool = Args.notNull(pool, "Connection pool");
8486
this.perRouteCache = new ConcurrentHashMap<>();
@@ -165,27 +167,30 @@ public void release(final PoolEntry<T, C> entry, final boolean reusable) {
165167
pool.release(entry, reusable);
166168
return;
167169
}
168-
final T route = entry.getRoute();
169-
final PerRoutePool<T, C> perRoutePool = perRouteCache.get(route);
170-
if (perRoutePool != null && perRoutePool.getCount(entry) == 0) {
171-
return;
172-
}
170+
171+
final PerRoutePool<T, C> perRoutePool = perRouteCache.get(entry.getRoute());
173172
if (perRoutePool != null) {
174-
final long count = perRoutePool.release(entry, reusable);
175-
if (count > 0) {
173+
final long res = perRoutePool.release(entry, reusable);
174+
175+
if (res == NOT_TRACKED) {
176+
return;
177+
}
178+
if (res > 0) {
176179
if (LOG.isDebugEnabled()) {
177180
LOG.debug("Connection {} is being shared for message exchange multiplexing (lease count = {})",
178-
ConnPoolSupport.getId(entry.getConnection()), count);
181+
ConnPoolSupport.getId(entry.getConnection()), res);
179182
}
180183
return;
181184
}
182185
}
183186
if (LOG.isDebugEnabled()) {
184-
LOG.debug("Releasing connection {} back to the pool", ConnPoolSupport.getId(entry.getConnection()));
187+
LOG.debug("Releasing connection {} back to the pool",
188+
ConnPoolSupport.getId(entry.getConnection()));
185189
}
186190
pool.release(entry, reusable);
187191
}
188192

193+
189194
@Override
190195
public void setMaxTotal(final int max) {
191196
pool.setMaxTotal(max);
@@ -291,20 +296,20 @@ PoolEntry<T, C> lease() {
291296
long release(final PoolEntry<T, C> entry, final boolean reusable) {
292297
lock.lock();
293298
try {
294-
final C connection = entry.getConnection();
295-
if (!reusable || connection == null || !connection.isOpen()) {
299+
final AtomicLong counter = entryMap.get(entry);
300+
if (counter == null) {
301+
return NOT_TRACKED;
302+
}
303+
final C conn = entry.getConnection();
304+
if (!reusable || conn == null || !conn.isOpen()) {
305+
entryMap.remove(entry);
306+
return 0L;
307+
}
308+
final long remaining = counter.decrementAndGet();
309+
if (remaining == 0) {
296310
entryMap.remove(entry);
297-
return 0;
298-
} else {
299-
final AtomicLong counter = entryMap.compute(entry, (e, c) -> {
300-
if (c == null) {
301-
return null;
302-
}
303-
final long count = c.decrementAndGet();
304-
return count > 0 ? c : null;
305-
});
306-
return counter != null ? counter.get() : 0L;
307311
}
312+
return remaining;
308313
} finally {
309314
lock.unlock();
310315
}

httpclient5/src/test/java/org/apache/hc/client5/http/impl/nio/H2SharingPerRoutePoolTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ void testReleaseReusable() {
8989
Assertions.assertEquals(2, pool.release(poolEntry1, true));
9090
Assertions.assertEquals(1, pool.release(poolEntry1, true));
9191
Assertions.assertEquals(0, pool.release(poolEntry1, true));
92-
Assertions.assertEquals(0, pool.release(poolEntry1, true));
92+
Assertions.assertEquals(-1, pool.release(poolEntry1, true));
9393
}
9494

9595
@Test
@@ -103,8 +103,8 @@ void testReleaseNonReusable() {
103103

104104
@Test
105105
void testReleaseNonPresent() {
106-
Assertions.assertEquals(0, pool.release(poolEntry1, true));
107-
Assertions.assertEquals(0, pool.release(poolEntry2, true));
106+
Assertions.assertEquals(-1, pool.release(poolEntry1, true));
107+
Assertions.assertEquals(-1, pool.release(poolEntry2, true));
108108
}
109109

110110
@Test

0 commit comments

Comments
 (0)