/*
* Copyright 2016 higherfrequencytrading.com
*
* Licensed 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 net.openhft.affinity;
import net.openhft.affinity.impl.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.BitSet;
/**
* Library to wrap low level JNI or JNA calls. Can be called without needing to know the actual
* implementation used.
*
* @author peter.lawrey
*/
public enum Affinity {
;
static final Logger LOGGER = LoggerFactory.getLogger(Affinity.class);
@NotNull
private static final IAffinity AFFINITY_IMPL;
private static Boolean JNAAvailable;
static {
String osName = System.getProperty("os.name");
if (osName.contains("Win") && isWindowsJNAAffinityUsable()) {
LOGGER.trace("Using Windows JNA-based affinity control implementation");
AFFINITY_IMPL = WindowsJNAAffinity.INSTANCE;
} else if (osName.contains("x")) {
/*if (osName.startsWith("Linux") && NativeAffinity.LOADED) {
LOGGER.trace("Using Linux JNI-based affinity control implementation");
AFFINITY_IMPL = NativeAffinity.INSTANCE;
} else*/
if (osName.startsWith("Linux") && isLinuxJNAAffinityUsable()) {
LOGGER.trace("Using Linux JNA-based affinity control implementation");
AFFINITY_IMPL = LinuxJNAAffinity.INSTANCE;
} else if (isPosixJNAAffinityUsable()) {
LOGGER.trace("Using Posix JNA-based affinity control implementation");
AFFINITY_IMPL = PosixJNAAffinity.INSTANCE;
} else {
LOGGER.info("Using dummy affinity control implementation");
AFFINITY_IMPL = NullAffinity.INSTANCE;
}
} else if (osName.contains("Mac") && isMacJNAAffinityUsable()) {
LOGGER.trace("Using MAC OSX JNA-based thread id implementation");
AFFINITY_IMPL = OSXJNAAffinity.INSTANCE;
} else if (osName.contains("SunOS") && isSolarisJNAAffinityUsable()) {
LOGGER.trace("Using Solaris JNA-based thread id implementation");
AFFINITY_IMPL = SolarisJNAAffinity.INSTANCE;
} else {
LOGGER.info("Using dummy affinity control implementation");
AFFINITY_IMPL = NullAffinity.INSTANCE;
}
}
public static IAffinity getAffinityImpl() {
return AFFINITY_IMPL;
}
private static boolean isWindowsJNAAffinityUsable() {
if (isJNAAvailable()) {
try {
return WindowsJNAAffinity.LOADED;
} catch (Throwable t) {
logThrowable(t, "Windows JNA-based affinity not usable because it failed to load!");
return false;
}
} else {
LOGGER.warn("Windows JNA-based affinity not usable due to JNA not being available!");
return false;
}
}
private static boolean isPosixJNAAffinityUsable() {
if (isJNAAvailable()) {
try {
return PosixJNAAffinity.LOADED;
} catch (Throwable t) {
logThrowable(t, "Posix JNA-based affinity not usable because it failed to load!");
return false;
}
} else {
LOGGER.warn("Posix JNA-based affinity not usable due to JNA not being available!");
return false;
}
}
private static boolean isLinuxJNAAffinityUsable() {
if (isJNAAvailable()) {
try {
return LinuxJNAAffinity.LOADED;
} catch (Throwable t) {
logThrowable(t, "Linux JNA-based affinity not usable because it failed to load!");
return false;
}
} else {
LOGGER.warn("Linux JNA-based affinity not usable due to JNA not being available!");
return false;
}
}
private static boolean isMacJNAAffinityUsable() {
if (isJNAAvailable()) {
return true;
} else {
LOGGER.warn("MAX OSX JNA-based affinity not usable due to JNA not being available!");
return false;
}
}
private static boolean isSolarisJNAAffinityUsable() {
if (isJNAAvailable()) {
return true;
} else {
LOGGER.warn("Solaris JNA-based affinity not usable due to JNA not being available!");
return false;
}
}
private static void logThrowable(Throwable t, String description) {
StringWriter sw = new StringWriter();
sw.append(description);
sw.append(" Reason: ");
t.printStackTrace(new PrintWriter(sw));
LOGGER.warn(sw.toString());
}
public static BitSet getAffinity() {
return AFFINITY_IMPL.getAffinity();
}
public static void setAffinity(final BitSet affinity) {
AFFINITY_IMPL.setAffinity(affinity);
}
public static void setAffinity(int cpu) {
BitSet affinity = new BitSet(Runtime.getRuntime().availableProcessors());
affinity.set(cpu);
setAffinity(affinity);
}
public static int getCpu() {
return AFFINITY_IMPL.getCpu();
}
public static int getThreadId() {
return AFFINITY_IMPL.getThreadId();
}
public static void setThreadId() {
try {
int threadId = Affinity.getThreadId();
final Field tid = Thread.class.getDeclaredField("tid");
tid.setAccessible(true);
final Thread thread = Thread.currentThread();
tid.setLong(thread, threadId);
Affinity.LOGGER.info("Set {} to thread id {}", thread.getName(), threadId);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
public static boolean isJNAAvailable() {
if (JNAAvailable == null)
try {
Class.forName("com.sun.jna.Platform");
JNAAvailable = true;
} catch (ClassNotFoundException ignored) {
JNAAvailable = false;
}
return JNAAvailable;
}
public static AffinityLock acquireLock() {
return isNonForkingAffinityAvailable() ? NonForkingAffinityLock.acquireLock() : AffinityLock.acquireLock();
}
public static AffinityLock acquireCore() {
return isNonForkingAffinityAvailable() ? NonForkingAffinityLock.acquireCore() : AffinityLock.acquireCore();
}
public static AffinityLock acquireLock(boolean bind) {
return isNonForkingAffinityAvailable() ? NonForkingAffinityLock.acquireLock(bind) : AffinityLock.acquireLock(bind);
}
public static AffinityLock acquireCore(boolean bind) {
return isNonForkingAffinityAvailable() ? NonForkingAffinityLock.acquireCore(bind) : AffinityLock.acquireCore(bind);
}
private static boolean isNonForkingAffinityAvailable() {
BootClassPath bootClassPath = BootClassPath.INSTANCE;
return bootClassPath.has("java.lang.ThreadTrackingGroup") && bootClassPath.has("java.lang.ThreadLifecycleListener");
}
public static void resetToBaseAffinity() {
Affinity.setAffinity(AffinityLock.BASE_AFFINITY);
}
}