1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.io;
20
21 import java.io.Closeable;
22 import java.io.IOException;
23 import java.net.ConnectException;
24 import java.net.Socket;
25 import java.net.SocketAddress;
26 import java.nio.channels.CancelledKeyException;
27 import java.nio.channels.ClosedChannelException;
28 import java.nio.channels.SelectionKey;
29 import java.nio.channels.Selector;
30 import java.nio.channels.ServerSocketChannel;
31 import java.nio.channels.SocketChannel;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Queue;
35 import java.util.Set;
36 import java.util.concurrent.ConcurrentLinkedQueue;
37 import java.util.concurrent.CountDownLatch;
38 import java.util.concurrent.TimeUnit;
39
40 import org.eclipse.jetty.util.ForkInvoker;
41 import org.eclipse.jetty.util.TypeUtil;
42 import org.eclipse.jetty.util.annotation.Name;
43 import org.eclipse.jetty.util.component.AbstractLifeCycle;
44 import org.eclipse.jetty.util.component.ContainerLifeCycle;
45 import org.eclipse.jetty.util.component.Dumpable;
46 import org.eclipse.jetty.util.log.Log;
47 import org.eclipse.jetty.util.log.Logger;
48
49
50
51
52
53
54
55 public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable
56 {
57 protected static final Logger LOG = Log.getLogger(SelectorManager.class);
58
59 private final ManagedSelector[] _selectors;
60 private volatile long _selectorIndex;
61
62 protected SelectorManager()
63 {
64 this((Runtime.getRuntime().availableProcessors() + 1) / 2);
65 }
66
67 protected SelectorManager(@Name(value="selectors") int selectors)
68 {
69 _selectors = new ManagedSelector[selectors];
70 }
71
72
73
74
75
76
77 protected abstract void execute(Runnable task);
78
79
80
81
82 public int getSelectorCount()
83 {
84 return _selectors.length;
85 }
86
87 private ManagedSelector chooseSelector()
88 {
89
90
91
92 long s = _selectorIndex++;
93 int index = (int)(s % getSelectorCount());
94 return _selectors[index];
95 }
96
97
98
99
100
101
102
103
104
105 public void connect(SocketChannel channel, Object attachment)
106 {
107 ManagedSelector set = chooseSelector();
108 set.submit(set.new Connect(channel, attachment));
109 }
110
111
112
113
114
115
116
117
118 public void accept(final SocketChannel channel)
119 {
120 final ManagedSelector selector = chooseSelector();
121 selector.submit(selector.new Accept(channel));
122 }
123
124 @Override
125 protected void doStart() throws Exception
126 {
127 super.doStart();
128 for (int i = 0; i < _selectors.length; i++)
129 {
130 ManagedSelector selector = newSelector(i);
131 _selectors[i] = selector;
132 selector.start();
133 execute(selector);
134 }
135 }
136
137
138
139
140
141
142
143 protected ManagedSelector newSelector(int id)
144 {
145 return new ManagedSelector(id);
146 }
147
148 @Override
149 protected void doStop() throws Exception
150 {
151 for (ManagedSelector selector : _selectors)
152 selector.stop();
153 super.doStop();
154 }
155
156
157
158
159
160
161 protected void endPointOpened(EndPoint endpoint)
162 {
163 endpoint.onOpen();
164 }
165
166
167
168
169
170
171 protected void endPointClosed(EndPoint endpoint)
172 {
173 endpoint.onClose();
174 }
175
176
177
178
179
180
181 public void connectionOpened(Connection connection)
182 {
183 try
184 {
185 connection.onOpen();
186 }
187 catch (Exception x)
188 {
189 LOG.info("Exception while notifying connection " + connection, x);
190 }
191 }
192
193
194
195
196
197
198 public void connectionClosed(Connection connection)
199 {
200 try
201 {
202 connection.onClose();
203 }
204 catch (Exception x)
205 {
206 LOG.info("Exception while notifying connection " + connection, x);
207 }
208 }
209
210
211
212
213
214
215
216
217
218 protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
219 {
220 LOG.warn(String.format("%s - %s", channel, attachment), ex);
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234
235 protected abstract EndPoint newEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selector, SelectionKey selectionKey) throws IOException;
236
237
238
239
240
241
242
243
244
245
246
247 public abstract Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment) throws IOException;
248
249 @Override
250 public String dump()
251 {
252 return ContainerLifeCycle.dump(this);
253 }
254
255 @Override
256 public void dump(Appendable out, String indent) throws IOException
257 {
258 ContainerLifeCycle.dumpObject(out, this);
259 ContainerLifeCycle.dump(out, indent, TypeUtil.asList(_selectors));
260 }
261
262
263
264
265
266
267
268 public class ManagedSelector extends AbstractLifeCycle implements Runnable, Dumpable
269 {
270 private final ForkInvoker<Runnable> invoker = new ManagedSelectorInvoker();
271 private final Queue<Runnable> _changes = new ConcurrentLinkedQueue<>();
272 private final int _id;
273 private Selector _selector;
274 private Thread _thread;
275 private boolean _needsWakeup = true;
276
277 public ManagedSelector(int id)
278 {
279 _id = id;
280 setStopTimeout(5000);
281 }
282
283 @Override
284 protected void doStart() throws Exception
285 {
286 super.doStart();
287 _selector = Selector.open();
288 }
289
290 @Override
291 protected void doStop() throws Exception
292 {
293 LOG.debug("Stopping {}", this);
294 Stop stop = new Stop();
295 submit(stop);
296 stop.await(getStopTimeout());
297 LOG.debug("Stopped {}", this);
298 }
299
300
301
302
303
304
305
306
307
308
309 public boolean submit(Runnable change)
310 {
311 return !invoker.invoke(change);
312 }
313
314 private void runChanges()
315 {
316 Runnable change;
317 while ((change = _changes.poll()) != null)
318 runChange(change);
319 }
320
321 protected void runChange(Runnable change)
322 {
323 LOG.debug("Running change {}", change);
324 change.run();
325 }
326
327 @Override
328 public void run()
329 {
330 _thread = Thread.currentThread();
331 String name = _thread.getName();
332 try
333 {
334 _thread.setName(name + "-selector-" + _id);
335 LOG.debug("Starting {} on {}", _thread, this);
336 while (isRunning())
337 select();
338 processChanges();
339 }
340 finally
341 {
342 LOG.debug("Stopped {} on {}", _thread, this);
343 _thread.setName(name);
344 }
345 }
346
347
348
349
350
351
352 public void select()
353 {
354 boolean debug = LOG.isDebugEnabled();
355 try
356 {
357 processChanges();
358
359 if (debug)
360 LOG.debug("Selector loop waiting on select");
361 int selected = _selector.select();
362 if (debug)
363 LOG.debug("Selector loop woken up from select, {}/{} selected", selected, _selector.keys().size());
364
365 _needsWakeup = false;
366
367 Set<SelectionKey> selectedKeys = _selector.selectedKeys();
368 for (SelectionKey key : selectedKeys)
369 {
370 if (key.isValid())
371 {
372 processKey(key);
373 }
374 else
375 {
376 if (debug)
377 LOG.debug("Selector loop ignoring invalid key for channel {}", key.channel());
378 Object attachment = key.attachment();
379 if (attachment instanceof EndPoint)
380 ((EndPoint)attachment).close();
381 }
382 }
383 selectedKeys.clear();
384 }
385 catch (Exception x)
386 {
387 if (isRunning())
388 LOG.warn(x);
389 else
390 LOG.ignore(x);
391 }
392 }
393
394 private void processChanges()
395 {
396 runChanges();
397
398
399
400
401 _needsWakeup = true;
402
403
404
405 runChanges();
406 }
407
408 private void processKey(SelectionKey key)
409 {
410 Object attachment = key.attachment();
411 try
412 {
413 if (attachment instanceof SelectableEndPoint)
414 {
415 ((SelectableEndPoint)attachment).onSelected();
416 }
417 else if (key.isConnectable())
418 {
419
420 SocketChannel channel = (SocketChannel)key.channel();
421 try
422 {
423 boolean connected = channel.finishConnect();
424 if (connected)
425 {
426 key.interestOps(0);
427 EndPoint endpoint = createEndPoint(channel, key);
428 key.attach(endpoint);
429 }
430 else
431 {
432 throw new ConnectException();
433 }
434 }
435 catch (Exception x)
436 {
437 connectionFailed(channel, x, attachment);
438 closeNoExceptions(channel);
439 }
440 }
441 else
442 {
443 throw new IllegalStateException();
444 }
445 }
446 catch (CancelledKeyException x)
447 {
448 LOG.debug("Ignoring cancelled key for channel {}", key.channel());
449 if (attachment instanceof EndPoint)
450 ((EndPoint)attachment).close();
451 }
452 catch (Exception x)
453 {
454 LOG.warn("Could not process key for channel " + key.channel(), x);
455 if (attachment instanceof EndPoint)
456 ((EndPoint)attachment).close();
457 }
458 }
459
460 private void closeNoExceptions(Closeable closeable)
461 {
462 try
463 {
464 closeable.close();
465 }
466 catch (IOException x)
467 {
468 LOG.ignore(x);
469 }
470 }
471
472 public SelectorManager getSelectorManager()
473 {
474 return SelectorManager.this;
475 }
476
477 public void wakeup()
478 {
479 _selector.wakeup();
480 }
481
482 private EndPoint createEndPoint(SocketChannel channel, SelectionKey selectionKey) throws IOException
483 {
484 EndPoint endPoint = newEndPoint(channel, this, selectionKey);
485 endPointOpened(endPoint);
486 Connection connection = newConnection(channel, endPoint, selectionKey.attachment());
487 endPoint.setConnection(connection);
488 connectionOpened(connection);
489 LOG.debug("Created {}", endPoint);
490 return endPoint;
491 }
492
493 public void destroyEndPoint(EndPoint endPoint)
494 {
495
496
497 LOG.debug("Destroyed {}", endPoint);
498 Connection connection = endPoint.getConnection();
499 if (connection != null)
500 connectionClosed(connection);
501 endPointClosed(endPoint);
502 }
503
504 @Override
505 public String dump()
506 {
507 return ContainerLifeCycle.dump(this);
508 }
509
510 @Override
511 public void dump(Appendable out, String indent) throws IOException
512 {
513 out.append(String.valueOf(this)).append(" id=").append(String.valueOf(_id)).append("\n");
514
515 Thread selecting = _thread;
516
517 Object where = "not selecting";
518 StackTraceElement[] trace = selecting == null ? null : selecting.getStackTrace();
519 if (trace != null)
520 {
521 for (StackTraceElement t : trace)
522 if (t.getClassName().startsWith("org.eclipse.jetty."))
523 {
524 where = t;
525 break;
526 }
527 }
528
529 Selector selector = _selector;
530 if (selector != null)
531 {
532 final ArrayList<Object> dump = new ArrayList<>(selector.keys().size() * 2);
533 dump.add(where);
534
535 DumpKeys dumpKeys = new DumpKeys(dump);
536 submit(dumpKeys);
537 dumpKeys.await(5, TimeUnit.SECONDS);
538
539 ContainerLifeCycle.dump(out, indent, dump);
540 }
541 }
542
543 public void dumpKeysState(List<Object> dumps)
544 {
545 Selector selector = _selector;
546 Set<SelectionKey> keys = selector.keys();
547 dumps.add(selector + " keys=" + keys.size());
548 for (SelectionKey key : keys)
549 {
550 if (key.isValid())
551 dumps.add(key.attachment() + " iOps=" + key.interestOps() + " rOps=" + key.readyOps());
552 else
553 dumps.add(key.attachment() + " iOps=-1 rOps=-1");
554 }
555 }
556
557 @Override
558 public String toString()
559 {
560 Selector selector = _selector;
561 return String.format("%s keys=%d selected=%d",
562 super.toString(),
563 selector != null && selector.isOpen() ? selector.keys().size() : -1,
564 selector != null && selector.isOpen() ? selector.selectedKeys().size() : -1);
565 }
566
567 private class ManagedSelectorInvoker extends ForkInvoker<Runnable>
568 {
569 private ManagedSelectorInvoker()
570 {
571 super(4);
572 }
573
574 @Override
575 protected boolean condition()
576 {
577 return Thread.currentThread() != _thread;
578 }
579
580 @Override
581 public void fork(Runnable change)
582 {
583 _changes.offer(change);
584 LOG.debug("Queued change {}", change);
585 boolean wakeup = _needsWakeup;
586 if (wakeup)
587 wakeup();
588 }
589
590 @Override
591 public void call(Runnable change)
592 {
593 LOG.debug("Submitted change {}", change);
594 runChanges();
595 runChange(change);
596 }
597 }
598
599 private class DumpKeys implements Runnable
600 {
601 private final CountDownLatch latch = new CountDownLatch(1);
602 private final List<Object> _dumps;
603
604 private DumpKeys(List<Object> dumps)
605 {
606 this._dumps = dumps;
607 }
608
609 @Override
610 public void run()
611 {
612 dumpKeysState(_dumps);
613 latch.countDown();
614 }
615
616 public boolean await(long timeout, TimeUnit unit)
617 {
618 try
619 {
620 return latch.await(timeout, unit);
621 }
622 catch (InterruptedException x)
623 {
624 return false;
625 }
626 }
627 }
628
629 private class Accept implements Runnable
630 {
631 private final SocketChannel _channel;
632
633 public Accept(SocketChannel channel)
634 {
635 this._channel = channel;
636 }
637
638 @Override
639 public void run()
640 {
641 try
642 {
643 SelectionKey key = _channel.register(_selector, 0, null);
644 EndPoint endpoint = createEndPoint(_channel, key);
645 key.attach(endpoint);
646 }
647 catch (IOException x)
648 {
649 LOG.debug(x);
650 }
651 }
652 }
653
654 private class Connect implements Runnable
655 {
656 private final SocketChannel channel;
657 private final Object attachment;
658
659 public Connect(SocketChannel channel, Object attachment)
660 {
661 this.channel = channel;
662 this.attachment = attachment;
663 }
664
665 @Override
666 public void run()
667 {
668 try
669 {
670 channel.register(_selector, SelectionKey.OP_CONNECT, attachment);
671 }
672 catch (ClosedChannelException x)
673 {
674 LOG.debug(x);
675 }
676 }
677 }
678
679 private class Stop implements Runnable
680 {
681 private final CountDownLatch latch = new CountDownLatch(1);
682
683 @Override
684 public void run()
685 {
686 try
687 {
688 for (SelectionKey key : _selector.keys())
689 {
690 Object attachment = key.attachment();
691 if (attachment instanceof EndPoint)
692 {
693 EndPointCloser closer = new EndPointCloser((EndPoint)attachment);
694 execute(closer);
695
696
697
698
699 closer.await(getStopTimeout());
700 }
701 }
702
703 closeNoExceptions(_selector);
704 }
705 finally
706 {
707 latch.countDown();
708 }
709 }
710
711 public boolean await(long timeout)
712 {
713 try
714 {
715 return latch.await(timeout, TimeUnit.MILLISECONDS);
716 }
717 catch (InterruptedException x)
718 {
719 return false;
720 }
721 }
722 }
723
724 private class EndPointCloser implements Runnable
725 {
726 private final CountDownLatch latch = new CountDownLatch(1);
727 private final EndPoint endPoint;
728
729 private EndPointCloser(EndPoint endPoint)
730 {
731 this.endPoint = endPoint;
732 }
733
734 @Override
735 public void run()
736 {
737 try
738 {
739 endPoint.getConnection().close();
740 }
741 finally
742 {
743 latch.countDown();
744 }
745 }
746
747 private boolean await(long timeout)
748 {
749 try
750 {
751 return latch.await(timeout, TimeUnit.MILLISECONDS);
752 }
753 catch (InterruptedException x)
754 {
755 return false;
756 }
757 }
758 }
759 }
760
761
762
763
764
765 public interface SelectableEndPoint extends EndPoint
766 {
767
768
769
770
771 void onSelected();
772 }
773 }