1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.util.internal;
17
18 import io.netty.util.internal.logging.InternalLogger;
19 import io.netty.util.internal.logging.InternalLoggerFactory;
20 import sun.misc.Unsafe;
21
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.nio.Buffer;
27 import java.nio.ByteBuffer;
28 import java.security.AccessController;
29 import java.security.PrivilegedAction;
30
31 import static io.netty.util.internal.ObjectUtil.checkNotNull;
32
33
34
35
36 final class PlatformDependent0 {
37
38 private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
39 private static final long ADDRESS_FIELD_OFFSET;
40 private static final long BYTE_ARRAY_BASE_OFFSET;
41 private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
42 private static final boolean IS_EXPLICIT_NO_UNSAFE = explicitNoUnsafe0();
43 private static final Method ALLOCATE_ARRAY_METHOD;
44 private static final int JAVA_VERSION = javaVersion0();
45 private static final boolean IS_ANDROID = isAndroid0();
46
47 private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
48 private static final Object INTERNAL_UNSAFE;
49 private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
50
51 static final Unsafe UNSAFE;
52
53
54
55
56
57 private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
58
59 private static final boolean UNALIGNED;
60
61 static {
62 final ByteBuffer direct;
63 Field addressField = null;
64 Method allocateArrayMethod = null;
65 Throwable unsafeUnavailabilityCause = null;
66 Unsafe unsafe;
67 Object internalUnsafe = null;
68
69 if (isExplicitNoUnsafe()) {
70 direct = null;
71 addressField = null;
72 unsafeUnavailabilityCause = new UnsupportedOperationException("Unsafe explicitly disabled");
73 unsafe = null;
74 internalUnsafe = null;
75 } else {
76 direct = ByteBuffer.allocateDirect(1);
77
78
79 final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
80 @Override
81 public Object run() {
82 try {
83 final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
84
85
86 Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
87 if (cause != null) {
88 return cause;
89 }
90
91 return unsafeField.get(null);
92 } catch (NoSuchFieldException e) {
93 return e;
94 } catch (SecurityException e) {
95 return e;
96 } catch (IllegalAccessException e) {
97 return e;
98 } catch (NoClassDefFoundError e) {
99
100
101 return e;
102 }
103 }
104 });
105
106
107
108
109
110 if (maybeUnsafe instanceof Throwable) {
111 unsafe = null;
112 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
113 logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", (Throwable) maybeUnsafe);
114 } else {
115 unsafe = (Unsafe) maybeUnsafe;
116 logger.debug("sun.misc.Unsafe.theUnsafe: available");
117 }
118
119
120
121
122 if (unsafe != null) {
123 final Unsafe finalUnsafe = unsafe;
124 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
125 @Override
126 public Object run() {
127 try {
128 finalUnsafe.getClass().getDeclaredMethod(
129 "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
130 return null;
131 } catch (NoSuchMethodException e) {
132 return e;
133 } catch (SecurityException e) {
134 return e;
135 }
136 }
137 });
138
139 if (maybeException == null) {
140 logger.debug("sun.misc.Unsafe.copyMemory: available");
141 } else {
142
143 unsafe = null;
144 unsafeUnavailabilityCause = (Throwable) maybeException;
145 logger.debug("sun.misc.Unsafe.copyMemory: unavailable", (Throwable) maybeException);
146 }
147 }
148
149 if (unsafe != null) {
150 final Unsafe finalUnsafe = unsafe;
151
152
153 final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() {
154 @Override
155 public Object run() {
156 try {
157 final Field field = Buffer.class.getDeclaredField("address");
158
159
160 final long offset = finalUnsafe.objectFieldOffset(field);
161 final long address = finalUnsafe.getLong(direct, offset);
162
163
164 if (address == 0) {
165 return null;
166 }
167 return field;
168 } catch (NoSuchFieldException e) {
169 return e;
170 } catch (SecurityException e) {
171 return e;
172 }
173 }
174 });
175
176 if (maybeAddressField instanceof Field) {
177 addressField = (Field) maybeAddressField;
178 logger.debug("java.nio.Buffer.address: available");
179 } else {
180 unsafeUnavailabilityCause = (Throwable) maybeAddressField;
181 logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField);
182
183
184
185 unsafe = null;
186 }
187 }
188
189 if (unsafe != null) {
190
191
192 long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class);
193 if (byteArrayIndexScale != 1) {
194 logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale);
195 unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale");
196 unsafe = null;
197 }
198 }
199 }
200 UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
201 UNSAFE = unsafe;
202
203 if (unsafe == null) {
204 BYTE_ARRAY_BASE_OFFSET = -1;
205 ADDRESS_FIELD_OFFSET = -1;
206 UNALIGNED = false;
207 DIRECT_BUFFER_CONSTRUCTOR = null;
208 ALLOCATE_ARRAY_METHOD = null;
209 } else {
210 Constructor<?> directBufferConstructor;
211 long address = -1;
212 try {
213 final Object maybeDirectBufferConstructor =
214 AccessController.doPrivileged(new PrivilegedAction<Object>() {
215 @Override
216 public Object run() {
217 try {
218 final Constructor<?> constructor =
219 direct.getClass().getDeclaredConstructor(long.class, int.class);
220 Throwable cause = ReflectionUtil.trySetAccessible(constructor, true);
221 if (cause != null) {
222 return cause;
223 }
224 return constructor;
225 } catch (NoSuchMethodException e) {
226 return e;
227 } catch (SecurityException e) {
228 return e;
229 }
230 }
231 });
232
233 if (maybeDirectBufferConstructor instanceof Constructor<?>) {
234 address = UNSAFE.allocateMemory(1);
235
236 try {
237 ((Constructor<?>) maybeDirectBufferConstructor).newInstance(address, 1);
238 directBufferConstructor = (Constructor<?>) maybeDirectBufferConstructor;
239 logger.debug("direct buffer constructor: available");
240 } catch (InstantiationException e) {
241 directBufferConstructor = null;
242 } catch (IllegalAccessException e) {
243 directBufferConstructor = null;
244 } catch (InvocationTargetException e) {
245 directBufferConstructor = null;
246 }
247 } else {
248 logger.debug(
249 "direct buffer constructor: unavailable",
250 (Throwable) maybeDirectBufferConstructor);
251 directBufferConstructor = null;
252 }
253 } finally {
254 if (address != -1) {
255 UNSAFE.freeMemory(address);
256 }
257 }
258 DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
259 ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
260 boolean unaligned;
261 Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() {
262 @Override
263 public Object run() {
264 try {
265 Class<?> bitsClass =
266 Class.forName("java.nio.Bits", false, getSystemClassLoader());
267 Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
268 Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true);
269 if (cause != null) {
270 return cause;
271 }
272 return unalignedMethod.invoke(null);
273 } catch (NoSuchMethodException e) {
274 return e;
275 } catch (SecurityException e) {
276 return e;
277 } catch (IllegalAccessException e) {
278 return e;
279 } catch (ClassNotFoundException e) {
280 return e;
281 } catch (InvocationTargetException e) {
282 return e;
283 }
284 }
285 });
286
287 if (maybeUnaligned instanceof Boolean) {
288 unaligned = (Boolean) maybeUnaligned;
289 logger.debug("java.nio.Bits.unaligned: available, {}", unaligned);
290 } else {
291 String arch = SystemPropertyUtil.get("os.arch", "");
292
293 unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
294 Throwable t = (Throwable) maybeUnaligned;
295 logger.debug("java.nio.Bits.unaligned: unavailable {}", unaligned, t);
296 }
297
298 UNALIGNED = unaligned;
299 BYTE_ARRAY_BASE_OFFSET = arrayBaseOffset();
300
301 if (javaVersion() >= 9) {
302 Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
303 @Override
304 public Object run() {
305 try {
306
307
308 Class<?> internalUnsafeClass = getClassLoader(PlatformDependent0.class)
309 .loadClass("jdk.internal.misc.Unsafe");
310 Method method = internalUnsafeClass.getDeclaredMethod("getUnsafe");
311 return method.invoke(null);
312 } catch (Throwable e) {
313 return e;
314 }
315 }
316 });
317 if (!(maybeException instanceof Throwable)) {
318 internalUnsafe = maybeException;
319 final Object finalInternalUnsafe = internalUnsafe;
320 maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
321 @Override
322 public Object run() {
323 try {
324 return finalInternalUnsafe.getClass().getDeclaredMethod(
325 "allocateUninitializedArray", Class.class, int.class);
326 } catch (NoSuchMethodException e) {
327 return e;
328 } catch (SecurityException e) {
329 return e;
330 }
331 }
332 });
333
334 if (maybeException instanceof Method) {
335 try {
336 Method m = (Method) maybeException;
337 byte[] bytes = (byte[]) m.invoke(finalInternalUnsafe, byte.class, 8);
338 assert bytes.length == 8;
339 allocateArrayMethod = m;
340 } catch (IllegalAccessException e) {
341 maybeException = e;
342 } catch (InvocationTargetException e) {
343 maybeException = e;
344 }
345 }
346 }
347
348 if (maybeException instanceof Throwable) {
349 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable",
350 (Throwable) maybeException);
351 } else {
352 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available");
353 }
354 } else {
355 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
356 }
357 ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
358 }
359
360 INTERNAL_UNSAFE = internalUnsafe;
361
362 logger.debug("java.nio.DirectByteBuffer.<init>(long, int): {}",
363 DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
364 }
365
366 static boolean isExplicitNoUnsafe() {
367 return IS_EXPLICIT_NO_UNSAFE;
368 }
369
370 private static boolean explicitNoUnsafe0() {
371 final boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
372 logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
373
374 if (noUnsafe) {
375 logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
376 return true;
377 }
378
379
380 boolean tryUnsafe;
381 if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
382 tryUnsafe = SystemPropertyUtil.getBoolean("io.netty.tryUnsafe", true);
383 } else {
384 tryUnsafe = SystemPropertyUtil.getBoolean("org.jboss.netty.tryUnsafe", true);
385 }
386
387 if (!tryUnsafe) {
388 logger.debug("sun.misc.Unsafe: unavailable (io.netty.tryUnsafe/org.jboss.netty.tryUnsafe)");
389 return true;
390 }
391
392 return false;
393 }
394
395 static boolean isUnaligned() {
396 return UNALIGNED;
397 }
398
399 static boolean hasUnsafe() {
400 return UNSAFE != null;
401 }
402
403 static Throwable getUnsafeUnavailabilityCause() {
404 return UNSAFE_UNAVAILABILITY_CAUSE;
405 }
406
407 static void throwException(Throwable cause) {
408
409 UNSAFE.throwException(checkNotNull(cause, "cause"));
410 }
411
412 static boolean hasDirectBufferNoCleanerConstructor() {
413 return DIRECT_BUFFER_CONSTRUCTOR != null;
414 }
415
416 static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
417 return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity);
418 }
419
420 static ByteBuffer allocateDirectNoCleaner(int capacity) {
421 return newDirectBuffer(UNSAFE.allocateMemory(capacity), capacity);
422 }
423
424 static boolean hasAllocateArrayMethod() {
425 return ALLOCATE_ARRAY_METHOD != null;
426 }
427
428 static byte[] allocateUninitializedArray(int size) {
429 try {
430 return (byte[]) ALLOCATE_ARRAY_METHOD.invoke(INTERNAL_UNSAFE, byte.class, size);
431 } catch (IllegalAccessException e) {
432 throw new Error(e);
433 } catch (InvocationTargetException e) {
434 throw new Error(e);
435 }
436 }
437
438 static ByteBuffer newDirectBuffer(long address, int capacity) {
439 ObjectUtil.checkPositiveOrZero(capacity, "capacity");
440
441 try {
442 return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
443 } catch (Throwable cause) {
444
445 if (cause instanceof Error) {
446 throw (Error) cause;
447 }
448 throw new Error(cause);
449 }
450 }
451
452 static long directBufferAddress(ByteBuffer buffer) {
453 return getLong(buffer, ADDRESS_FIELD_OFFSET);
454 }
455
456 static long arrayBaseOffset() {
457 return UNSAFE.arrayBaseOffset(byte[].class);
458 }
459
460 static Object getObject(Object object, long fieldOffset) {
461 return UNSAFE.getObject(object, fieldOffset);
462 }
463
464 static int getInt(Object object, long fieldOffset) {
465 return UNSAFE.getInt(object, fieldOffset);
466 }
467
468 private static long getLong(Object object, long fieldOffset) {
469 return UNSAFE.getLong(object, fieldOffset);
470 }
471
472 static long objectFieldOffset(Field field) {
473 return UNSAFE.objectFieldOffset(field);
474 }
475
476 static byte getByte(long address) {
477 return UNSAFE.getByte(address);
478 }
479
480 static short getShort(long address) {
481 return UNSAFE.getShort(address);
482 }
483
484 static int getInt(long address) {
485 return UNSAFE.getInt(address);
486 }
487
488 static long getLong(long address) {
489 return UNSAFE.getLong(address);
490 }
491
492 static byte getByte(byte[] data, int index) {
493 return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
494 }
495
496 static short getShort(byte[] data, int index) {
497 return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index);
498 }
499
500 static int getInt(byte[] data, int index) {
501 return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index);
502 }
503
504 static long getLong(byte[] data, int index) {
505 return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index);
506 }
507
508 static void putByte(long address, byte value) {
509 UNSAFE.putByte(address, value);
510 }
511
512 static void putShort(long address, short value) {
513 UNSAFE.putShort(address, value);
514 }
515
516 static void putInt(long address, int value) {
517 UNSAFE.putInt(address, value);
518 }
519
520 static void putLong(long address, long value) {
521 UNSAFE.putLong(address, value);
522 }
523
524 static void putByte(byte[] data, int index, byte value) {
525 UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
526 }
527
528 static void putShort(byte[] data, int index, short value) {
529 UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
530 }
531
532 static void putInt(byte[] data, int index, int value) {
533 UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
534 }
535
536 static void putLong(byte[] data, int index, long value) {
537 UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
538 }
539
540 static void copyMemory(long srcAddr, long dstAddr, long length) {
541
542 while (length > 0) {
543 long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
544 UNSAFE.copyMemory(srcAddr, dstAddr, size);
545 length -= size;
546 srcAddr += size;
547 dstAddr += size;
548 }
549 }
550
551 static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
552
553 while (length > 0) {
554 long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
555 UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
556 length -= size;
557 srcOffset += size;
558 dstOffset += size;
559 }
560 }
561
562 static void setMemory(long address, long bytes, byte value) {
563 UNSAFE.setMemory(address, bytes, value);
564 }
565
566 static void setMemory(Object o, long offset, long bytes, byte value) {
567 UNSAFE.setMemory(o, offset, bytes, value);
568 }
569
570 static boolean isZero(byte[] bytes, int startPos, int length) {
571 if (length <= 0) {
572 return true;
573 }
574 final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
575 int remainingBytes = length & 7;
576 final long end = baseOffset + remainingBytes;
577 for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
578 if (UNSAFE.getLong(bytes, i) != 0) {
579 return false;
580 }
581 }
582
583 if (remainingBytes >= 4) {
584 remainingBytes -= 4;
585 if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
586 return false;
587 }
588 }
589 if (remainingBytes >= 2) {
590 return UNSAFE.getChar(bytes, baseOffset) == 0 &&
591 (remainingBytes == 2 || bytes[startPos + 2] == 0);
592 }
593 return bytes[startPos] == 0;
594 }
595
596 static ClassLoader getClassLoader(final Class<?> clazz) {
597 if (System.getSecurityManager() == null) {
598 return clazz.getClassLoader();
599 } else {
600 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
601 @Override
602 public ClassLoader run() {
603 return clazz.getClassLoader();
604 }
605 });
606 }
607 }
608
609 static ClassLoader getContextClassLoader() {
610 if (System.getSecurityManager() == null) {
611 return Thread.currentThread().getContextClassLoader();
612 } else {
613 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
614 @Override
615 public ClassLoader run() {
616 return Thread.currentThread().getContextClassLoader();
617 }
618 });
619 }
620 }
621
622 static ClassLoader getSystemClassLoader() {
623 if (System.getSecurityManager() == null) {
624 return ClassLoader.getSystemClassLoader();
625 } else {
626 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
627 @Override
628 public ClassLoader run() {
629 return ClassLoader.getSystemClassLoader();
630 }
631 });
632 }
633 }
634
635 static int addressSize() {
636 return UNSAFE.addressSize();
637 }
638
639 static long allocateMemory(long size) {
640 return UNSAFE.allocateMemory(size);
641 }
642
643 static void freeMemory(long address) {
644 UNSAFE.freeMemory(address);
645 }
646
647 static long reallocateMemory(long address, long newSize) {
648 return UNSAFE.reallocateMemory(address, newSize);
649 }
650
651 static boolean isAndroid() {
652 return IS_ANDROID;
653 }
654
655 private static boolean isAndroid0() {
656 boolean android;
657 try {
658 Class.forName("android.app.Application", false, getSystemClassLoader());
659 android = true;
660 } catch (Throwable ignored) {
661
662 android = false;
663 }
664
665 if (android) {
666 logger.debug("Platform: Android");
667 }
668 return android;
669 }
670
671 private static boolean explicitTryReflectionSetAccessible0() {
672
673 return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible", javaVersion() < 9);
674 }
675
676 static boolean isExplicitTryReflectionSetAccessible() {
677 return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE;
678 }
679
680 static int javaVersion() {
681 return JAVA_VERSION;
682 }
683
684 private static int javaVersion0() {
685 final int majorVersion;
686
687 if (isAndroid0()) {
688 majorVersion = 6;
689 } else {
690 majorVersion = majorVersionFromJavaSpecificationVersion();
691 }
692
693 logger.debug("Java version: {}", majorVersion);
694
695 return majorVersion;
696 }
697
698
699 static int majorVersionFromJavaSpecificationVersion() {
700 return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
701 }
702
703
704 static int majorVersion(final String javaSpecVersion) {
705 final String[] components = javaSpecVersion.split("\\.");
706 final int[] version = new int[components.length];
707 for (int i = 0; i < components.length; i++) {
708 version[i] = Integer.parseInt(components[i]);
709 }
710
711 if (version[0] == 1) {
712 assert version[1] >= 6;
713 return version[1];
714 } else {
715 return version[0];
716 }
717 }
718
719 private PlatformDependent0() {
720 }
721
722 }