28
28
import java .nio .channels .Selector ;
29
29
import java .nio .channels .SocketChannel ;
30
30
import java .nio .channels .spi .SelectorProvider ;
31
- import java .util .Iterator ;
32
- import java .util .LinkedList ;
31
+ import java .util .concurrent .ConcurrentHashMap ;
32
+ import java .util .concurrent .ConcurrentLinkedDeque ;
33
+ import java .util .concurrent .atomic .AtomicBoolean ;
33
34
34
35
import org .apache .hadoop .util .Time ;
35
36
import org .slf4j .Logger ;
@@ -48,8 +49,6 @@ abstract class SocketIOWithTimeout {
48
49
private long timeout ;
49
50
private boolean closed = false ;
50
51
51
- private static SelectorPool selector = new SelectorPool ();
52
-
53
52
/* A timeout value of 0 implies wait for ever.
54
53
* We should have a value of timeout that implies zero wait.. i.e.
55
54
* read or write returns immediately.
@@ -154,7 +153,7 @@ int doIO(ByteBuffer buf, int ops) throws IOException {
154
153
//now wait for socket to be ready.
155
154
int count = 0 ;
156
155
try {
157
- count = selector .select (channel , ops , timeout );
156
+ count = SelectorPool .select (channel , ops , timeout );
158
157
} catch (IOException e ) { //unexpected IOException.
159
158
closed = true ;
160
159
throw e ;
@@ -200,7 +199,7 @@ static void connect(SocketChannel channel,
200
199
// we might have to call finishConnect() more than once
201
200
// for some channels (with user level protocols)
202
201
203
- int ret = selector .select (( SelectableChannel ) channel ,
202
+ int ret = SelectorPool .select (channel ,
204
203
SelectionKey .OP_CONNECT , timeoutLeft );
205
204
206
205
if (ret > 0 && channel .finishConnect ()) {
@@ -242,7 +241,7 @@ static void connect(SocketChannel channel,
242
241
*/
243
242
void waitForIO (int ops ) throws IOException {
244
243
245
- if (selector .select (channel , ops , timeout ) == 0 ) {
244
+ if (SelectorPool .select (channel , ops , timeout ) == 0 ) {
246
245
throw new SocketTimeoutException (timeoutExceptionString (channel , timeout ,
247
246
ops ));
248
247
}
@@ -280,12 +279,17 @@ private static String timeoutExceptionString(SelectableChannel channel,
280
279
* This maintains a pool of selectors. These selectors are closed
281
280
* once they are idle (unused) for a few seconds.
282
281
*/
283
- private static class SelectorPool {
282
+ private static final class SelectorPool {
284
283
285
- private static class SelectorInfo {
286
- Selector selector ;
287
- long lastActivityTime ;
288
- LinkedList <SelectorInfo > queue ;
284
+ private static final class SelectorInfo {
285
+ private final SelectorProvider provider ;
286
+ private final Selector selector ;
287
+ private long lastActivityTime ;
288
+
289
+ private SelectorInfo (SelectorProvider provider , Selector selector ) {
290
+ this .provider = provider ;
291
+ this .selector = selector ;
292
+ }
289
293
290
294
void close () {
291
295
if (selector != null ) {
@@ -298,16 +302,11 @@ void close() {
298
302
}
299
303
}
300
304
301
- private static class ProviderInfo {
302
- SelectorProvider provider ;
303
- LinkedList <SelectorInfo > queue ; // lifo
304
- ProviderInfo next ;
305
- }
305
+ private static ConcurrentHashMap <SelectorProvider , ConcurrentLinkedDeque
306
+ <SelectorInfo >> providerMap = new ConcurrentHashMap <>();
306
307
307
308
private static final long IDLE_TIMEOUT = 10 * 1000 ; // 10 seconds.
308
309
309
- private ProviderInfo providerList = null ;
310
-
311
310
/**
312
311
* Waits on the channel with the given timeout using one of the
313
312
* cached selectors. It also removes any cached selectors that are
@@ -319,7 +318,7 @@ private static class ProviderInfo {
319
318
* @return
320
319
* @throws IOException
321
320
*/
322
- int select (SelectableChannel channel , int ops , long timeout )
321
+ static int select (SelectableChannel channel , int ops , long timeout )
323
322
throws IOException {
324
323
325
324
SelectorInfo info = get (channel );
@@ -385,35 +384,18 @@ int select(SelectableChannel channel, int ops, long timeout)
385
384
* @return
386
385
* @throws IOException
387
386
*/
388
- private synchronized SelectorInfo get (SelectableChannel channel )
387
+ private static SelectorInfo get (SelectableChannel channel )
389
388
throws IOException {
390
- SelectorInfo selInfo = null ;
391
-
392
389
SelectorProvider provider = channel .provider ();
393
-
394
390
// pick the list : rarely there is more than one provider in use.
395
- ProviderInfo pList = providerList ;
396
- while (pList != null && pList .provider != provider ) {
397
- pList = pList .next ;
398
- }
399
- if (pList == null ) {
400
- //LOG.info("Creating new ProviderInfo : " + provider.toString());
401
- pList = new ProviderInfo ();
402
- pList .provider = provider ;
403
- pList .queue = new LinkedList <SelectorInfo >();
404
- pList .next = providerList ;
405
- providerList = pList ;
406
- }
407
-
408
- LinkedList <SelectorInfo > queue = pList .queue ;
409
-
410
- if (queue .isEmpty ()) {
391
+ ConcurrentLinkedDeque <SelectorInfo > infoQ = providerMap .computeIfAbsent (
392
+ provider , k -> new ConcurrentLinkedDeque <>());
393
+
394
+ SelectorInfo selInfo = infoQ .pollLast (); // last in first out
395
+ if (selInfo == null ) {
411
396
Selector selector = provider .openSelector ();
412
- selInfo = new SelectorInfo ();
413
- selInfo .selector = selector ;
414
- selInfo .queue = queue ;
415
- } else {
416
- selInfo = queue .removeLast ();
397
+ // selInfo will be put into infoQ after `#release()`
398
+ selInfo = new SelectorInfo (provider , selector );
417
399
}
418
400
419
401
trimIdleSelectors (Time .now ());
@@ -426,34 +408,39 @@ private synchronized SelectorInfo get(SelectableChannel channel)
426
408
*
427
409
* @param info
428
410
*/
429
- private synchronized void release (SelectorInfo info ) {
411
+ private static void release (SelectorInfo info ) {
430
412
long now = Time .now ();
431
413
trimIdleSelectors (now );
432
414
info .lastActivityTime = now ;
433
- info .queue .addLast (info );
415
+ // SelectorInfos in queue are sorted by lastActivityTime
416
+ providerMap .get (info .provider ).addLast (info );
434
417
}
435
418
419
+ private static AtomicBoolean trimming = new AtomicBoolean (false );
420
+
436
421
/**
437
422
* Closes selectors that are idle for IDLE_TIMEOUT (10 sec). It does not
438
423
* traverse the whole list, just over the one that have crossed
439
424
* the timeout.
440
425
*/
441
- private void trimIdleSelectors (long now ) {
426
+ private static void trimIdleSelectors (long now ) {
427
+ if (!trimming .compareAndSet (false , true )) {
428
+ return ;
429
+ }
430
+
442
431
long cutoff = now - IDLE_TIMEOUT ;
443
-
444
- for (ProviderInfo pList =providerList ; pList != null ; pList =pList .next ) {
445
- if (pList .queue .isEmpty ()) {
446
- continue ;
447
- }
448
- for (Iterator <SelectorInfo > it = pList .queue .iterator (); it .hasNext ();) {
449
- SelectorInfo info = it .next ();
450
- if (info .lastActivityTime > cutoff ) {
432
+ for (ConcurrentLinkedDeque <SelectorInfo > infoQ : providerMap .values ()) {
433
+ SelectorInfo oldest ;
434
+ while ((oldest = infoQ .peekFirst ()) != null ) {
435
+ if (oldest .lastActivityTime <= cutoff && infoQ .remove (oldest )) {
436
+ oldest .close ();
437
+ } else {
451
438
break ;
452
439
}
453
- it .remove ();
454
- info .close ();
455
440
}
456
441
}
442
+
443
+ trimming .set (false );
457
444
}
458
445
}
459
446
}
0 commit comments