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;
009
010 import org.slf4j.Logger;
011 import org.slf4j.LoggerFactory;
012
013 /**
014 * UDT Epoll Manager
015 *
016 * @see <a href="http://en.wikipedia.org/wiki/Epoll">Epoll</a>
017 * @see <a href="http://udt.sourceforge.net/udt4/doc/epoll.htm">UDT Epoll</a>
018 */
019 public class EpollUDT {
020
021 /**
022 * poll interest option mask
023 * <p>
024 * see udt.h enum - EPOLLOpt
025 *
026 * <pre>
027 * UDT_EPOLL_IN = 0x1,
028 * UDT_EPOLL_OUT = 0x4,
029 * UDT_EPOLL_ERR = 0x8
030 * </pre>
031 *
032 * this is subset adapted to jdk select pattern
033 */
034 public static enum Opt {
035
036 /**
037 * not interested
038 */
039 NONE(0x0), //
040
041 /**
042 * UDT_EPOLL_IN : interested in read
043 */
044 READ(0x1), //
045
046 /**
047 * UDT_EPOLL_OUT: interested in write
048 */
049 WRITE(0x4), //
050
051 /**
052 * UDT_EPOLL_ERR: interested in exceptions
053 */
054 ERROR(0x8), //
055
056 BOTH(WRITE.code | READ.code), //
057
058 ERROR_READ(ERROR.code | READ.code), //
059
060 ERROR_WRITE(ERROR.code | WRITE.code), //
061
062 ALL(ERROR.code | WRITE.code | READ.code), //
063
064 UNKNOWN(-1);
065
066 ;
067
068 private static final Opt[] ENUM_VALS = Opt.values();
069
070 public static Opt from(final int code) {
071 for (final Opt known : ENUM_VALS) {
072 if (known.code == code) {
073 return known;
074 }
075 }
076 return UNKNOWN;
077 }
078
079 /**
080 * poll event mask;
081 * <p>
082 * used for both requesting interest and reporting readiness
083 */
084 public final int code;
085
086 Opt(final int code) {
087 this.code = code;
088 }
089
090 public boolean hasError() {
091 return (code & ERROR.code) != 0;
092 }
093
094 public boolean hasRead() {
095 return (code & READ.code) != 0;
096 }
097
098 public boolean hasWrite() {
099 return (code & WRITE.code) != 0;
100 }
101
102 /**
103 * Non-empty mask of 3 parts.
104 */
105 public boolean isValidInterestRequest() {
106 switch (this) {
107 case NONE:
108 case READ:
109 case WRITE:
110 case ERROR:
111 case BOTH:
112 case ERROR_WRITE:
113 case ERROR_READ:
114 case ALL:
115 return true;
116 default:
117 return false;
118 }
119 }
120
121 }
122
123 protected static final Logger log = LoggerFactory.getLogger(EpollUDT.class);
124
125 protected final int id;
126
127 protected volatile boolean isActive;
128
129 /**
130 * place holder socket to work around logic in epoll.h CEPoll::wait() which
131 * expects at least one socket being monitored with non empty interest
132 */
133 private final SocketUDT socketUDT;
134
135 /**
136 * allocate poll
137 */
138 public EpollUDT() throws ExceptionUDT {
139
140 id = SocketUDT.epollCreate0();
141 isActive = true;
142
143 socketUDT = new SocketUDT(TypeUDT.DATAGRAM);
144 SocketUDT.epollAdd0(id, socketUDT.id(), Opt.BOTH.code);
145
146 log.debug("ep {} create", id());
147
148 }
149
150 /**
151 * deallocate poll; called on {@link #finalize()}
152 */
153 public void destroy() throws ExceptionUDT {
154
155 SocketUDT.epollRemove0(id(), socketUDT.id());
156 socketUDT.close();
157
158 isActive = false;
159 SocketUDT.epollRelease0(id());
160
161 log.debug("ep {} delete", id());
162
163 }
164
165 /**
166 * poll descriptor id
167 */
168 public int id() {
169 return id;
170 }
171
172 /**
173 * poll becomes active after instance creation and inactive after
174 * {@link #destroy()}
175 */
176 public boolean isActive() {
177 return isActive;
178 }
179
180 /**
181 * deallocate poll
182 * <p>
183 * NOTE: catch all exceptions; else prevents GC
184 * <p>
185 * NOTE: do not leak "this" references; else prevents GC
186 */
187 @Override
188 protected void finalize() {
189 try {
190 destroy();
191 super.finalize();
192 } catch (final Throwable e) {
193 log.error("failed to destroy id=" + id(), e);
194 }
195 }
196
197 /**
198 * register socket into event processing poll
199 */
200 public void add(final SocketUDT socket, final Opt option)
201 throws ExceptionUDT {
202
203 log.debug("ep {} add {} {}", id(), socket, option);
204
205 // assert option.isValidInterestRequest();
206
207 SocketUDT.epollAdd0(id(), socket.id(), option.code);
208
209 }
210
211 /**
212 * unregister socket from event processing poll
213 */
214 public void remove(final SocketUDT socket) throws ExceptionUDT {
215
216 log.debug("ep {} rem {}", id(), socket);
217
218 SocketUDT.epollRemove0(id(), socket.id());
219
220 }
221
222 /**
223 * update existing poll/socket registration with changed interest
224 */
225 public void update(final SocketUDT socket, final Opt option)
226 throws ExceptionUDT {
227
228 log.debug("ep {} mod {} {}", id(), socket, option);
229
230 assert option.isValidInterestRequest();
231
232 SocketUDT.epollUpdate0(id(), socket.id(), option.code);
233
234 }
235
236 /** report current poll/socket readiness */
237 public Opt verify(final SocketUDT socket) throws ExceptionUDT {
238
239 final int code = SocketUDT.epollVerify0(id(), socket.id());
240
241 return Opt.from(code);
242
243 }
244
245 }