blob: fc88e163f79c287a538620520e3040c8a925ea41 [file] [log] [blame]
tom6a62aa22014-09-25 09:10:12 -07001package org.onlab.nio;
2
3import org.slf4j.Logger;
4import org.slf4j.LoggerFactory;
5
6import java.io.IOException;
7import java.nio.channels.Selector;
8
9import static com.google.common.base.Preconditions.checkArgument;
10
11/**
12 * Abstraction of an I/O processing loop based on an NIO selector.
13 */
14public abstract class SelectorLoop implements Runnable {
15
16 protected final Logger log = LoggerFactory.getLogger(getClass());
17
18 /**
19 * Selector used by this loop to pace the I/O operations.
20 */
21 protected final Selector selector;
22
23 /**
24 * Selection operations timeout; specified in millis.
25 */
26 protected long selectTimeout;
27
28 /**
29 * Retains the error that caused the loop to exit prematurely.
30 */
31 private Throwable error;
32
33 // State indicator
34 private enum State { STARTING, STARTED, STOPPING, STOPPED };
35 private volatile State state = State.STOPPED;
36
37 /**
38 * Creates a new selector loop with the given selection timeout.
39 *
40 * @param selectTimeout selection timeout; specified in millis
41 * @throws IOException if the backing selector cannot be opened
42 */
43 public SelectorLoop(long selectTimeout) throws IOException {
44 checkArgument(selectTimeout > 0, "Timeout must be positive");
45 this.selectTimeout = selectTimeout;
46 this.selector = openSelector();
47 }
48
49 /**
50 * Opens a new selector for the use by the loop.
51 *
52 * @return newly open selector
53 * @throws IOException if the backing selector cannot be opened
54 */
55 protected Selector openSelector() throws IOException {
56 return Selector.open();
57 }
58
59 /**
60 * Indicates that the loop is marked to run.
61 */
62 protected boolean isRunning() {
63 return state == State.STARTED || state == State.STARTING;
64 }
65
66 /**
67 * Returns the error, if there was one, that caused the loop to terminate
68 * prematurely.
69 *
70 * @return error or null if there was none
71 */
72 public Throwable getError() {
73 return error;
74 }
75
76 /**
77 * Contains the body of the I/O selector loop.
78 *
79 * @throws IOException if an error is encountered while selecting I/O
80 */
81 protected abstract void loop() throws IOException;
82
83 @Override
84 public void run() {
85 error = null;
86 state = State.STARTING;
87 try {
88 loop();
89 } catch (Throwable e) {
90 error = e;
91 log.error("Loop aborted", e);
92 }
93 notifyDone();
94 }
95
96 /**
97 * Notifies observers waiting for loop to become ready.
98 */
99 protected synchronized void notifyReady() {
100 state = State.STARTED;
101 notifyAll();
102 }
103
104 /**
105 * Triggers loop shutdown.
106 */
107 public void shutdown() {
108 // Mark the loop as no longer running and wake up the selector.
109 state = State.STOPPING;
110 selector.wakeup();
111 }
112
113 /**
114 * Notifies observers waiting for loop to fully stop.
115 */
116 private synchronized void notifyDone() {
117 state = State.STOPPED;
118 notifyAll();
119 }
120
121}