1 /*
2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * KQueueSelectorImpl.java
28 * Implementation of Selector using FreeBSD / Mac OS X kqueues
29 * Derived from Sun's DevPollSelectorImpl
30 */
31
32 package sun.nio.ch;
33
34 import java.io.IOException;
35 import java.io.FileDescriptor;
36 import java.nio.channels.*;
37 import java.nio.channels.spi.*;
38 import java.util.*;
39
40 class KQueueSelectorImpl
41 extends SelectorImpl
42 {
43 // File descriptors used for interrupt
44 protected int fd0;
45 protected int fd1;
46
47 // The kqueue manipulator
48 KQueueArrayWrapper kqueueWrapper;
49
50 // Count of registered descriptors (including interrupt)
51 private int totalChannels;
52
53 // Map from a file descriptor to an entry containing the selection key
54 private HashMap<Integer,MapEntry> fdMap;
55
56 // True if this Selector has been closed
57 private boolean closed = false;
58
59 // Lock for interrupt triggering and clearing
60 private Object interruptLock = new Object();
61 private boolean interruptTriggered = false;
62
63 // used by updateSelectedKeys to handle cases where the same file
64 // descriptor is polled by more than one filter
65 private long updateCount;
66
67 // Used to map file descriptors to a selection key and "update count"
68 // (see updateSelectedKeys for usage).
69 private static class MapEntry {
70 SelectionKeyImpl ski;
71 long updateCount;
72 MapEntry(SelectionKeyImpl ski) {
73 this.ski = ski;
74 }
75 }
76
77 /**
78 * Package private constructor called by factory method in
79 * the abstract superclass Selector.
80 */
81 KQueueSelectorImpl(SelectorProvider sp) {
82 super(sp);
83 long fds = IOUtil.makePipe(false);
84 fd0 = (int)(fds >>> 32);
85 fd1 = (int)fds;
86 try {
87 kqueueWrapper = new KQueueArrayWrapper();
88 kqueueWrapper.initInterrupt(fd0, fd1);
89 fdMap = new HashMap<>();
90 totalChannels = 1;
91 } catch (Throwable t) {
92 try {
93 FileDispatcherImpl.closeIntFD(fd0);
94 } catch (IOException ioe0) {
95 t.addSuppressed(ioe0);
96 }
97 try {
98 FileDispatcherImpl.closeIntFD(fd1);
99 } catch (IOException ioe1) {
100 t.addSuppressed(ioe1);
101 }
102 throw t;
103 }
104 }
105
106
107 protected int doSelect(long timeout)
108 throws IOException
109 {
110 int entries = 0;
111 if (closed)
112 throw new ClosedSelectorException();
113 processDeregisterQueue();
114 try {
115 begin();
116 entries = kqueueWrapper.poll(timeout);
117 } finally {
118 end();
119 }
120 processDeregisterQueue();
121 return updateSelectedKeys(entries);
122 }
123
124 /**
125 * Update the keys whose fd's have been selected by kqueue.
126 * Add the ready keys to the selected key set.
127 * If the interrupt fd has been selected, drain it and clear the interrupt.
128 */
129 private int updateSelectedKeys(int entries)
130 throws IOException
131 {
132 int numKeysUpdated = 0;
133 boolean interrupted = false;
134
135 // A file descriptor may be registered with kqueue with more than one
136 // filter and so there may be more than one event for a fd. The update
137 // count in the MapEntry tracks when the fd was last updated and this
138 // ensures that the ready ops are updated rather than replaced by a
139 // second or subsequent event.
140 updateCount++;
141
142 for (int i = 0; i < entries; i++) {
143 int nextFD = kqueueWrapper.getDescriptor(i);
144 if (nextFD == fd0) {
145 interrupted = true;
146 } else {
147 MapEntry me = fdMap.get(Integer.valueOf(nextFD));
148
149 // entry is null in the case of an interrupt
150 if (me != null) {
151 int rOps = kqueueWrapper.getReventOps(i);
152 SelectionKeyImpl ski = me.ski;
153 if (selectedKeys.contains(ski)) {
154 // first time this file descriptor has been encountered on this
155 // update?
156 if (me.updateCount != updateCount) {
157 if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
158 numKeysUpdated++;
159 me.updateCount = updateCount;
160 }
161 } else {
162 // ready ops have already been set on this update
163 ski.channel.translateAndUpdateReadyOps(rOps, ski);
164 }
165 } else {
166 ski.channel.translateAndSetReadyOps(rOps, ski);
167 if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
168 selectedKeys.add(ski);
169 numKeysUpdated++;
170 me.updateCount = updateCount;
171 }
172 }
173 }
174 }
175 }
176
177 if (interrupted) {
178 // Clear the wakeup pipe
179 synchronized (interruptLock) {
180 IOUtil.drain(fd0);
181 interruptTriggered = false;
182 }
183 }
184 return numKeysUpdated;
185 }
186
187
188 protected void implClose() throws IOException {
189 if (!closed) {
190 closed = true;
191
192 // prevent further wakeup
193 synchronized (interruptLock) {
194 interruptTriggered = true;
195 }
196
197 FileDispatcherImpl.closeIntFD(fd0);
198 FileDispatcherImpl.closeIntFD(fd1);
199 if (kqueueWrapper != null) {
200 kqueueWrapper.close();
201 kqueueWrapper = null;
202 selectedKeys = null;
203
204 // Deregister channels
205 Iterator<SelectionKey> i = keys.iterator();
206 while (i.hasNext()) {
207 SelectionKeyImpl ski = (SelectionKeyImpl)i.next();
208 deregister(ski);
209 SelectableChannel selch = ski.channel();
210 if (!selch.isOpen() && !selch.isRegistered())
211 ((SelChImpl)selch).kill();
212 i.remove();
213 }
214 totalChannels = 0;
215 }
216 fd0 = -1;
217 fd1 = -1;
218 }
219 }
220
221
222 protected void implRegister(SelectionKeyImpl ski) {
223 if (closed)
224 throw new ClosedSelectorException();
225 int fd = IOUtil.fdVal(ski.channel.getFD());
226 fdMap.put(Integer.valueOf(fd), new MapEntry(ski));
227 totalChannels++;
228 keys.add(ski);
229 }
230
231
232 protected void implDereg(SelectionKeyImpl ski) throws IOException {
233 int fd = ski.channel.getFDVal();
234 fdMap.remove(Integer.valueOf(fd));
235 kqueueWrapper.release(ski.channel);
236 totalChannels--;
237 keys.remove(ski);
238 selectedKeys.remove(ski);
239 deregister((AbstractSelectionKey)ski);
240 SelectableChannel selch = ski.channel();
241 if (!selch.isOpen() && !selch.isRegistered())
242 ((SelChImpl)selch).kill();
243 }
244
245
246 public void putEventOps(SelectionKeyImpl ski, int ops) {
247 if (closed)
248 throw new ClosedSelectorException();
249 kqueueWrapper.setInterest(ski.channel, ops);
250 }
251
252
253 public Selector wakeup() {
254 synchronized (interruptLock) {
255 if (!interruptTriggered) {
256 kqueueWrapper.interrupt();
257 interruptTriggered = true;
258 }
259 }
260 return this;
261 }
262 }
|
1 /*
2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * KQueueSelectorImpl.java
28 * Implementation of Selector using FreeBSD / Mac OS X kqueues
29 */
30
31 package sun.nio.ch;
32
33 import java.io.IOException;
34 import java.nio.channels.ClosedSelectorException;
35 import java.nio.channels.SelectableChannel;
36 import java.nio.channels.SelectionKey;
37 import java.nio.channels.Selector;
38 import java.nio.channels.spi.SelectorProvider;
39 import java.util.HashMap;
40 import java.util.Iterator;
41
42 class KQueueSelectorImpl
43 extends SelectorImpl
44 {
45 // File descriptors used for interrupt
46 private final int fd0;
47 private final int fd1;
48
49 // The kqueue manipulator
50 private final KQueueArrayWrapper kqueueWrapper;
51
52 // Map from a file descriptor to an entry containing the selection key
53 private final HashMap<Integer, MapEntry> fdMap;
54
55 // True if this Selector has been closed
56 private boolean closed;
57
58 // Lock for interrupt triggering and clearing
59 private final Object interruptLock = new Object();
60 private boolean interruptTriggered;
61
62 // used by updateSelectedKeys to handle cases where the same file
63 // descriptor is polled by more than one filter
64 private long updateCount;
65
66 // Used to map file descriptors to a selection key and "update count"
67 // (see updateSelectedKeys for usage).
68 private static class MapEntry {
69 SelectionKeyImpl ski;
70 long updateCount;
71 MapEntry(SelectionKeyImpl ski) {
72 this.ski = ski;
73 }
74 }
75
76 /**
77 * Package private constructor called by factory method in
78 * the abstract superclass Selector.
79 */
80 KQueueSelectorImpl(SelectorProvider sp) throws IOException {
81 super(sp);
82 long fds = IOUtil.makePipe(false);
83 fd0 = (int)(fds >>> 32);
84 fd1 = (int)fds;
85 try {
86 kqueueWrapper = new KQueueArrayWrapper(fd0, fd1);
87 fdMap = new HashMap<>();
88 } catch (Throwable t) {
89 try {
90 FileDispatcherImpl.closeIntFD(fd0);
91 } catch (IOException ioe0) {
92 t.addSuppressed(ioe0);
93 }
94 try {
95 FileDispatcherImpl.closeIntFD(fd1);
96 } catch (IOException ioe1) {
97 t.addSuppressed(ioe1);
98 }
99 throw t;
100 }
101 }
102
103 private void ensureOpen() {
104 if (closed)
105 throw new ClosedSelectorException();
106 }
107
108 @Override
109 protected int doSelect(long timeout)
110 throws IOException
111 {
112 ensureOpen();
113 int numEntries;
114 processDeregisterQueue();
115 try {
116 begin();
117 numEntries = kqueueWrapper.poll(timeout);
118 } finally {
119 end();
120 }
121 processDeregisterQueue();
122 return updateSelectedKeys(numEntries);
123 }
124
125 /**
126 * Update the keys whose fd's have been selected by kqueue.
127 * Add the ready keys to the selected key set.
128 * If the interrupt fd has been selected, drain it and clear the interrupt.
129 */
130 private int updateSelectedKeys(int numEntries)
131 throws IOException
132 {
133 int numKeysUpdated = 0;
134 boolean interrupted = false;
135
136 // A file descriptor may be registered with kqueue with more than one
137 // filter and so there may be more than one event for a fd. The update
138 // count in the MapEntry tracks when the fd was last updated and this
139 // ensures that the ready ops are updated rather than replaced by a
140 // second or subsequent event.
141 updateCount++;
142
143 for (int i = 0; i < numEntries; i++) {
144 int nextFD = kqueueWrapper.getDescriptor(i);
145 if (nextFD == fd0) {
146 interrupted = true;
147 } else {
148 MapEntry me = fdMap.get(Integer.valueOf(nextFD));
149 if (me != null) {
150 int rOps = kqueueWrapper.getReventOps(i);
151 SelectionKeyImpl ski = me.ski;
152 if (selectedKeys.contains(ski)) {
153 // first time this file descriptor has been encountered on this
154 // update?
155 if (me.updateCount != updateCount) {
156 if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
157 numKeysUpdated++;
158 me.updateCount = updateCount;
159 }
160 } else {
161 // ready ops have already been set on this update
162 ski.channel.translateAndUpdateReadyOps(rOps, ski);
163 }
164 } else {
165 ski.channel.translateAndSetReadyOps(rOps, ski);
166 if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
167 selectedKeys.add(ski);
168 numKeysUpdated++;
169 me.updateCount = updateCount;
170 }
171 }
172 }
173 }
174 }
175
176 if (interrupted) {
177 clearInterrupt();
178 }
179 return numKeysUpdated;
180 }
181
182 @Override
183 protected void implClose() throws IOException {
184 if (!closed) {
185 closed = true;
186
187 // prevent further wakeup
188 synchronized (interruptLock) {
189 interruptTriggered = true;
190 }
191
192 kqueueWrapper.close();
193 FileDispatcherImpl.closeIntFD(fd0);
194 FileDispatcherImpl.closeIntFD(fd1);
195
196 // Deregister channels
197 Iterator<SelectionKey> i = keys.iterator();
198 while (i.hasNext()) {
199 SelectionKeyImpl ski = (SelectionKeyImpl)i.next();
200 deregister(ski);
201 SelectableChannel selch = ski.channel();
202 if (!selch.isOpen() && !selch.isRegistered())
203 ((SelChImpl)selch).kill();
204 i.remove();
205 }
206 }
207 }
208
209 @Override
210 protected void implRegister(SelectionKeyImpl ski) {
211 ensureOpen();
212 int fd = IOUtil.fdVal(ski.channel.getFD());
213 fdMap.put(Integer.valueOf(fd), new MapEntry(ski));
214 keys.add(ski);
215 }
216
217 @Override
218 protected void implDereg(SelectionKeyImpl ski) throws IOException {
219 int fd = ski.channel.getFDVal();
220 fdMap.remove(Integer.valueOf(fd));
221 kqueueWrapper.release(ski.channel);
222 keys.remove(ski);
223 selectedKeys.remove(ski);
224 deregister(ski);
225 SelectableChannel selch = ski.channel();
226 if (!selch.isOpen() && !selch.isRegistered())
227 ((SelChImpl)selch).kill();
228 }
229
230 @Override
231 public void putEventOps(SelectionKeyImpl ski, int ops) {
232 ensureOpen();
233 kqueueWrapper.setInterest(ski.channel, ops);
234 }
235
236 @Override
237 public Selector wakeup() {
238 synchronized (interruptLock) {
239 if (!interruptTriggered) {
240 kqueueWrapper.interrupt();
241 interruptTriggered = true;
242 }
243 }
244 return this;
245 }
246
247 private void clearInterrupt() throws IOException {
248 synchronized (interruptLock) {
249 IOUtil.drain(fd0);
250 interruptTriggered = false;
251 }
252 }
253 }
|