class EPollArrayWrapper {
64 // EPOLL_EVENTS
65 static final int EPOLLIN = 0x001;
66
67 // opcodes
68 static final int EPOLL_CTL_ADD = 1;
69 static final int EPOLL_CTL_DEL = 2;
70 static final int EPOLL_CTL_MOD = 3;
71
72 // Miscellaneous constants
73 static final int SIZE_EPOLLEVENT = sizeofEPollEvent();
74 static final int EVENT_OFFSET = 0;
75 static final int DATA_OFFSET = offsetofData();
76 static final int FD_OFFSET = DATA_OFFSET;
77 static final int NUM_EPOLLEVENTS = Math.min(fdLimit(), 8192);
78
79 // Base address of the native pollArray
80 private final long pollArrayAddress;
81
82 // Set of "idle" channels
83 private final HashSet<SelChImpl> idleSet;
84
85 EPollArrayWrapper() {
86 // creates the epoll file descriptor
87 epfd = epollCreate();
88
89 // the epoll_event array passed to epoll_wait
90 int allocationSize = NUM_EPOLLEVENTS * SIZE_EPOLLEVENT;
91 pollArray = new AllocatedNativeObject(allocationSize, true);
92 pollArrayAddress = pollArray.address();
93
94 for (int i=0; i<NUM_EPOLLEVENTS; i++) {
95 putEventOps(i, 0);
96 putData(i, 0L);
97 }
98
99 // create idle set
100 idleSet = new HashSet<SelChImpl>();
101 }
102
103 // Used to update file description registrations
104 private static class Updator {
105 SelChImpl channel;
106 int opcode;
107 int events;
108 Updator(SelChImpl channel, int opcode, int events) {
109 this.channel = channel;
110 this.opcode = opcode;
111 this.events = events;
112 }
113 Updator(SelChImpl channel, int opcode) {
114 this(channel, opcode, 0);
115 }
116 }
117
118 private LinkedList<Updator> updateList = new LinkedList<Updator>();
119
120 // The epoll_event array for results from epoll_wait
121 private AllocatedNativeObject pollArray;
122
123 // The fd of the epoll driver
124 final int epfd;
125
126 // The fd of the interrupt line going out
127 int outgoingInterruptFD;
128
129 // The fd of the interrupt line coming in
130 int incomingInterruptFD;
131
132 // The index of the interrupt FD
133 int interruptedIndex;
134
135 // Number of updated pollfd entries
136 int updated;
137
138 void initInterrupt(int fd0, int fd1) {
139 outgoingInterruptFD = fd1;
140 incomingInterruptFD = fd0;
141 epollCtl(epfd, EPOLL_CTL_ADD, fd0, EPOLLIN);
142 }
143
144 void putEventOps(int i, int event) {
145 int offset = SIZE_EPOLLEVENT * i + EVENT_OFFSET;
146 pollArray.putInt(offset, event);
147 }
148
149 void putData(int i, long value) {
150 int offset = SIZE_EPOLLEVENT * i + DATA_OFFSET;
151 pollArray.putLong(offset, value);
152 }
153
154 void putDescriptor(int i, int fd) {
155 int offset = SIZE_EPOLLEVENT * i + FD_OFFSET;
156 pollArray.putInt(offset, fd);
157 }
158
159 int getEventOps(int i) {
160 int offset = SIZE_EPOLLEVENT * i + EVENT_OFFSET;
161 return pollArray.getInt(offset);
162 }
163
164 int getDescriptor(int i) {
165 int offset = SIZE_EPOLLEVENT * i + FD_OFFSET;
166 return pollArray.getInt(offset);
167 }
168
169 /**
170 * Update the events for a given channel.
171 */
172 void setInterest(SelChImpl channel, int mask) {
173 synchronized (updateList) {
174 // if the previous pending operation is to add this file descriptor
175 // to epoll then update its event set
176 if (updateList.size() > 0) {
177 Updator last = updateList.getLast();
178 if (last.channel == channel && last.opcode == EPOLL_CTL_ADD) {
179 last.events = mask;
180 return;
181 }
182 }
183
184 // update existing registration
185 updateList.add(new Updator(channel, EPOLL_CTL_MOD, mask));
186 }
187 }
188
189 /**
190 * Add a channel's file descriptor to epoll
191 */
192 void add(SelChImpl channel) {
193 synchronized (updateList) {
194 updateList.add(new Updator(channel, EPOLL_CTL_ADD));
195 }
196 }
197
198 /**
199 * Remove a channel's file descriptor from epoll
200 */
201 void release(SelChImpl channel) {
202 synchronized (updateList) {
203 // flush any pending updates
204 for (Iterator<Updator> it = updateList.iterator(); it.hasNext();) {
205 if (it.next().channel == channel) {
206 it.remove();
207 }
208 }
209
210 // remove from the idle set (if present)
211 idleSet.remove(channel);
212
213 // remove from epoll (if registered)
214 epollCtl(epfd, EPOLL_CTL_DEL, channel.getFDVal(), 0);
215 }
216 }
217
218 /**
219 * Close epoll file descriptor and free poll array
220 */
221 void closeEPollFD() throws IOException {
222 FileDispatcherImpl.closeIntFD(epfd);
223 pollArray.free();
224 }
225
226 int poll(long timeout) throws IOException {
227 updateRegistrations();
228 updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd);
229 for (int i=0; i<updated; i++) {
230 if (getDescriptor(i) == incomingInterruptFD) {
231 interruptedIndex = i;
232 interrupted = true;
233 break;
234 }
235 }
236 return updated;
237 }
238
239 /**
240 * Update the pending registrations.
241 */
242 void updateRegistrations() {
243 synchronized (updateList) {
244 Updator u = null;
245 while ((u = updateList.poll()) != null) {
246 SelChImpl ch = u.channel;
247 if (!ch.isOpen())
248 continue;
249
250 // if the events are 0 then file descriptor is put into "idle
251 // set" to prevent it being polled
252 if (u.events == 0) {
253 boolean added = idleSet.add(u.channel);
254 // if added to idle set then remove from epoll if registered
255 if (added && (u.opcode == EPOLL_CTL_MOD))
256 epollCtl(epfd, EPOLL_CTL_DEL, ch.getFDVal(), 0);
257 } else {
258 // events are specified. If file descriptor was in idle set
259 // it must be re-registered (by converting opcode to ADD)
260 boolean idle = false;
261 if (!idleSet.isEmpty())
262 idle = idleSet.remove(u.channel);
263 int opcode = (idle) ? EPOLL_CTL_ADD : u.opcode;
264 epollCtl(epfd, opcode, ch.getFDVal(), u.events);
265 }
266 }
267 }
268 }
269
270 // interrupt support
271 boolean interrupted = false;
272
273 public void interrupt() {
274 interrupt(outgoingInterruptFD);
275 }
276
277 public int interruptedIndex() {
278 return interruptedIndex;
279 }
280
281 boolean interrupted() {
282 return interrupted;
283 }
284
285 void clearInterrupted() {
286 interrupted = false;
287 }
288
289 static {
290 init();
291 }
292
293 private native int epollCreate();
294 private native void epollCtl(int epfd, int opcode, int fd, int events);
295 private native int epollWait(long pollAddress, int numfds, long timeout,
296 int epfd) throws IOException;
297 private static native int sizeofEPollEvent();
298 private static native int offsetofData();
299 private static native int fdLimit();
300 private static native void interrupt(int fd);
301 private static native void init();
302 }