blob: a74703a493303c7ebd507d482437a43ad1a3e92d [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;
tom74d49652014-09-25 23:48:46 -070010import static java.lang.System.currentTimeMillis;
tom6a62aa22014-09-25 09:10:12 -070011
12/**
13 * Abstraction of an I/O processing loop based on an NIO selector.
14 */
15public abstract class SelectorLoop implements Runnable {
16
17 protected final Logger log = LoggerFactory.getLogger(getClass());
18
19 /**
20 * Selector used by this loop to pace the I/O operations.
21 */
22 protected final Selector selector;
23
24 /**
25 * Selection operations timeout; specified in millis.
26 */
27 protected long selectTimeout;
28
29 /**
30 * Retains the error that caused the loop to exit prematurely.
31 */
32 private Throwable error;
33
34 // State indicator
35 private enum State { STARTING, STARTED, STOPPING, STOPPED };
36 private volatile State state = State.STOPPED;
37
38 /**
39 * Creates a new selector loop with the given selection timeout.
40 *
41 * @param selectTimeout selection timeout; specified in millis
42 * @throws IOException if the backing selector cannot be opened
43 */
44 public SelectorLoop(long selectTimeout) throws IOException {
45 checkArgument(selectTimeout > 0, "Timeout must be positive");
46 this.selectTimeout = selectTimeout;
47 this.selector = openSelector();
48 }
49
50 /**
51 * Opens a new selector for the use by the loop.
52 *
53 * @return newly open selector
54 * @throws IOException if the backing selector cannot be opened
55 */
56 protected Selector openSelector() throws IOException {
57 return Selector.open();
58 }
59
60 /**
61 * Indicates that the loop is marked to run.
62 */
63 protected boolean isRunning() {
64 return state == State.STARTED || state == State.STARTING;
65 }
66
67 /**
68 * Returns the error, if there was one, that caused the loop to terminate
69 * prematurely.
70 *
71 * @return error or null if there was none
72 */
73 public Throwable getError() {
74 return error;
75 }
76
77 /**
78 * Contains the body of the I/O selector loop.
79 *
80 * @throws IOException if an error is encountered while selecting I/O
81 */
82 protected abstract void loop() throws IOException;
83
84 @Override
85 public void run() {
86 error = null;
87 state = State.STARTING;
88 try {
89 loop();
90 } catch (Throwable e) {
91 error = e;
92 log.error("Loop aborted", e);
93 }
94 notifyDone();
95 }
96
97 /**
98 * Notifies observers waiting for loop to become ready.
99 */
100 protected synchronized void notifyReady() {
101 state = State.STARTED;
102 notifyAll();
103 }
104
105 /**
106 * Triggers loop shutdown.
107 */
108 public void shutdown() {
109 // Mark the loop as no longer running and wake up the selector.
110 state = State.STOPPING;
111 selector.wakeup();
112 }
113
114 /**
115 * Notifies observers waiting for loop to fully stop.
116 */
117 private synchronized void notifyDone() {
118 state = State.STOPPED;
119 notifyAll();
120 }
121
tom74d49652014-09-25 23:48:46 -0700122 /**
123 * Waits for the loop execution to start.
124 *
125 * @param timeout number of milliseconds to wait
126 * @return true if loop started in time
127 */
128 public final synchronized boolean awaitStart(long timeout) {
129 long max = currentTimeMillis() + timeout;
130 while (state != State.STARTED && (currentTimeMillis() < max)) {
131 try {
132 wait(timeout);
133 } catch (InterruptedException e) {
134 throw new RuntimeException("Interrupted", e);
135 }
136 }
137 return state == State.STARTED;
138 }
139
140 /**
141 * Waits for the loop execution to stop.
142 *
143 * @param timeout number of milliseconds to wait
144 * @return true if loop finished in time
145 */
146 public final synchronized boolean awaitStop(long timeout) {
147 long max = currentTimeMillis() + timeout;
148 while (state != State.STOPPED && (currentTimeMillis() < max)) {
149 try {
150 wait(timeout);
151 } catch (InterruptedException e) {
152 throw new RuntimeException("Interrupted", e);
153 }
154 }
155 return state == State.STOPPED;
156 }
157
158
tom6a62aa22014-09-25 09:10:12 -0700159}