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    }