1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.jboss.netty.channel.socket.nio;
24
25 import java.io.IOException;
26 import java.net.InetSocketAddress;
27 import java.nio.channels.ClosedChannelException;
28 import java.nio.channels.SelectionKey;
29 import java.nio.channels.Selector;
30 import java.nio.channels.ServerSocketChannel;
31 import java.nio.channels.spi.SelectorProvider;
32 import java.util.Set;
33 import java.util.concurrent.BlockingQueue;
34 import java.util.concurrent.ExecutorService;
35 import java.util.concurrent.Executors;
36 import java.util.concurrent.LinkedBlockingQueue;
37 import java.util.concurrent.TimeUnit;
38
39 import org.jboss.netty.logging.InternalLogger;
40 import org.jboss.netty.logging.InternalLoggerFactory;
41 import org.jboss.netty.util.SystemPropertyUtil;
42 import org.jboss.netty.util.ThreadRenamingRunnable;
43
44
45
46
47
48
49
50
51
52
53
54 class NioProviderMetadata {
55 static final InternalLogger logger =
56 InternalLoggerFactory.getInstance(NioProviderMetadata.class);
57
58 private static final String CONSTRAINT_LEVEL_PROPERTY =
59 "java.nio.channels.spi.constraintLevel";
60
61 private static final long AUTODETECTION_TIMEOUT = 7000L;
62
63
64
65
66
67
68 static final int CONSTRAINT_LEVEL;
69
70 static {
71 int constraintLevel = -1;
72
73
74 try {
75 String value = SystemPropertyUtil.get(CONSTRAINT_LEVEL_PROPERTY, "-1");
76 constraintLevel = Integer.parseInt(value);
77 if (constraintLevel < 0 || constraintLevel > 2) {
78 constraintLevel = -1;
79 } else {
80 logger.debug(
81 "Using the specified NIO constraint level: " +
82 constraintLevel);
83 }
84 } catch (Exception e) {
85
86 }
87
88 if (constraintLevel < 0) {
89 constraintLevel = detectConstraintLevelFromSystemProperties();
90
91 if (constraintLevel < 0) {
92 logger.debug(
93 "Couldn't get the NIO constraint level from the system properties.");
94 ConstraintLevelAutodetector autodetector =
95 new ConstraintLevelAutodetector();
96
97 try {
98 constraintLevel = autodetector.autodetectWithTimeout();
99 } catch (Exception e) {
100
101
102 constraintLevel = autodetector.autodetectWithoutTimeout();
103 }
104 }
105
106 if (constraintLevel < 0) {
107 constraintLevel = 2;
108 logger.warn(
109 "Failed to autodetect the NIO constraint level; " +
110 "using the safest level (2)");
111 } else if (constraintLevel != 0) {
112 logger.info(
113 "Using the autodetected NIO constraint level: " +
114 constraintLevel +
115 " (Use better NIO provider for better performance)");
116 } else {
117 logger.debug(
118 "Using the autodetected NIO constraint level: " +
119 constraintLevel);
120 }
121 }
122
123 CONSTRAINT_LEVEL = constraintLevel;
124
125 if (CONSTRAINT_LEVEL < 0 || CONSTRAINT_LEVEL > 2) {
126 throw new Error(
127 "Unexpected NIO constraint level: " +
128 CONSTRAINT_LEVEL + ", please report this error.");
129 }
130 }
131
132 private static int detectConstraintLevelFromSystemProperties() {
133 String version = SystemPropertyUtil.get("java.specification.version");
134 String os = SystemPropertyUtil.get("os.name");
135 String vendor = SystemPropertyUtil.get("java.vm.vendor");
136 String provider;
137 try {
138 provider = SelectorProvider.provider().getClass().getName();
139 } catch (Exception e) {
140
141 provider = null;
142 }
143
144 if (version == null || os == null || vendor == null || provider == null) {
145 return -1;
146 }
147
148 os = os.toLowerCase();
149 vendor = vendor.toLowerCase();
150
151
152 if (vendor.indexOf("sun") >= 0) {
153
154 if (os.indexOf("linux") >= 0) {
155 if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
156 provider.equals("sun.nio.ch.PollSelectorProvider")) {
157 return 0;
158 }
159
160
161 } else if (os.indexOf("windows") >= 0) {
162 if (provider.equals("sun.nio.ch.WindowsSelectorProvider")) {
163 return 0;
164 }
165
166
167 } else if (os.indexOf("sun") >= 0 || os.indexOf("solaris") >= 0) {
168 if (provider.equals("sun.nio.ch.DevPollSelectorProvider")) {
169 return 0;
170 }
171 }
172
173 } else if (vendor.indexOf("apple") >= 0) {
174
175 if (os.indexOf("mac") >= 0 && os.indexOf("os") >= 0) {
176 if (provider.equals("sun.nio.ch.KQueueSelectorProvider")) {
177 return 0;
178 }
179 }
180
181 } else if (vendor.indexOf("ibm") >= 0) {
182
183 if (os.indexOf("linux") >= 0) {
184 if (version.equals("1.5") || version.matches("^1\\.5\\D.*$")) {
185 if (provider.equals("sun.nio.ch.PollSelectorProvider")) {
186 return 1;
187 }
188 } else if (version.equals("1.6") || version.matches("^1\\.6\\D.*$")) {
189 if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
190 provider.equals("sun.nio.ch.PollSelectorProvider")) {
191 return 2;
192 }
193 }
194
195
196 } if (os.indexOf("aix") >= 0) {
197 if (version.equals("1.5") || version.matches("^1\\.5\\D.*$")) {
198 if (provider.equals("sun.nio.ch.PollSelectorProvider")) {
199 return 1;
200 }
201 } else if (version.equals("1.6") || version.matches("^1\\.6\\D.*$")) {
202 if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
203 provider.equals("sun.nio.ch.PollSelectorProvider")) {
204 return 2;
205 }
206 }
207 }
208
209 } else if (vendor.indexOf("bea") >= 0 || vendor.indexOf("oracle") >= 0) {
210
211 if (os.indexOf("linux") >= 0) {
212 if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
213 provider.equals("sun.nio.ch.PollSelectorProvider")) {
214 return 0;
215 }
216
217
218 } else if (os.indexOf("windows") >= 0) {
219 if (provider.equals("sun.nio.ch.WindowsSelectorProvider")) {
220 return 0;
221 }
222 }
223 }
224
225
226 return -1;
227 }
228
229
230 private static class ConstraintLevelAutodetector {
231
232 ConstraintLevelAutodetector() {
233 super();
234 }
235
236 int autodetectWithTimeout() {
237 final BlockingQueue<Integer> resultQueue = new LinkedBlockingQueue<Integer>();
238 Runnable detector = new ThreadRenamingRunnable(new Runnable() {
239 public void run() {
240 int level = -1;
241 try {
242 level = autodetectWithoutTimeout();
243 } finally {
244 resultQueue.offer(Integer.valueOf(level));
245 }
246 }
247 }, "NIO constraint level detector");
248
249 Thread detectorThread = new Thread(detector);
250 detectorThread.start();
251
252 for (;;) {
253 try {
254 Integer result = resultQueue.poll(AUTODETECTION_TIMEOUT, TimeUnit.MILLISECONDS);
255 if (result == null) {
256 logger.warn("NIO constraint level autodetection timed out.");
257 return -1;
258 } else {
259 return result.intValue();
260 }
261 } catch (InterruptedException e) {
262
263 }
264 }
265 }
266
267 int autodetectWithoutTimeout() {
268
269 final int constraintLevel;
270 ExecutorService executor = Executors.newCachedThreadPool();
271 boolean success;
272 long startTime;
273 int interestOps;
274
275 ServerSocketChannel ch = null;
276 SelectorLoop loop = null;
277
278 try {
279
280 ch = ServerSocketChannel.open();
281
282
283 try {
284 ch.socket().bind(new InetSocketAddress(0));
285 ch.configureBlocking(false);
286 } catch (IOException e) {
287 logger.warn("Failed to configure a temporary socket.", e);
288 return -1;
289 }
290
291
292 try {
293 loop = new SelectorLoop();
294 } catch (IOException e) {
295 logger.warn("Failed to open a temporary selector.", e);
296 return -1;
297 }
298
299
300 try {
301 ch.register(loop.selector, 0);
302 } catch (ClosedChannelException e) {
303 logger.warn("Failed to register a temporary selector.", e);
304 return -1;
305 }
306
307 SelectionKey key = ch.keyFor(loop.selector);
308
309
310 executor.execute(loop);
311
312
313 success = true;
314 for (int i = 0; i < 10; i ++) {
315
316
317
318 do {
319 while (!loop.selecting) {
320 Thread.yield();
321 }
322
323
324 try {
325 Thread.sleep(50);
326 } catch (InterruptedException e) {
327
328 }
329 } while (!loop.selecting);
330
331 startTime = System.nanoTime();
332 key.interestOps(key.interestOps() | SelectionKey.OP_ACCEPT);
333 key.interestOps(key.interestOps() & ~SelectionKey.OP_ACCEPT);
334
335 if (System.nanoTime() - startTime >= 500000000L) {
336 success = false;
337 break;
338 }
339 }
340
341 if (success) {
342 constraintLevel = 0;
343 } else {
344
345 success = true;
346 for (int i = 0; i < 10; i ++) {
347
348
349
350 do {
351 while (!loop.selecting) {
352 Thread.yield();
353 }
354
355
356 try {
357 Thread.sleep(50);
358 } catch (InterruptedException e) {
359
360 }
361 } while (!loop.selecting);
362
363 startTime = System.nanoTime();
364 interestOps = key.interestOps();
365 synchronized (loop) {
366 loop.selector.wakeup();
367 key.interestOps(interestOps | SelectionKey.OP_ACCEPT);
368 key.interestOps(interestOps & ~SelectionKey.OP_ACCEPT);
369 }
370
371 if (System.nanoTime() - startTime >= 500000000L) {
372 success = false;
373 break;
374 }
375 }
376 if (success) {
377 constraintLevel = 1;
378 } else {
379 constraintLevel = 2;
380 }
381 }
382 } catch (IOException e) {
383 return -1;
384 } finally {
385 if (ch != null) {
386 try {
387 ch.close();
388 } catch (IOException e) {
389 logger.warn("Failed to close a temporary socket.", e);
390 }
391 }
392
393 if (loop != null) {
394 loop.done = true;
395 executor.shutdownNow();
396 try {
397 for (;;) {
398 loop.selector.wakeup();
399 try {
400 if (executor.awaitTermination(1, TimeUnit.SECONDS)) {
401 break;
402 }
403 } catch (InterruptedException e) {
404
405 }
406 }
407 } catch (Exception e) {
408
409 }
410
411 try {
412 loop.selector.close();
413 } catch (IOException e) {
414 logger.warn("Failed to close a temporary selector.", e);
415 }
416 }
417 }
418
419 return constraintLevel;
420 }
421 }
422
423 private static class SelectorLoop implements Runnable {
424 final Selector selector;
425 volatile boolean done;
426 volatile boolean selecting;
427
428 SelectorLoop() throws IOException {
429 selector = Selector.open();
430 }
431
432 public void run() {
433 while (!done) {
434 synchronized (this) {
435
436 }
437 try {
438 selecting = true;
439 try {
440 selector.select(1000);
441 } finally {
442 selecting = false;
443 }
444
445 Set<SelectionKey> keys = selector.selectedKeys();
446 for (SelectionKey k: keys) {
447 k.interestOps(0);
448 }
449 keys.clear();
450 } catch (IOException e) {
451 logger.warn("Failed to wait for a temporary selector.", e);
452 }
453 }
454 }
455 }
456 }