1
1
/*
2
- * Copyright (c) 2015, 2022 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2015, 2023 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
40
40
import java .util .Objects ;
41
41
import java .util .Optional ;
42
42
import java .util .concurrent .Flow ;
43
+ import java .util .concurrent .locks .ReentrantLock ;
43
44
import java .util .stream .Collectors ;
44
45
import jdk .internal .net .http .common .FlowTube ;
45
46
import jdk .internal .net .http .common .Logger ;
@@ -57,11 +58,12 @@ final class ConnectionPool {
57
58
58
59
// Pools of idle connections
59
60
61
+ private final ReentrantLock stateLock = new ReentrantLock ();
60
62
private final HashMap <CacheKey ,LinkedList <HttpConnection >> plainPool ;
61
63
private final HashMap <CacheKey ,LinkedList <HttpConnection >> sslPool ;
62
64
private final ExpiryList expiryList ;
63
65
private final String dbgTag ; // used for debug
64
- boolean stopped ;
66
+ volatile boolean stopped ;
65
67
66
68
/**
67
69
* Entries in connection pool are keyed by destination address and/or
@@ -139,7 +141,7 @@ final String dbgString() {
139
141
return dbgTag ;
140
142
}
141
143
142
- synchronized void start () {
144
+ void start () {
143
145
assert !stopped : "Already stopped" ;
144
146
}
145
147
@@ -149,9 +151,21 @@ static CacheKey cacheKey(boolean secure, InetSocketAddress destination,
149
151
return new CacheKey (secure , destination , proxy );
150
152
}
151
153
152
- synchronized HttpConnection getConnection (boolean secure ,
153
- InetSocketAddress addr ,
154
- InetSocketAddress proxy ) {
154
+ HttpConnection getConnection (boolean secure ,
155
+ InetSocketAddress addr ,
156
+ InetSocketAddress proxy ) {
157
+ if (stopped ) return null ;
158
+ stateLock .lock ();
159
+ try {
160
+ return getConnection0 (secure , addr , proxy );
161
+ } finally {
162
+ stateLock .unlock ();
163
+ }
164
+ }
165
+
166
+ private HttpConnection getConnection0 (boolean secure ,
167
+ InetSocketAddress addr ,
168
+ InetSocketAddress proxy ) {
155
169
if (stopped ) return null ;
156
170
// for plain (unsecure) proxy connection the destination address is irrelevant.
157
171
addr = secure || proxy == null ? addr : null ;
@@ -185,7 +199,8 @@ void returnToPool(HttpConnection conn, Instant now, long keepAlive) {
185
199
186
200
// it's possible that cleanup may have been called.
187
201
HttpConnection toClose = null ;
188
- synchronized (this ) {
202
+ stateLock .lock ();
203
+ try {
189
204
if (cleanup .isDone ()) {
190
205
return ;
191
206
} else if (stopped ) {
@@ -203,6 +218,8 @@ void returnToPool(HttpConnection conn, Instant now, long keepAlive) {
203
218
putConnection (conn , sslPool );
204
219
}
205
220
expiryList .add (conn , now , keepAlive );
221
+ } finally {
222
+ stateLock .unlock ();
206
223
}
207
224
if (toClose != null ) {
208
225
if (debug .on ()) {
@@ -243,7 +260,7 @@ private CleanupTrigger registerCleanupTrigger(HttpConnection conn) {
243
260
removeFromPool (HttpConnection c ,
244
261
HashMap <CacheKey ,LinkedList <HttpConnection >> pool ) {
245
262
//System.out.println("cacheCleaner removing: " + c);
246
- assert Thread . holdsLock ( this );
263
+ assert stateLock . isHeldByCurrentThread ( );
247
264
CacheKey k = c .cacheKey ();
248
265
List <HttpConnection > l = pool .get (k );
249
266
if (l == null || l .isEmpty ()) {
@@ -288,7 +305,8 @@ long purgeExpiredConnectionsAndReturnNextDeadline(Instant now) {
288
305
if (!expiryList .purgeMaybeRequired ()) return nextPurge ;
289
306
290
307
List <HttpConnection > closelist ;
291
- synchronized (this ) {
308
+ stateLock .lock ();
309
+ try {
292
310
closelist = expiryList .purgeUntil (now );
293
311
for (HttpConnection c : closelist ) {
294
312
if (c instanceof PlainHttpConnection ) {
@@ -302,6 +320,8 @@ long purgeExpiredConnectionsAndReturnNextDeadline(Instant now) {
302
320
nextPurge = now .until (
303
321
expiryList .nextExpiryDeadline ().orElse (now ),
304
322
ChronoUnit .MILLIS );
323
+ } finally {
324
+ stateLock .unlock ();
305
325
}
306
326
closelist .forEach (this ::close );
307
327
return nextPurge ;
@@ -316,14 +336,17 @@ private void close(HttpConnection c) {
316
336
void stop () {
317
337
List <HttpConnection > closelist = Collections .emptyList ();
318
338
try {
319
- synchronized (this ) {
339
+ stateLock .lock ();
340
+ try {
320
341
stopped = true ;
321
342
closelist = expiryList .stream ()
322
343
.map (e -> e .connection )
323
344
.collect (Collectors .toList ());
324
345
expiryList .clear ();
325
346
plainPool .clear ();
326
347
sslPool .clear ();
348
+ } finally {
349
+ stateLock .unlock ();
327
350
}
328
351
} finally {
329
352
closelist .forEach (this ::close );
@@ -354,28 +377,25 @@ private static final class ExpiryList {
354
377
355
378
// A loosely accurate boolean whose value is computed
356
379
// at the end of each operation performed on ExpiryList;
357
- // Does not require synchronizing on the ConnectionPool.
380
+ // Does not require holding the ConnectionPool stateLock .
358
381
boolean purgeMaybeRequired () {
359
382
return mayContainEntries ;
360
383
}
361
384
362
385
// Returns the next expiry deadline
363
- // should only be called while holding a synchronization
364
- // lock on the ConnectionPool
386
+ // should only be called while holding the ConnectionPool stateLock.
365
387
Optional <Instant > nextExpiryDeadline () {
366
388
if (list .isEmpty ()) return Optional .empty ();
367
389
else return Optional .of (list .getLast ().expiry );
368
390
}
369
391
370
- // should only be called while holding a synchronization
371
- // lock on the ConnectionPool
392
+ // should only be called while holding the ConnectionPool stateLock.
372
393
HttpConnection removeOldest () {
373
394
ExpiryEntry entry = list .pollLast ();
374
395
return entry == null ? null : entry .connection ;
375
396
}
376
397
377
- // should only be called while holding a synchronization
378
- // lock on the ConnectionPool
398
+ // should only be called while holding the ConnectionPool stateLock.
379
399
void add (HttpConnection conn ) {
380
400
add (conn , Instant .now (), KEEP_ALIVE_TIMEOUT );
381
401
}
@@ -408,8 +428,7 @@ void add(HttpConnection conn, Instant now, long keepAlive) {
408
428
mayContainEntries = true ;
409
429
}
410
430
411
- // should only be called while holding a synchronization
412
- // lock on the ConnectionPool
431
+ // should only be called while holding the ConnectionPool stateLock.
413
432
void remove (HttpConnection c ) {
414
433
if (c == null || list .isEmpty ()) return ;
415
434
ListIterator <ExpiryEntry > li = list .listIterator ();
@@ -423,8 +442,7 @@ void remove(HttpConnection c) {
423
442
}
424
443
}
425
444
426
- // should only be called while holding a synchronization
427
- // lock on the ConnectionPool.
445
+ // should only be called while holding the ConnectionPool stateLock.
428
446
// Purge all elements whose deadline is before now (now included).
429
447
List <HttpConnection > purgeUntil (Instant now ) {
430
448
if (list .isEmpty ()) return Collections .emptyList ();
@@ -450,25 +468,22 @@ List<HttpConnection> purgeUntil(Instant now) {
450
468
return closelist ;
451
469
}
452
470
453
- // should only be called while holding a synchronization
454
- // lock on the ConnectionPool
471
+ // should only be called while holding the ConnectionPool stateLock.
455
472
java .util .stream .Stream <ExpiryEntry > stream () {
456
473
return list .stream ();
457
474
}
458
475
459
- // should only be called while holding a synchronization
460
- // lock on the ConnectionPool
476
+ // should only be called while holding the ConnectionPool stateLock.
461
477
void clear () {
462
478
list .clear ();
463
479
mayContainEntries = false ;
464
480
}
465
481
}
466
482
467
483
// Remove a connection from the pool.
468
- // should only be called while holding a synchronization
469
- // lock on the ConnectionPool
484
+ // should only be called while holding the ConnectionPool stateLock.
470
485
private void removeFromPool (HttpConnection c ) {
471
- assert Thread . holdsLock ( this );
486
+ assert stateLock . isHeldByCurrentThread ( );
472
487
if (c instanceof PlainHttpConnection ) {
473
488
removeFromPool (c , plainPool );
474
489
} else {
@@ -478,7 +493,16 @@ private void removeFromPool(HttpConnection c) {
478
493
}
479
494
480
495
// Used by tests
481
- synchronized boolean contains (HttpConnection c ) {
496
+ boolean contains (HttpConnection c ) {
497
+ stateLock .lock ();
498
+ try {
499
+ return contains0 (c );
500
+ } finally {
501
+ stateLock .unlock ();
502
+ }
503
+ }
504
+
505
+ private boolean contains0 (HttpConnection c ) {
482
506
final CacheKey key = c .cacheKey ();
483
507
List <HttpConnection > list ;
484
508
if ((list = plainPool .get (key )) != null ) {
@@ -494,9 +518,12 @@ void cleanup(HttpConnection c, Throwable error) {
494
518
if (debug .on ())
495
519
debug .log ("%s : ConnectionPool.cleanup(%s)" ,
496
520
String .valueOf (c .getConnectionFlow ()), error );
497
- synchronized (this ) {
521
+ stateLock .lock ();
522
+ try {
498
523
removeFromPool (c );
499
524
expiryList .remove (c );
525
+ } finally {
526
+ stateLock .unlock ();
500
527
}
501
528
c .close ();
502
529
}
0 commit comments