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