blob: 4ee5e0617990e0e14d7908d36e8d1a15e6016951 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.nio.ch;
27
28import java.io.IOException;
29import java.util.LinkedList;
30import java.util.HashSet;
31
32/**
33 * Manipulates a native array of epoll_event structs on Linux:
34 *
35 * typedef union epoll_data {
36 * void *ptr;
37 * int fd;
38 * __uint32_t u32;
39 * __uint64_t u64;
40 * } epoll_data_t;
41 *
42 * struct epoll_event {
43 * __uint32_t events;
44 * epoll_data_t data;
45 * };
46 *
47 * The system call to wait for I/O events is epoll_wait(2). It populates an
48 * array of epoll_event structures that are passed to the call. The data
49 * member of the epoll_event structure contains the same data as was set
50 * when the file descriptor was registered to epoll via epoll_ctl(2). In
51 * this implementation we set data.fd to be the file descriptor that we
52 * register. That way, we have the file descriptor available when we
53 * process the events.
54 *
55 * All file descriptors registered with epoll have the POLLHUP and POLLERR
56 * events enabled even when registered with an event set of 0. To ensure
57 * that epoll_wait doesn't poll an idle file descriptor when the underlying
58 * connection is closed or reset then its registration is deleted from
59 * epoll (it will be re-added again if the event set is changed)
60 */
61
62class EPollArrayWrapper {
63 // EPOLL_EVENTS
64 static final int EPOLLIN = 0x001;
65
66 // opcodes
67 static final int EPOLL_CTL_ADD = 1;
68 static final int EPOLL_CTL_DEL = 2;
69 static final int EPOLL_CTL_MOD = 3;
70
71 // Miscellaneous constants
72 static final short SIZE_EPOLLEVENT = 12;
73 static final short EVENT_OFFSET = 0;
74 static final short DATA_OFFSET = 4;
75 static final short FD_OFFSET = 4;
76 static final int NUM_EPOLLEVENTS = Math.min(fdLimit(), 8192);
77
78 // Base address of the native pollArray
79 private final long pollArrayAddress;
80
81 // Set of "idle" file descriptors
82 private final HashSet<Integer> idleSet;
83
84 EPollArrayWrapper() {
85 // creates the epoll file descriptor
86 epfd = epollCreate();
87
88 // the epoll_event array passed to epoll_wait
89 int allocationSize = NUM_EPOLLEVENTS * SIZE_EPOLLEVENT;
90 pollArray = new AllocatedNativeObject(allocationSize, true);
91 pollArrayAddress = pollArray.address();
92
93 for (int i=0; i<NUM_EPOLLEVENTS; i++) {
94 putEventOps(i, 0);
95 putData(i, 0L);
96 }
97
98 // create idle set
99 idleSet = new HashSet<Integer>();
100 }
101
102 // Used to update file description registrations
103 private static class Updator {
104 int opcode;
105 int fd;
106 int events;
107 Updator(int opcode, int fd, int events) {
108 this.opcode = opcode;
109 this.fd = fd;
110 this.events = events;
111 }
112 }
113
114 private LinkedList<Updator> updateList = new LinkedList<Updator>();
115
116 // The epoll_event array for results from epoll_wait
117 private AllocatedNativeObject pollArray;
118
119 // The fd of the epoll driver
120 final int epfd;
121
122 // The fd of the interrupt line going out
123 int outgoingInterruptFD;
124
125 // The fd of the interrupt line coming in
126 int incomingInterruptFD;
127
128 // The index of the interrupt FD
129 int interruptedIndex;
130
131 // Number of updated pollfd entries
132 int updated;
133
134 void initInterrupt(int fd0, int fd1) {
135 outgoingInterruptFD = fd1;
136 incomingInterruptFD = fd0;
137 epollCtl(epfd, EPOLL_CTL_ADD, fd0, EPOLLIN);
138 }
139
140 void putEventOps(int i, int event) {
141 int offset = SIZE_EPOLLEVENT * i + EVENT_OFFSET;
142 pollArray.putInt(offset, event);
143 }
144
145 void putData(int i, long value) {
146 int offset = SIZE_EPOLLEVENT * i + DATA_OFFSET;
147 pollArray.putLong(offset, value);
148 }
149
150 void putDescriptor(int i, int fd) {
151 int offset = SIZE_EPOLLEVENT * i + FD_OFFSET;
152 pollArray.putInt(offset, fd);
153 }
154
155 int getEventOps(int i) {
156 int offset = SIZE_EPOLLEVENT * i + EVENT_OFFSET;
157 return pollArray.getInt(offset);
158 }
159
160 int getDescriptor(int i) {
161 int offset = SIZE_EPOLLEVENT * i + FD_OFFSET;
162 return pollArray.getInt(offset);
163 }
164
165 /**
166 * Update the events for a given file descriptor.
167 */
168 void setInterest(int fd, int mask) {
169 synchronized (updateList) {
170
171 // if the interest events are 0 then add to idle set, and delete
172 // from epoll if registered (or pending)
173 if (mask == 0) {
174 if (idleSet.add(fd)) {
175 updateList.add(new Updator(EPOLL_CTL_DEL, fd, 0));
176 }
177 return;
178 }
179
180 // if file descriptor is idle then add to epoll
181 if (!idleSet.isEmpty() && idleSet.remove(fd)) {
182 updateList.add(new Updator(EPOLL_CTL_ADD, fd, mask));
183 return;
184 }
185
186 // if the previous pending operation is to add this file descriptor
187 // to epoll then update its event set
188 if (updateList.size() > 0) {
189 Updator last = updateList.getLast();
190 if (last.fd == fd && last.opcode == EPOLL_CTL_ADD) {
191 last.events = mask;
192 return;
193 }
194 }
195
196 // update existing registration
197 updateList.add(new Updator(EPOLL_CTL_MOD, fd, mask));
198 }
199 }
200
201 /**
202 * Add a new file descriptor to epoll
203 */
204 void add(int fd) {
205 synchronized (updateList) {
206 updateList.add(new Updator(EPOLL_CTL_ADD, fd, 0));
207 }
208 }
209
210 /**
211 * Remove a file descriptor from epoll
212 */
213 void release(int fd) {
214 synchronized (updateList) {
215 // if file descriptor is idle then remove from idle set, otherwise
216 // delete from epoll
217 if (!idleSet.remove(fd)) {
218 updateList.add(new Updator(EPOLL_CTL_DEL, fd, 0));
219 }
220 }
221 }
222
223 /**
224 * Close epoll file descriptor and free poll array
225 */
226 void closeEPollFD() throws IOException {
227 FileDispatcher.closeIntFD(epfd);
228 pollArray.free();
229 }
230
231 int poll(long timeout) throws IOException {
232 updateRegistrations();
233 updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd);
234 for (int i=0; i<updated; i++) {
235 if (getDescriptor(i) == incomingInterruptFD) {
236 interruptedIndex = i;
237 interrupted = true;
238 break;
239 }
240 }
241 return updated;
242 }
243
244 /**
245 * Update the pending registrations.
246 */
247 void updateRegistrations() {
248 synchronized (updateList) {
249 Updator u = null;
250 while ((u = updateList.poll()) != null) {
251 epollCtl(epfd, u.opcode, u.fd, u.events);
252 }
253 }
254 }
255
256 // interrupt support
257 boolean interrupted = false;
258
259 public void interrupt() {
260 interrupt(outgoingInterruptFD);
261 }
262
263 public int interruptedIndex() {
264 return interruptedIndex;
265 }
266
267 boolean interrupted() {
268 return interrupted;
269 }
270
271 void clearInterrupted() {
272 interrupted = false;
273 }
274
275 static {
276 init();
277 }
278
279 private native int epollCreate();
280 private native void epollCtl(int epfd, int opcode, int fd, int events);
281 private native int epollWait(long pollAddress, int numfds, long timeout,
282 int epfd) throws IOException;
283 private static native int fdLimit();
284 private static native void interrupt(int fd);
285 private static native void init();
286}