/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.internal;
import io.netty.util.internal.logging.
InternalLogger;
import io.netty.util.internal.logging.
InternalLoggerFactory;
import org.
jctools.
queues.
MpscArrayQueue;
import org.
jctools.
queues.
MpscChunkedArrayQueue;
import org.
jctools.
queues.
MpscUnboundedArrayQueue;
import org.
jctools.
queues.
SpscLinkedQueue;
import org.
jctools.
queues.
atomic.
MpscAtomicArrayQueue;
import org.
jctools.
queues.
atomic.
MpscGrowableAtomicArrayQueue;
import org.
jctools.
queues.
atomic.
MpscUnboundedAtomicArrayQueue;
import org.
jctools.
queues.
atomic.
SpscLinkedAtomicQueue;
import org.
jctools.
util.
Pow2;
import org.
jctools.
util.
UnsafeAccess;
import java.io.
File;
import java.lang.reflect.
Method;
import java.nio.
ByteBuffer;
import java.nio.
ByteOrder;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.util.
Deque;
import java.util.
List;
import java.util.
Locale;
import java.util.
Map;
import java.util.
Queue;
import java.util.
Random;
import java.util.concurrent.
ConcurrentHashMap;
import java.util.concurrent.
ConcurrentLinkedDeque;
import java.util.concurrent.
ConcurrentMap;
import java.util.concurrent.
LinkedBlockingDeque;
import java.util.concurrent.atomic.
AtomicLong;
import java.util.regex.
Matcher;
import java.util.regex.
Pattern;
import static io.netty.util.internal.
PlatformDependent0.
HASH_CODE_ASCII_SEED;
import static io.netty.util.internal.
PlatformDependent0.
HASH_CODE_C1;
import static io.netty.util.internal.
PlatformDependent0.
HASH_CODE_C2;
import static io.netty.util.internal.
PlatformDependent0.hashCodeAsciiSanitize;
import static io.netty.util.internal.
PlatformDependent0.unalignedAccess;
import static java.lang.
Math.max;
import static java.lang.
Math.min;
/**
* Utility that detects various properties specific to the current runtime
* environment, such as Java version and the availability of the
* {@code sun.misc.Unsafe} object.
* <p>
* You can disable the use of {@code sun.misc.Unsafe} if you specify
* the system property <strong>io.netty.noUnsafe</strong>.
*/
public final class
PlatformDependent {
private static final
InternalLogger logger =
InternalLoggerFactory.
getInstance(
PlatformDependent.class);
private static final
Pattern MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN =
Pattern.
compile(
"\\s*-XX:MaxDirectMemorySize\\s*=\\s*([0-9]+)\\s*([kKmMgG]?)\\s*$");
private static final boolean
IS_WINDOWS =
isWindows0();
private static final boolean
IS_OSX =
isOsx0();
private static final boolean
MAYBE_SUPER_USER;
private static final boolean
CAN_ENABLE_TCP_NODELAY_BY_DEFAULT = !
isAndroid();
private static final
Throwable UNSAFE_UNAVAILABILITY_CAUSE =
unsafeUnavailabilityCause0();
private static final boolean
DIRECT_BUFFER_PREFERRED =
UNSAFE_UNAVAILABILITY_CAUSE == null && !
SystemPropertyUtil.
getBoolean("io.netty.noPreferDirect", false);
private static final long
MAX_DIRECT_MEMORY =
maxDirectMemory0();
private static final int
MPSC_CHUNK_SIZE = 1024;
private static final int
MIN_MAX_MPSC_CAPACITY =
MPSC_CHUNK_SIZE * 2;
private static final int
MAX_ALLOWED_MPSC_CAPACITY =
Pow2.
MAX_POW2;
private static final long
BYTE_ARRAY_BASE_OFFSET =
byteArrayBaseOffset0();
private static final
File TMPDIR =
tmpdir0();
private static final int
BIT_MODE =
bitMode0();
private static final
String NORMALIZED_ARCH =
normalizeArch(
SystemPropertyUtil.
get("os.arch", ""));
private static final
String NORMALIZED_OS =
normalizeOs(
SystemPropertyUtil.
get("os.name", ""));
private static final int
ADDRESS_SIZE =
addressSize0();
private static final boolean
USE_DIRECT_BUFFER_NO_CLEANER;
private static final
AtomicLong DIRECT_MEMORY_COUNTER;
private static final long
DIRECT_MEMORY_LIMIT;
private static final
ThreadLocalRandomProvider RANDOM_PROVIDER;
private static final
Cleaner CLEANER;
private static final int
UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD;
public static final boolean
BIG_ENDIAN_NATIVE_ORDER =
ByteOrder.
nativeOrder() ==
ByteOrder.
BIG_ENDIAN;
private static final
Cleaner NOOP = new
Cleaner() {
@
Override
public void
freeDirectBuffer(
ByteBuffer buffer) {
// NOOP
}
};
static {
if (
javaVersion() >= 7) {
RANDOM_PROVIDER = new
ThreadLocalRandomProvider() {
@
Override
public
Random current() {
return java.util.concurrent.
ThreadLocalRandom.
current();
}
};
} else {
RANDOM_PROVIDER = new
ThreadLocalRandomProvider() {
@
Override
public
Random current() {
return
ThreadLocalRandom.
current();
}
};
}
if (
logger.
isDebugEnabled()) {
logger.
debug("-Dio.netty.noPreferDirect: {}", !
DIRECT_BUFFER_PREFERRED);
}
/*
* We do not want to log this message if unsafe is explicitly disabled. Do not remove the explicit no unsafe
* guard.
*/
if (!
hasUnsafe() && !
isAndroid() && !
PlatformDependent0.
isExplicitNoUnsafe()) {
logger.
info(
"Your platform does not provide complete low-level API for accessing direct buffers reliably. " +
"Unless explicitly requested, heap buffer will always be preferred to avoid potential system " +
"instability.");
}
// Here is how the system property is used:
//
// * < 0 - Don't use cleaner, and inherit max direct memory from java. In this case the
// "practical max direct memory" would be 2 * max memory as defined by the JDK.
// * == 0 - Use cleaner, Netty will not enforce max memory, and instead will defer to JDK.
// * > 0 - Don't use cleaner. This will limit Netty's total direct memory
// (note: that JDK's direct memory limit is independent of this).
long
maxDirectMemory =
SystemPropertyUtil.
getLong("io.netty.maxDirectMemory", -1);
if (
maxDirectMemory == 0 || !
hasUnsafe() || !
PlatformDependent0.
hasDirectBufferNoCleanerConstructor()) {
USE_DIRECT_BUFFER_NO_CLEANER = false;
DIRECT_MEMORY_COUNTER = null;
} else {
USE_DIRECT_BUFFER_NO_CLEANER = true;
if (
maxDirectMemory < 0) {
maxDirectMemory =
MAX_DIRECT_MEMORY;
if (
maxDirectMemory <= 0) {
DIRECT_MEMORY_COUNTER = null;
} else {
DIRECT_MEMORY_COUNTER = new
AtomicLong();
}
} else {
DIRECT_MEMORY_COUNTER = new
AtomicLong();
}
}
DIRECT_MEMORY_LIMIT =
maxDirectMemory;
logger.
debug("-Dio.netty.maxDirectMemory: {} bytes",
maxDirectMemory);
int
tryAllocateUninitializedArray =
SystemPropertyUtil.
getInt("io.netty.uninitializedArrayAllocationThreshold", 1024);
UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD =
javaVersion() >= 9 &&
PlatformDependent0.
hasAllocateArrayMethod() ?
tryAllocateUninitializedArray : -1;
logger.
debug("-Dio.netty.uninitializedArrayAllocationThreshold: {}",
UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD);
MAYBE_SUPER_USER =
maybeSuperUser0();
if (!
isAndroid() &&
hasUnsafe()) {
// only direct to method if we are not running on android.
// See https://github.com/netty/netty/issues/2604
if (
javaVersion() >= 9) {
CLEANER =
CleanerJava9.
isSupported() ? new
CleanerJava9() :
NOOP;
} else {
CLEANER =
CleanerJava6.
isSupported() ? new
CleanerJava6() :
NOOP;
}
} else {
CLEANER =
NOOP;
}
}
public static boolean
hasDirectBufferNoCleanerConstructor() {
return
PlatformDependent0.
hasDirectBufferNoCleanerConstructor();
}
public static byte[]
allocateUninitializedArray(int
size) {
return
UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD < 0 ||
UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD >
size ?
new byte[
size] :
PlatformDependent0.
allocateUninitializedArray(
size);
}
/**
* Returns {@code true} if and only if the current platform is Android
*/
public static boolean
isAndroid() {
return
PlatformDependent0.
isAndroid();
}
/**
* Return {@code true} if the JVM is running on Windows
*/
public static boolean
isWindows() {
return
IS_WINDOWS;
}
/**
* Return {@code true} if the JVM is running on OSX / MacOS
*/
public static boolean
isOsx() {
return
IS_OSX;
}
/**
* Return {@code true} if the current user may be a super-user. Be aware that this is just an hint and so it may
* return false-positives.
*/
public static boolean
maybeSuperUser() {
return
MAYBE_SUPER_USER;
}
/**
* Return the version of Java under which this library is used.
*/
public static int
javaVersion() {
return
PlatformDependent0.
javaVersion();
}
/**
* Returns {@code true} if and only if it is fine to enable TCP_NODELAY socket option by default.
*/
public static boolean
canEnableTcpNoDelayByDefault() {
return
CAN_ENABLE_TCP_NODELAY_BY_DEFAULT;
}
/**
* Return {@code true} if {@code sun.misc.Unsafe} was found on the classpath and can be used for accelerated
* direct memory access.
*/
public static boolean
hasUnsafe() {
return
UNSAFE_UNAVAILABILITY_CAUSE == null;
}
/**
* Return the reason (if any) why {@code sun.misc.Unsafe} was not available.
*/
public static
Throwable getUnsafeUnavailabilityCause() {
return
UNSAFE_UNAVAILABILITY_CAUSE;
}
/**
* {@code true} if and only if the platform supports unaligned access.
*
* @see <a href="http://en.wikipedia.org/wiki/Segmentation_fault#Bus_error">Wikipedia on segfault</a>
*/
public static boolean
isUnaligned() {
return
PlatformDependent0.
isUnaligned();
}
/**
* Returns {@code true} if the platform has reliable low-level direct buffer access API and a user has not specified
* {@code -Dio.netty.noPreferDirect} option.
*/
public static boolean
directBufferPreferred() {
return
DIRECT_BUFFER_PREFERRED;
}
/**
* Returns the maximum memory reserved for direct buffer allocation.
*/
public static long
maxDirectMemory() {
return
MAX_DIRECT_MEMORY;
}
/**
* Returns the temporary directory.
*/
public static
File tmpdir() {
return
TMPDIR;
}
/**
* Returns the bit mode of the current VM (usually 32 or 64.)
*/
public static int
bitMode() {
return
BIT_MODE;
}
/**
* Return the address size of the OS.
* 4 (for 32 bits systems ) and 8 (for 64 bits systems).
*/
public static int
addressSize() {
return
ADDRESS_SIZE;
}
public static long
allocateMemory(long
size) {
return
PlatformDependent0.
allocateMemory(
size);
}
public static void
freeMemory(long
address) {
PlatformDependent0.
freeMemory(
address);
}
public static long
reallocateMemory(long
address, long
newSize) {
return
PlatformDependent0.
reallocateMemory(
address,
newSize);
}
/**
* Raises an exception bypassing compiler checks for checked exceptions.
*/
public static void
throwException(
Throwable t) {
if (
hasUnsafe()) {
PlatformDependent0.
throwException(
t);
} else {
PlatformDependent.<
RuntimeException>
throwException0(
t);
}
}
@
SuppressWarnings("unchecked")
private static <E extends
Throwable> void
throwException0(
Throwable t) throws E {
throw (E)
t;
}
/**
* Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
*/
public static <K, V>
ConcurrentMap<K, V>
newConcurrentHashMap() {
return new
ConcurrentHashMap<K, V>();
}
/**
* Creates a new fastest {@link LongCounter} implementation for the current platform.
*/
public static
LongCounter newLongCounter() {
if (
javaVersion() >= 8) {
return new
LongAdderCounter();
} else {
return new
AtomicLongCounter();
}
}
/**
* Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
*/
public static <K, V>
ConcurrentMap<K, V>
newConcurrentHashMap(int
initialCapacity) {
return new
ConcurrentHashMap<K, V>(
initialCapacity);
}
/**
* Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
*/
public static <K, V>
ConcurrentMap<K, V>
newConcurrentHashMap(int
initialCapacity, float
loadFactor) {
return new
ConcurrentHashMap<K, V>(
initialCapacity,
loadFactor);
}
/**
* Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
*/
public static <K, V>
ConcurrentMap<K, V>
newConcurrentHashMap(
int
initialCapacity, float
loadFactor, int
concurrencyLevel) {
return new
ConcurrentHashMap<K, V>(
initialCapacity,
loadFactor,
concurrencyLevel);
}
/**
* Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
*/
public static <K, V>
ConcurrentMap<K, V>
newConcurrentHashMap(
Map<? extends K, ? extends V>
map) {
return new
ConcurrentHashMap<K, V>(
map);
}
/**
* Try to deallocate the specified direct {@link ByteBuffer}. Please note this method does nothing if
* the current platform does not support this operation or the specified buffer is not a direct buffer.
*/
public static void
freeDirectBuffer(
ByteBuffer buffer) {
CLEANER.
freeDirectBuffer(
buffer);
}
public static long
directBufferAddress(
ByteBuffer buffer) {
return
PlatformDependent0.
directBufferAddress(
buffer);
}
public static
ByteBuffer directBuffer(long
memoryAddress, int
size) {
if (
PlatformDependent0.
hasDirectBufferNoCleanerConstructor()) {
return
PlatformDependent0.
newDirectBuffer(
memoryAddress,
size);
}
throw new
UnsupportedOperationException(
"sun.misc.Unsafe or java.nio.DirectByteBuffer.<init>(long, int) not available");
}
public static int
getInt(
Object object, long
fieldOffset) {
return
PlatformDependent0.
getInt(
object,
fieldOffset);
}
public static byte
getByte(long
address) {
return
PlatformDependent0.
getByte(
address);
}
public static short
getShort(long
address) {
return
PlatformDependent0.
getShort(
address);
}
public static int
getInt(long
address) {
return
PlatformDependent0.
getInt(
address);
}
public static long
getLong(long
address) {
return
PlatformDependent0.
getLong(
address);
}
public static byte
getByte(byte[]
data, int
index) {
return
PlatformDependent0.
getByte(
data,
index);
}
public static short
getShort(byte[]
data, int
index) {
return
PlatformDependent0.
getShort(
data,
index);
}
public static int
getInt(byte[]
data, int
index) {
return
PlatformDependent0.
getInt(
data,
index);
}
public static long
getLong(byte[]
data, int
index) {
return
PlatformDependent0.
getLong(
data,
index);
}
private static long
getLongSafe(byte[]
bytes, int
offset) {
if (
BIG_ENDIAN_NATIVE_ORDER) {
return (long)
bytes[
offset] << 56 |
((long)
bytes[
offset + 1] & 0xff) << 48 |
((long)
bytes[
offset + 2] & 0xff) << 40 |
((long)
bytes[
offset + 3] & 0xff) << 32 |
((long)
bytes[
offset + 4] & 0xff) << 24 |
((long)
bytes[
offset + 5] & 0xff) << 16 |
((long)
bytes[
offset + 6] & 0xff) << 8 |
(long)
bytes[
offset + 7] & 0xff;
}
return (long)
bytes[
offset] & 0xff |
((long)
bytes[
offset + 1] & 0xff) << 8 |
((long)
bytes[
offset + 2] & 0xff) << 16 |
((long)
bytes[
offset + 3] & 0xff) << 24 |
((long)
bytes[
offset + 4] & 0xff) << 32 |
((long)
bytes[
offset + 5] & 0xff) << 40 |
((long)
bytes[
offset + 6] & 0xff) << 48 |
(long)
bytes[
offset + 7] << 56;
}
private static int
getIntSafe(byte[]
bytes, int
offset) {
if (
BIG_ENDIAN_NATIVE_ORDER) {
return
bytes[
offset] << 24 |
(
bytes[
offset + 1] & 0xff) << 16 |
(
bytes[
offset + 2] & 0xff) << 8 |
bytes[
offset + 3] & 0xff;
}
return
bytes[
offset] & 0xff |
(
bytes[
offset + 1] & 0xff) << 8 |
(
bytes[
offset + 2] & 0xff) << 16 |
bytes[
offset + 3] << 24;
}
private static short
getShortSafe(byte[]
bytes, int
offset) {
if (
BIG_ENDIAN_NATIVE_ORDER) {
return (short) (
bytes[
offset] << 8 | (
bytes[
offset + 1] & 0xff));
}
return (short) (
bytes[
offset] & 0xff | (
bytes[
offset + 1] << 8));
}
/**
* Identical to {@link PlatformDependent0#hashCodeAsciiCompute(long, int)} but for {@link CharSequence}.
*/
private static int
hashCodeAsciiCompute(
CharSequence value, int
offset, int
hash) {
if (
BIG_ENDIAN_NATIVE_ORDER) {
return
hash *
HASH_CODE_C1 +
// Low order int
hashCodeAsciiSanitizeInt(
value,
offset + 4) *
HASH_CODE_C2 +
// High order int
hashCodeAsciiSanitizeInt(
value,
offset);
}
return
hash *
HASH_CODE_C1 +
// Low order int
hashCodeAsciiSanitizeInt(
value,
offset) *
HASH_CODE_C2 +
// High order int
hashCodeAsciiSanitizeInt(
value,
offset + 4);
}
/**
* Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(int)} but for {@link CharSequence}.
*/
private static int
hashCodeAsciiSanitizeInt(
CharSequence value, int
offset) {
if (
BIG_ENDIAN_NATIVE_ORDER) {
// mimic a unsafe.getInt call on a big endian machine
return (
value.
charAt(
offset + 3) & 0x1f) |
(
value.
charAt(
offset + 2) & 0x1f) << 8 |
(
value.
charAt(
offset + 1) & 0x1f) << 16 |
(
value.
charAt(
offset) & 0x1f) << 24;
}
return (
value.
charAt(
offset + 3) & 0x1f) << 24 |
(
value.
charAt(
offset + 2) & 0x1f) << 16 |
(
value.
charAt(
offset + 1) & 0x1f) << 8 |
(
value.
charAt(
offset) & 0x1f);
}
/**
* Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(short)} but for {@link CharSequence}.
*/
private static int
hashCodeAsciiSanitizeShort(
CharSequence value, int
offset) {
if (
BIG_ENDIAN_NATIVE_ORDER) {
// mimic a unsafe.getShort call on a big endian machine
return (
value.
charAt(
offset + 1) & 0x1f) |
(
value.
charAt(
offset) & 0x1f) << 8;
}
return (
value.
charAt(
offset + 1) & 0x1f) << 8 |
(
value.
charAt(
offset) & 0x1f);
}
/**
* Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(byte)} but for {@link CharSequence}.
*/
private static int
hashCodeAsciiSanitizeByte(char
value) {
return
value & 0x1f;
}
public static void
putByte(long
address, byte
value) {
PlatformDependent0.
putByte(
address,
value);
}
public static void
putShort(long
address, short
value) {
PlatformDependent0.
putShort(
address,
value);
}
public static void
putInt(long
address, int
value) {
PlatformDependent0.
putInt(
address,
value);
}
public static void
putLong(long
address, long
value) {
PlatformDependent0.
putLong(
address,
value);
}
public static void
putByte(byte[]
data, int
index, byte
value) {
PlatformDependent0.
putByte(
data,
index,
value);
}
public static void
putShort(byte[]
data, int
index, short
value) {
PlatformDependent0.
putShort(
data,
index,
value);
}
public static void
putInt(byte[]
data, int
index, int
value) {
PlatformDependent0.
putInt(
data,
index,
value);
}
public static void
putLong(byte[]
data, int
index, long
value) {
PlatformDependent0.
putLong(
data,
index,
value);
}
public static void
copyMemory(long
srcAddr, long
dstAddr, long
length) {
PlatformDependent0.
copyMemory(
srcAddr,
dstAddr,
length);
}
public static void
copyMemory(byte[]
src, int
srcIndex, long
dstAddr, long
length) {
PlatformDependent0.
copyMemory(
src,
BYTE_ARRAY_BASE_OFFSET +
srcIndex, null,
dstAddr,
length);
}
public static void
copyMemory(long
srcAddr, byte[]
dst, int
dstIndex, long
length) {
PlatformDependent0.
copyMemory(null,
srcAddr,
dst,
BYTE_ARRAY_BASE_OFFSET +
dstIndex,
length);
}
public static void
setMemory(byte[]
dst, int
dstIndex, long
bytes, byte
value) {
PlatformDependent0.
setMemory(
dst,
BYTE_ARRAY_BASE_OFFSET +
dstIndex,
bytes,
value);
}
public static void
setMemory(long
address, long
bytes, byte
value) {
PlatformDependent0.
setMemory(
address,
bytes,
value);
}
/**
* Allocate a new {@link ByteBuffer} with the given {@code capacity}. {@link ByteBuffer}s allocated with
* this method <strong>MUST</strong> be deallocated via {@link #freeDirectNoCleaner(ByteBuffer)}.
*/
public static
ByteBuffer allocateDirectNoCleaner(int
capacity) {
assert
USE_DIRECT_BUFFER_NO_CLEANER;
incrementMemoryCounter(
capacity);
try {
return
PlatformDependent0.
allocateDirectNoCleaner(
capacity);
} catch (
Throwable e) {
decrementMemoryCounter(
capacity);
throwException(
e);
return null;
}
}
/**
* Reallocate a new {@link ByteBuffer} with the given {@code capacity}. {@link ByteBuffer}s reallocated with
* this method <strong>MUST</strong> be deallocated via {@link #freeDirectNoCleaner(ByteBuffer)}.
*/
public static
ByteBuffer reallocateDirectNoCleaner(
ByteBuffer buffer, int
capacity) {
assert
USE_DIRECT_BUFFER_NO_CLEANER;
int
len =
capacity -
buffer.
capacity();
incrementMemoryCounter(
len);
try {
return
PlatformDependent0.
reallocateDirectNoCleaner(
buffer,
capacity);
} catch (
Throwable e) {
decrementMemoryCounter(
len);
throwException(
e);
return null;
}
}
/**
* This method <strong>MUST</strong> only be called for {@link ByteBuffer}s that were allocated via
* {@link #allocateDirectNoCleaner(int)}.
*/
public static void
freeDirectNoCleaner(
ByteBuffer buffer) {
assert
USE_DIRECT_BUFFER_NO_CLEANER;
int
capacity =
buffer.
capacity();
PlatformDependent0.
freeMemory(
PlatformDependent0.
directBufferAddress(
buffer));
decrementMemoryCounter(
capacity);
}
private static void
incrementMemoryCounter(int
capacity) {
if (
DIRECT_MEMORY_COUNTER != null) {
for (;;) {
long
usedMemory =
DIRECT_MEMORY_COUNTER.
get();
long
newUsedMemory =
usedMemory +
capacity;
if (
newUsedMemory >
DIRECT_MEMORY_LIMIT) {
throw new
OutOfDirectMemoryError("failed to allocate " +
capacity
+ " byte(s) of direct memory (used: " +
usedMemory + ", max: " +
DIRECT_MEMORY_LIMIT + ')');
}
if (
DIRECT_MEMORY_COUNTER.
compareAndSet(
usedMemory,
newUsedMemory)) {
break;
}
}
}
}
private static void
decrementMemoryCounter(int
capacity) {
if (
DIRECT_MEMORY_COUNTER != null) {
long
usedMemory =
DIRECT_MEMORY_COUNTER.
addAndGet(-
capacity);
assert
usedMemory >= 0;
}
}
public static boolean
useDirectBufferNoCleaner() {
return
USE_DIRECT_BUFFER_NO_CLEANER;
}
/**
* Compare two {@code byte} arrays for equality. For performance reasons no bounds checking on the
* parameters is performed.
*
* @param bytes1 the first byte array.
* @param startPos1 the position (inclusive) to start comparing in {@code bytes1}.
* @param bytes2 the second byte array.
* @param startPos2 the position (inclusive) to start comparing in {@code bytes2}.
* @param length the amount of bytes to compare. This is assumed to be validated as not going out of bounds
* by the caller.
*/
public static boolean
equals(byte[]
bytes1, int
startPos1, byte[]
bytes2, int
startPos2, int
length) {
return !
hasUnsafe() || !
unalignedAccess() ?
equalsSafe(
bytes1,
startPos1,
bytes2,
startPos2,
length) :
PlatformDependent0.
equals(
bytes1,
startPos1,
bytes2,
startPos2,
length);
}
/**
* Determine if a subsection of an array is zero.
* @param bytes The byte array.
* @param startPos The starting index (inclusive) in {@code bytes}.
* @param length The amount of bytes to check for zero.
* @return {@code false} if {@code bytes[startPos:startsPos+length)} contains a value other than zero.
*/
public static boolean
isZero(byte[]
bytes, int
startPos, int
length) {
return !
hasUnsafe() || !
unalignedAccess() ?
isZeroSafe(
bytes,
startPos,
length) :
PlatformDependent0.
isZero(
bytes,
startPos,
length);
}
/**
* Compare two {@code byte} arrays for equality without leaking timing information.
* For performance reasons no bounds checking on the parameters is performed.
* <p>
* The {@code int} return type is intentional and is designed to allow cascading of constant time operations:
* <pre>
* byte[] s1 = new {1, 2, 3};
* byte[] s2 = new {1, 2, 3};
* byte[] s3 = new {1, 2, 3};
* byte[] s4 = new {4, 5, 6};
* boolean equals = (equalsConstantTime(s1, 0, s2, 0, s1.length) &
* equalsConstantTime(s3, 0, s4, 0, s3.length)) != 0;
* </pre>
* @param bytes1 the first byte array.
* @param startPos1 the position (inclusive) to start comparing in {@code bytes1}.
* @param bytes2 the second byte array.
* @param startPos2 the position (inclusive) to start comparing in {@code bytes2}.
* @param length the amount of bytes to compare. This is assumed to be validated as not going out of bounds
* by the caller.
* @return {@code 0} if not equal. {@code 1} if equal.
*/
public static int
equalsConstantTime(byte[]
bytes1, int
startPos1, byte[]
bytes2, int
startPos2, int
length) {
return !
hasUnsafe() || !
unalignedAccess() ?
ConstantTimeUtils.
equalsConstantTime(
bytes1,
startPos1,
bytes2,
startPos2,
length) :
PlatformDependent0.
equalsConstantTime(
bytes1,
startPos1,
bytes2,
startPos2,
length);
}
/**
* Calculate a hash code of a byte array assuming ASCII character encoding.
* The resulting hash code will be case insensitive.
* @param bytes The array which contains the data to hash.
* @param startPos What index to start generating a hash code in {@code bytes}
* @param length The amount of bytes that should be accounted for in the computation.
* @return The hash code of {@code bytes} assuming ASCII character encoding.
* The resulting hash code will be case insensitive.
*/
public static int
hashCodeAscii(byte[]
bytes, int
startPos, int
length) {
return !
hasUnsafe() || !
unalignedAccess() ?
hashCodeAsciiSafe(
bytes,
startPos,
length) :
PlatformDependent0.
hashCodeAscii(
bytes,
startPos,
length);
}
/**
* Calculate a hash code of a byte array assuming ASCII character encoding.
* The resulting hash code will be case insensitive.
* <p>
* This method assumes that {@code bytes} is equivalent to a {@code byte[]} but just using {@link CharSequence}
* for storage. The upper most byte of each {@code char} from {@code bytes} is ignored.
* @param bytes The array which contains the data to hash (assumed to be equivalent to a {@code byte[]}).
* @return The hash code of {@code bytes} assuming ASCII character encoding.
* The resulting hash code will be case insensitive.
*/
public static int
hashCodeAscii(
CharSequence bytes) {
int
hash =
HASH_CODE_ASCII_SEED;
final int
remainingBytes =
bytes.
length() & 7;
// Benchmarking shows that by just naively looping for inputs 8~31 bytes long we incur a relatively large
// performance penalty (only achieve about 60% performance of loop which iterates over each char). So because
// of this we take special provisions to unroll the looping for these conditions.
switch (
bytes.
length()) {
case 31:
case 30:
case 29:
case 28:
case 27:
case 26:
case 25:
case 24:
hash =
hashCodeAsciiCompute(
bytes,
bytes.
length() - 24,
hashCodeAsciiCompute(
bytes,
bytes.
length() - 16,
hashCodeAsciiCompute(
bytes,
bytes.
length() - 8,
hash)));
break;
case 23:
case 22:
case 21:
case 20:
case 19:
case 18:
case 17:
case 16:
hash =
hashCodeAsciiCompute(
bytes,
bytes.
length() - 16,
hashCodeAsciiCompute(
bytes,
bytes.
length() - 8,
hash));
break;
case 15:
case 14:
case 13:
case 12:
case 11:
case 10:
case 9:
case 8:
hash =
hashCodeAsciiCompute(
bytes,
bytes.
length() - 8,
hash);
break;
case 7:
case 6:
case 5:
case 4:
case 3:
case 2:
case 1:
case 0:
break;
default:
for (int
i =
bytes.
length() - 8;
i >=
remainingBytes;
i -= 8) {
hash =
hashCodeAsciiCompute(
bytes,
i,
hash);
}
break;
}
switch(
remainingBytes) {
case 7:
return ((
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitizeByte(
bytes.
charAt(0)))
*
HASH_CODE_C2 +
hashCodeAsciiSanitizeShort(
bytes, 1))
*
HASH_CODE_C1 +
hashCodeAsciiSanitizeInt(
bytes, 3);
case 6:
return (
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitizeShort(
bytes, 0))
*
HASH_CODE_C2 +
hashCodeAsciiSanitizeInt(
bytes, 2);
case 5:
return (
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitizeByte(
bytes.
charAt(0)))
*
HASH_CODE_C2 +
hashCodeAsciiSanitizeInt(
bytes, 1);
case 4:
return
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitizeInt(
bytes, 0);
case 3:
return (
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitizeByte(
bytes.
charAt(0)))
*
HASH_CODE_C2 +
hashCodeAsciiSanitizeShort(
bytes, 1);
case 2:
return
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitizeShort(
bytes, 0);
case 1:
return
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitizeByte(
bytes.
charAt(0));
default:
return
hash;
}
}
private static final class
Mpsc {
private static final boolean
USE_MPSC_CHUNKED_ARRAY_QUEUE;
private
Mpsc() {
}
static {
Object unsafe = null;
if (
hasUnsafe()) {
// jctools goes through its own process of initializing unsafe; of
// course, this requires permissions which might not be granted to calling code, so we
// must mark this block as privileged too
unsafe =
AccessController.
doPrivileged(new
PrivilegedAction<
Object>() {
@
Override
public
Object run() {
// force JCTools to initialize unsafe
return
UnsafeAccess.
UNSAFE;
}
});
}
if (
unsafe == null) {
logger.
debug("org.jctools-core.MpscChunkedArrayQueue: unavailable");
USE_MPSC_CHUNKED_ARRAY_QUEUE = false;
} else {
logger.
debug("org.jctools-core.MpscChunkedArrayQueue: available");
USE_MPSC_CHUNKED_ARRAY_QUEUE = true;
}
}
static <T>
Queue<T>
newMpscQueue(final int
maxCapacity) {
// Calculate the max capacity which can not be bigger then MAX_ALLOWED_MPSC_CAPACITY.
// This is forced by the MpscChunkedArrayQueue implementation as will try to round it
// up to the next power of two and so will overflow otherwise.
final int
capacity =
max(
min(
maxCapacity,
MAX_ALLOWED_MPSC_CAPACITY),
MIN_MAX_MPSC_CAPACITY);
return
USE_MPSC_CHUNKED_ARRAY_QUEUE ? new
MpscChunkedArrayQueue<T>(
MPSC_CHUNK_SIZE,
capacity)
: new
MpscGrowableAtomicArrayQueue<T>(
MPSC_CHUNK_SIZE,
capacity);
}
static <T>
Queue<T>
newMpscQueue() {
return
USE_MPSC_CHUNKED_ARRAY_QUEUE ? new
MpscUnboundedArrayQueue<T>(
MPSC_CHUNK_SIZE)
: new
MpscUnboundedAtomicArrayQueue<T>(
MPSC_CHUNK_SIZE);
}
}
/**
* Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
* consumer (one thread!).
* @return A MPSC queue which may be unbounded.
*/
public static <T>
Queue<T>
newMpscQueue() {
return
Mpsc.
newMpscQueue();
}
/**
* Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
* consumer (one thread!).
*/
public static <T>
Queue<T>
newMpscQueue(final int
maxCapacity) {
return
Mpsc.
newMpscQueue(
maxCapacity);
}
/**
* Create a new {@link Queue} which is safe to use for single producer (one thread!) and a single
* consumer (one thread!).
*/
public static <T>
Queue<T>
newSpscQueue() {
return
hasUnsafe() ? new
SpscLinkedQueue<T>() : new
SpscLinkedAtomicQueue<T>();
}
/**
* Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
* consumer (one thread!) with the given fixes {@code capacity}.
*/
public static <T>
Queue<T>
newFixedMpscQueue(int
capacity) {
return
hasUnsafe() ? new
MpscArrayQueue<T>(
capacity) : new
MpscAtomicArrayQueue<T>(
capacity);
}
/**
* Return the {@link ClassLoader} for the given {@link Class}.
*/
public static
ClassLoader getClassLoader(final
Class<?>
clazz) {
return
PlatformDependent0.
getClassLoader(
clazz);
}
/**
* Return the context {@link ClassLoader} for the current {@link Thread}.
*/
public static
ClassLoader getContextClassLoader() {
return
PlatformDependent0.
getContextClassLoader();
}
/**
* Return the system {@link ClassLoader}.
*/
public static
ClassLoader getSystemClassLoader() {
return
PlatformDependent0.
getSystemClassLoader();
}
/**
* Returns a new concurrent {@link Deque}.
*/
public static <C>
Deque<C>
newConcurrentDeque() {
if (
javaVersion() < 7) {
return new
LinkedBlockingDeque<C>();
} else {
return new
ConcurrentLinkedDeque<C>();
}
}
/**
* Return a {@link Random} which is not-threadsafe and so can only be used from the same thread.
*/
public static
Random threadLocalRandom() {
return
RANDOM_PROVIDER.
current();
}
private static boolean
isWindows0() {
boolean
windows =
SystemPropertyUtil.
get("os.name", "").
toLowerCase(
Locale.
US).
contains("win");
if (
windows) {
logger.
debug("Platform: Windows");
}
return
windows;
}
private static boolean
isOsx0() {
String osname =
SystemPropertyUtil.
get("os.name", "").
toLowerCase(
Locale.
US)
.
replaceAll("[^a-z0-9]+", "");
boolean
osx =
osname.
startsWith("macosx") ||
osname.
startsWith("osx");
if (
osx) {
logger.
debug("Platform: MacOS");
}
return
osx;
}
private static boolean
maybeSuperUser0() {
String username =
SystemPropertyUtil.
get("user.name");
if (
isWindows()) {
return "Administrator".
equals(
username);
}
// Check for root and toor as some BSDs have a toor user that is basically the same as root.
return "root".
equals(
username) || "toor".
equals(
username);
}
private static
Throwable unsafeUnavailabilityCause0() {
if (
isAndroid()) {
logger.
debug("sun.misc.Unsafe: unavailable (Android)");
return new
UnsupportedOperationException("sun.misc.Unsafe: unavailable (Android)");
}
Throwable cause =
PlatformDependent0.
getUnsafeUnavailabilityCause();
if (
cause != null) {
return
cause;
}
try {
boolean
hasUnsafe =
PlatformDependent0.
hasUnsafe();
logger.
debug("sun.misc.Unsafe: {}",
hasUnsafe ? "available" : "unavailable");
return
hasUnsafe ? null :
PlatformDependent0.
getUnsafeUnavailabilityCause();
} catch (
Throwable t) {
logger.
trace("Could not determine if Unsafe is available",
t);
// Probably failed to initialize PlatformDependent0.
return new
UnsupportedOperationException("Could not determine if Unsafe is available",
t);
}
}
private static long
maxDirectMemory0() {
long
maxDirectMemory = 0;
ClassLoader systemClassLoader = null;
try {
systemClassLoader =
getSystemClassLoader();
// When using IBM J9 / Eclipse OpenJ9 we should not use VM.maxDirectMemory() as it not reflects the
// correct value.
// See:
// - https://github.com/netty/netty/issues/7654
String vmName =
SystemPropertyUtil.
get("java.vm.name", "").
toLowerCase();
if (!
vmName.
startsWith("ibm j9") &&
// https://github.com/eclipse/openj9/blob/openj9-0.8.0/runtime/include/vendor_version.h#L53
!
vmName.
startsWith("eclipse openj9")) {
// Try to get from sun.misc.VM.maxDirectMemory() which should be most accurate.
Class<?>
vmClass =
Class.
forName("sun.misc.VM", true,
systemClassLoader);
Method m =
vmClass.
getDeclaredMethod("maxDirectMemory");
maxDirectMemory = ((
Number)
m.
invoke(null)).
longValue();
}
} catch (
Throwable ignored) {
// Ignore
}
if (
maxDirectMemory > 0) {
return
maxDirectMemory;
}
try {
// Now try to get the JVM option (-XX:MaxDirectMemorySize) and parse it.
// Note that we are using reflection because Android doesn't have these classes.
Class<?>
mgmtFactoryClass =
Class.
forName(
"java.lang.management.ManagementFactory", true,
systemClassLoader);
Class<?>
runtimeClass =
Class.
forName(
"java.lang.management.RuntimeMXBean", true,
systemClassLoader);
Object runtime =
mgmtFactoryClass.
getDeclaredMethod("getRuntimeMXBean").
invoke(null);
@
SuppressWarnings("unchecked")
List<
String>
vmArgs = (
List<
String>)
runtimeClass.
getDeclaredMethod("getInputArguments").
invoke(
runtime);
for (int
i =
vmArgs.
size() - 1;
i >= 0;
i --) {
Matcher m =
MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN.
matcher(
vmArgs.
get(
i));
if (!
m.
matches()) {
continue;
}
maxDirectMemory =
Long.
parseLong(
m.
group(1));
switch (
m.
group(2).
charAt(0)) {
case 'k': case 'K':
maxDirectMemory *= 1024;
break;
case 'm': case 'M':
maxDirectMemory *= 1024 * 1024;
break;
case 'g': case 'G':
maxDirectMemory *= 1024 * 1024 * 1024;
break;
}
break;
}
} catch (
Throwable ignored) {
// Ignore
}
if (
maxDirectMemory <= 0) {
maxDirectMemory =
Runtime.
getRuntime().
maxMemory();
logger.
debug("maxDirectMemory: {} bytes (maybe)",
maxDirectMemory);
} else {
logger.
debug("maxDirectMemory: {} bytes",
maxDirectMemory);
}
return
maxDirectMemory;
}
private static
File tmpdir0() {
File f;
try {
f =
toDirectory(
SystemPropertyUtil.
get("io.netty.tmpdir"));
if (
f != null) {
logger.
debug("-Dio.netty.tmpdir: {}",
f);
return
f;
}
f =
toDirectory(
SystemPropertyUtil.
get("java.io.tmpdir"));
if (
f != null) {
logger.
debug("-Dio.netty.tmpdir: {} (java.io.tmpdir)",
f);
return
f;
}
// This shouldn't happen, but just in case ..
if (
isWindows()) {
f =
toDirectory(
System.
getenv("TEMP"));
if (
f != null) {
logger.
debug("-Dio.netty.tmpdir: {} (%TEMP%)",
f);
return
f;
}
String userprofile =
System.
getenv("USERPROFILE");
if (
userprofile != null) {
f =
toDirectory(
userprofile + "\\AppData\\Local\\Temp");
if (
f != null) {
logger.
debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\AppData\\Local\\Temp)",
f);
return
f;
}
f =
toDirectory(
userprofile + "\\Local Settings\\Temp");
if (
f != null) {
logger.
debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\Local Settings\\Temp)",
f);
return
f;
}
}
} else {
f =
toDirectory(
System.
getenv("TMPDIR"));
if (
f != null) {
logger.
debug("-Dio.netty.tmpdir: {} ($TMPDIR)",
f);
return
f;
}
}
} catch (
Throwable ignored) {
// Environment variable inaccessible
}
// Last resort.
if (
isWindows()) {
f = new
File("C:\\Windows\\Temp");
} else {
f = new
File("/tmp");
}
logger.
warn("Failed to get the temporary directory; falling back to: {}",
f);
return
f;
}
@
SuppressWarnings("ResultOfMethodCallIgnored")
private static
File toDirectory(
String path) {
if (
path == null) {
return null;
}
File f = new
File(
path);
f.
mkdirs();
if (!
f.
isDirectory()) {
return null;
}
try {
return
f.
getAbsoluteFile();
} catch (
Exception ignored) {
return
f;
}
}
private static int
bitMode0() {
// Check user-specified bit mode first.
int
bitMode =
SystemPropertyUtil.
getInt("io.netty.bitMode", 0);
if (
bitMode > 0) {
logger.
debug("-Dio.netty.bitMode: {}",
bitMode);
return
bitMode;
}
// And then the vendor specific ones which is probably most reliable.
bitMode =
SystemPropertyUtil.
getInt("sun.arch.data.model", 0);
if (
bitMode > 0) {
logger.
debug("-Dio.netty.bitMode: {} (sun.arch.data.model)",
bitMode);
return
bitMode;
}
bitMode =
SystemPropertyUtil.
getInt("com.ibm.vm.bitmode", 0);
if (
bitMode > 0) {
logger.
debug("-Dio.netty.bitMode: {} (com.ibm.vm.bitmode)",
bitMode);
return
bitMode;
}
// os.arch also gives us a good hint.
String arch =
SystemPropertyUtil.
get("os.arch", "").
toLowerCase(
Locale.
US).
trim();
if ("amd64".
equals(
arch) || "x86_64".
equals(
arch)) {
bitMode = 64;
} else if ("i386".
equals(
arch) || "i486".
equals(
arch) || "i586".
equals(
arch) || "i686".
equals(
arch)) {
bitMode = 32;
}
if (
bitMode > 0) {
logger.
debug("-Dio.netty.bitMode: {} (os.arch: {})",
bitMode,
arch);
}
// Last resort: guess from VM name and then fall back to most common 64-bit mode.
String vm =
SystemPropertyUtil.
get("java.vm.name", "").
toLowerCase(
Locale.
US);
Pattern BIT_PATTERN =
Pattern.
compile("([1-9][0-9]+)-?bit");
Matcher m =
BIT_PATTERN.
matcher(
vm);
if (
m.
find()) {
return
Integer.
parseInt(
m.
group(1));
} else {
return 64;
}
}
private static int
addressSize0() {
if (!
hasUnsafe()) {
return -1;
}
return
PlatformDependent0.
addressSize();
}
private static long
byteArrayBaseOffset0() {
if (!
hasUnsafe()) {
return -1;
}
return
PlatformDependent0.
byteArrayBaseOffset();
}
private static boolean
equalsSafe(byte[]
bytes1, int
startPos1, byte[]
bytes2, int
startPos2, int
length) {
final int
end =
startPos1 +
length;
for (;
startPos1 <
end; ++
startPos1, ++
startPos2) {
if (
bytes1[
startPos1] !=
bytes2[
startPos2]) {
return false;
}
}
return true;
}
private static boolean
isZeroSafe(byte[]
bytes, int
startPos, int
length) {
final int
end =
startPos +
length;
for (;
startPos <
end; ++
startPos) {
if (
bytes[
startPos] != 0) {
return false;
}
}
return true;
}
/**
* Package private for testing purposes only!
*/
static int
hashCodeAsciiSafe(byte[]
bytes, int
startPos, int
length) {
int
hash =
HASH_CODE_ASCII_SEED;
final int
remainingBytes =
length & 7;
final int
end =
startPos +
remainingBytes;
for (int
i =
startPos - 8 +
length;
i >=
end;
i -= 8) {
hash =
PlatformDependent0.
hashCodeAsciiCompute(
getLongSafe(
bytes,
i),
hash);
}
switch(
remainingBytes) {
case 7:
return ((
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitize(
bytes[
startPos]))
*
HASH_CODE_C2 +
hashCodeAsciiSanitize(
getShortSafe(
bytes,
startPos + 1)))
*
HASH_CODE_C1 +
hashCodeAsciiSanitize(
getIntSafe(
bytes,
startPos + 3));
case 6:
return (
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitize(
getShortSafe(
bytes,
startPos)))
*
HASH_CODE_C2 +
hashCodeAsciiSanitize(
getIntSafe(
bytes,
startPos + 2));
case 5:
return (
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitize(
bytes[
startPos]))
*
HASH_CODE_C2 +
hashCodeAsciiSanitize(
getIntSafe(
bytes,
startPos + 1));
case 4:
return
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitize(
getIntSafe(
bytes,
startPos));
case 3:
return (
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitize(
bytes[
startPos]))
*
HASH_CODE_C2 +
hashCodeAsciiSanitize(
getShortSafe(
bytes,
startPos + 1));
case 2:
return
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitize(
getShortSafe(
bytes,
startPos));
case 1:
return
hash *
HASH_CODE_C1 +
hashCodeAsciiSanitize(
bytes[
startPos]);
default:
return
hash;
}
}
public static
String normalizedArch() {
return
NORMALIZED_ARCH;
}
public static
String normalizedOs() {
return
NORMALIZED_OS;
}
private static
String normalize(
String value) {
return
value.
toLowerCase(
Locale.
US).
replaceAll("[^a-z0-9]+", "");
}
private static
String normalizeArch(
String value) {
value =
normalize(
value);
if (
value.
matches("^(x8664|amd64|ia32e|em64t|x64)$")) {
return "x86_64";
}
if (
value.
matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) {
return "x86_32";
}
if (
value.
matches("^(ia64|itanium64)$")) {
return "itanium_64";
}
if (
value.
matches("^(sparc|sparc32)$")) {
return "sparc_32";
}
if (
value.
matches("^(sparcv9|sparc64)$")) {
return "sparc_64";
}
if (
value.
matches("^(arm|arm32)$")) {
return "arm_32";
}
if ("aarch64".
equals(
value)) {
return "aarch_64";
}
if (
value.
matches("^(ppc|ppc32)$")) {
return "ppc_32";
}
if ("ppc64".
equals(
value)) {
return "ppc_64";
}
if ("ppc64le".
equals(
value)) {
return "ppcle_64";
}
if ("s390".
equals(
value)) {
return "s390_32";
}
if ("s390x".
equals(
value)) {
return "s390_64";
}
return "unknown";
}
private static
String normalizeOs(
String value) {
value =
normalize(
value);
if (
value.
startsWith("aix")) {
return "aix";
}
if (
value.
startsWith("hpux")) {
return "hpux";
}
if (
value.
startsWith("os400")) {
// Avoid the names such as os4000
if (
value.
length() <= 5 || !
Character.
isDigit(
value.
charAt(5))) {
return "os400";
}
}
if (
value.
startsWith("linux")) {
return "linux";
}
if (
value.
startsWith("macosx") ||
value.
startsWith("osx")) {
return "osx";
}
if (
value.
startsWith("freebsd")) {
return "freebsd";
}
if (
value.
startsWith("openbsd")) {
return "openbsd";
}
if (
value.
startsWith("netbsd")) {
return "netbsd";
}
if (
value.
startsWith("solaris") ||
value.
startsWith("sunos")) {
return "sunos";
}
if (
value.
startsWith("windows")) {
return "windows";
}
return "unknown";
}
private static final class
AtomicLongCounter extends
AtomicLong implements
LongCounter {
private static final long
serialVersionUID = 4074772784610639305L;
@
Override
public void
add(long
delta) {
addAndGet(
delta);
}
@
Override
public void
increment() {
incrementAndGet();
}
@
Override
public void
decrement() {
decrementAndGet();
}
@
Override
public long
value() {
return
get();
}
}
private interface
ThreadLocalRandomProvider {
Random current();
}
private
PlatformDependent() {
// only static method supported
}
}