001 /**
002 * Copyright (C) 2009-2013 Barchart, Inc. <http://www.barchart.com/>
003 *
004 * All rights reserved. Licensed under the OSI BSD License.
005 *
006 * http://www.opensource.org/licenses/bsd-license.php
007 */
008 package com.barchart.udt.nio;
009
010 import java.nio.channels.CancelledKeyException;
011 import java.nio.channels.SelectableChannel;
012 import java.nio.channels.SelectionKey;
013
014 import org.slf4j.Logger;
015 import org.slf4j.LoggerFactory;
016
017 import com.barchart.udt.EpollUDT;
018 import com.barchart.udt.EpollUDT.Opt;
019 import com.barchart.udt.ExceptionUDT;
020 import com.barchart.udt.OptionUDT;
021 import com.barchart.udt.SocketUDT;
022 import com.barchart.udt.StatusUDT;
023
024 /**
025 * UDT selection key implementation.
026 */
027 public class SelectionKeyUDT extends SelectionKey implements
028 Comparable<SelectionKeyUDT> {
029
030 /**
031 * JDK interest to Epoll READ mapping.
032 */
033 protected static final int HAS_READ = OP_ACCEPT | OP_READ;
034
035 /**
036 * JDK interest to Epoll WRITE mapping.
037 */
038 protected static final int HAS_WRITE = OP_CONNECT | OP_WRITE;
039
040 protected static final Logger log = LoggerFactory
041 .getLogger(SelectionKeyUDT.class);
042
043 /**
044 * Convert select options : from jdk into epoll.
045 */
046 protected static Opt from(final int interestOps) {
047
048 final boolean hasRead = (interestOps & HAS_READ) != 0;
049 final boolean hasWrite = (interestOps & HAS_WRITE) != 0;
050
051 if (hasRead && hasWrite) {
052 return Opt.ALL;
053 }
054
055 if (hasRead) {
056 return Opt.ERROR_READ;
057 }
058
059 if (hasWrite) {
060 return Opt.ERROR_WRITE;
061 }
062
063 return Opt.ERROR;
064
065 }
066
067 /**
068 * Render select options.
069 */
070 public static final String toStringOps(final int selectOps) {
071 final char A = (OP_ACCEPT & selectOps) != 0 ? 'A' : '-';
072 final char C = (OP_CONNECT & selectOps) != 0 ? 'C' : '-';
073 final char R = (OP_READ & selectOps) != 0 ? 'R' : '-';
074 final char W = (OP_WRITE & selectOps) != 0 ? 'W' : '-';
075 return String.format("%c%c%c%c", A, C, R, W);
076 }
077
078 /**
079 * Channel bound to the key.
080 */
081 private final ChannelUDT channelUDT;
082
083 /**
084 * Requested interest in epoll format.
085 */
086 private volatile Opt epollOpt;
087
088 /**
089 * Requested interest in JDK format.
090 */
091 private volatile int interestOps;
092
093 /**
094 * Key validity state. Key is valid when created, and invalid when canceled.
095 */
096 private volatile boolean isValid;
097
098 /**
099 * Reported ready interest.
100 */
101 private volatile int readyOps;
102
103 /**
104 * Correlation index for {@link #doRead(int)} vs {@link #doWrite(int)}
105 */
106 private volatile int resultIndex;
107
108 /**
109 * Selector bound to the key.
110 */
111 private final SelectorUDT selectorUDT;
112
113 protected SelectionKeyUDT( //
114 final SelectorUDT selectorUDT, //
115 final ChannelUDT channelUDT, //
116 final Object attachment //
117 ) {
118
119 super.attach(attachment);
120
121 this.selectorUDT = selectorUDT;
122 this.channelUDT = channelUDT;
123
124 makeValid(true);
125
126 }
127
128 /**
129 * Ensure key is NOT canceled.
130 */
131 protected void assertValidKey() throws CancelledKeyException {
132 if (isValid()) {
133 return;
134 }
135 throw new CancelledKeyException();
136 }
137
138 /**
139 * Ensure only permitted interest mask bits are present.
140 */
141 protected void assertValidOps(final int interestOps) {
142 if ((interestOps & ~(channel().validOps())) != 0) {
143 throw new IllegalArgumentException("invalid interestOps="
144 + interestOps);
145 }
146 }
147
148 @Override
149 public void cancel() {
150 if (isValid()) {
151 selector().cancel(this);
152 }
153 }
154
155 @Override
156 public SelectableChannel channel() {
157 return (SelectableChannel) channelUDT;
158 }
159
160 /**
161 * Underlying UDT channel.
162 */
163 protected ChannelUDT channelUDT() {
164 return channelUDT;
165 }
166
167 @Override
168 public int compareTo(final SelectionKeyUDT that) {
169 final int thisId = this.socketId();
170 final int thatId = that.socketId();
171 if (thisId > thatId) {
172 return +1;
173 }
174 if (thisId < thatId) {
175 return -1;
176 }
177 return 0;
178 }
179
180 /**
181 * Apply READ readiness according to {@link KindUDT} channel role.
182 * <p>
183 * Note: {@link #doRead(int)} is invoked before {@link #doWrite(int)}
184 * <p>
185 * Sockets with exceptions are returned to both read and write sets.
186 *
187 * @return Should report ready-state change?
188 */
189 protected boolean doRead(final int resultIndex) {
190
191 int readyOps = 0;
192 final int interestOps = this.interestOps;
193
194 /** Store read/write verifier. */
195 this.resultIndex = resultIndex;
196
197 try {
198
199 /** Check error report. */
200 if (!epollOpt.hasRead()) {
201 if (isSocketBroken()) {
202 readyOps = channel().validOps();
203 return true;
204 } else {
205 logError("Unexpected error report.");
206 return false;
207 }
208 }
209
210 switch (kindUDT()) {
211 case ACCEPTOR:
212 if ((interestOps & OP_ACCEPT) != 0) {
213 readyOps = OP_ACCEPT;
214 return true;
215 } else {
216 logError("Ready to ACCEPT while not interested.");
217 return false;
218 }
219 case CONNECTOR:
220 case RENDEZVOUS:
221 if ((interestOps & OP_READ) != 0) {
222 readyOps = OP_READ;
223 return true;
224 } else {
225 logError("Ready to READ while not interested.");
226 return false;
227 }
228 default:
229 logError("Wrong kind.");
230 return false;
231 }
232
233 } finally {
234
235 this.readyOps = readyOps;
236
237 }
238
239 }
240
241 /**
242 * Apply WRITE readiness according to {@link KindUDT} channel role.
243 * <p>
244 * Note: {@link #doRead(int)} is invoked before {@link #doWrite(int)}
245 * <p>
246 * Sockets with exceptions are returned to both read and write sets.
247 *
248 * @return Should report ready-state change?
249 */
250 protected boolean doWrite(final int resultIndex) {
251
252 int readyOps = 0;
253 final int interestOps = this.interestOps;
254
255 /** Verify read/write relationship. */
256 final boolean hadReadBeforeWrite = this.resultIndex == resultIndex;
257
258 try {
259
260 /** Check error report. */
261 if (!epollOpt.hasWrite()) {
262 if (isSocketBroken()) {
263 readyOps = channel().validOps();
264 return true;
265 } else {
266 logError("Unexpected error report.");
267 return false;
268 }
269 }
270
271 switch (kindUDT()) {
272 case ACCEPTOR:
273 logError("Ready to WRITE for acceptor.");
274 return false;
275 case CONNECTOR:
276 case RENDEZVOUS:
277 if (channelUDT().isConnectFinished()) {
278 if ((interestOps & OP_WRITE) != 0) {
279 readyOps = OP_WRITE;
280 return true;
281 } else {
282 logError("Ready to WRITE when not insterested.");
283 return false;
284 }
285 } else {
286 if ((interestOps & OP_CONNECT) != 0) {
287 readyOps = OP_CONNECT;
288 return true;
289 } else {
290 logError("Ready to CONNECT when not interested.");
291 return false;
292 }
293 }
294 default:
295 logError("Wrong kind.");
296 return false;
297 }
298
299 } finally {
300 if (hadReadBeforeWrite) {
301 this.readyOps |= readyOps;
302 } else {
303 this.readyOps = readyOps;
304 }
305 }
306
307 }
308
309 /**
310 * Requested interest in epoll format.
311 */
312 protected Opt epollOpt() {
313 return epollOpt;
314 }
315
316 /**
317 * Epoll bound to this key.
318 */
319 protected EpollUDT epollUDT() {
320 return selector().epollUDT();
321 }
322
323 /**
324 * Key equality based on socket-id.
325 */
326 @Override
327 public boolean equals(final Object otherKey) {
328 if (otherKey instanceof SelectionKeyUDT) {
329 final SelectionKeyUDT other = (SelectionKeyUDT) otherKey;
330 return other.socketId() == this.socketId();
331 }
332 return false;
333 }
334
335 /**
336 * Check socket error condition.
337 */
338 boolean hasError() throws ExceptionUDT {
339 final int code = socketUDT().getOption(OptionUDT.Epoll_Event_Mask);
340 return EpollUDT.Opt.from(code).hasError();
341 }
342
343 /**
344 * Key hach code based on socket-id.
345 */
346 @Override
347 public int hashCode() {
348 return socketId();
349 }
350
351 @Override
352 public int interestOps() {
353 return interestOps;
354 }
355
356 @Override
357 public SelectionKey interestOps(final int interestOps) {
358
359 assertValidKey();
360 assertValidOps(interestOps);
361
362 try {
363
364 final Opt epollNew = from(interestOps);
365
366 if (epollNew != epollOpt) {
367
368 if (Opt.ERROR == epollNew) {
369 epollUDT().remove(socketUDT());
370 } else {
371 epollUDT().remove(socketUDT());
372 epollUDT().add(socketUDT(), epollNew);
373 }
374
375 epollOpt = epollNew;
376
377 }
378
379 } catch (final Exception e) {
380
381 log.error("epoll udpate failure", e);
382
383 } finally {
384
385 this.interestOps = interestOps;
386
387 }
388
389 return this;
390
391 }
392
393 /**
394 * Check socket termination status.
395 *
396 * @return true if status is {@link StatusUDT#BROKEN} or worse
397 */
398 protected boolean isSocketBroken() {
399 switch (socketUDT().status()) {
400 case INIT:
401 case OPENED:
402 case LISTENING:
403 case CONNECTING:
404 case CONNECTED:
405 return false;
406 case BROKEN:
407 case CLOSING:
408 case CLOSED:
409 case NONEXIST:
410 return true;
411 default:
412 logError("Unknown socket status.");
413 return true;
414 }
415 }
416
417 @Override
418 public boolean isValid() {
419 return isValid;
420 }
421
422 /**
423 * Channel role.
424 */
425 protected KindUDT kindUDT() {
426 return channelUDT.kindUDT();
427 }
428
429 /**
430 * Key processing logic error logger.
431 */
432 protected void logError(final String comment) {
433
434 final String message = "logic error : \n\t" + this;
435
436 log.warn(message, new Exception("" + comment));
437
438 }
439
440 /**
441 * Change socket registration with epoll, and change key validity status.
442 */
443 protected void makeValid(final boolean isValid) {
444 try {
445 if (isValid) {
446 epollOpt = Opt.ERROR;
447 epollUDT().add(socketUDT(), epollOpt);
448 } else {
449 epollUDT().remove(socketUDT());
450 }
451 } catch (final Throwable e) {
452 log.error("Epoll failure.", e);
453 } finally {
454 this.isValid = isValid;
455 }
456 }
457
458 @Override
459 public int readyOps() {
460 return readyOps;
461 }
462
463 protected void readyOps(final int ops) {
464 readyOps = ops;
465 }
466
467 @Override
468 public SelectorUDT selector() {
469 return selectorUDT;
470 }
471
472 /**
473 * Id of a socket bound to this key.
474 */
475 protected int socketId() {
476 return socketUDT().id();
477 }
478
479 /**
480 * Socket bound to this key.
481 */
482 protected SocketUDT socketUDT() {
483 return channelUDT.socketUDT();
484 }
485
486 @Override
487 public String toString() {
488
489 return String
490 .format("[id: 0x%08x] poll=%s ready=%s inter=%s %s %s %s bind=%s:%s peer=%s:%s", //
491 socketUDT().id(), //
492 epollOpt, //
493 toStringOps(readyOps), //
494 toStringOps(interestOps), //
495 channelUDT.typeUDT(), //
496 channelUDT.kindUDT(), //
497 socketUDT().status(), //
498 socketUDT().getLocalInetAddress(), //
499 socketUDT().getLocalInetPort(), //
500 socketUDT().getRemoteInetAddress(), //
501 socketUDT().getRemoteInetPort() //
502 );
503
504 }
505
506 }