
net.openhft.affinity.NonForkingAffinityLock Maven / Gradle / Ivy
Show all versions of affinity Show documentation
/*
* 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.NoCpuLayout;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
public class NonForkingAffinityLock extends AffinityLock implements ThreadLifecycleListener {
private static final Field GROUP_FIELD = makeThreadFieldModifiable("group");
private static final Field TARGET_FIELD = makeThreadFieldModifiable("target");
private static final LockInventory LOCK_INVENTORY = new LockInventory(new NoCpuLayout(PROCESSORS)) {
@Override
protected AffinityLock newLock(int cpuId, boolean base, boolean reservable) {
return new NonForkingAffinityLock(cpuId, base, reservable, this);
}
};
NonForkingAffinityLock(int cpuId, boolean base, boolean reservable, LockInventory lockInventory) {
super(cpuId, base, reservable, lockInventory);
}
/**
* Assign any free cpu to this thread.
*
* @return A handle for the current AffinityLock.
*/
public static AffinityLock acquireLock() {
return acquireLock(true);
}
/**
* Assign any free core to this thread.
*
* In reality, only one cpu is assigned, the rest of the threads for that core are reservable so they are not used.
*
* @return A handle for the current AffinityLock.
*/
public static AffinityLock acquireCore() {
return acquireCore(true);
}
/**
* Assign a cpu which can be bound to the current thread or another thread.
*
* This can be used for defining your thread layout centrally and passing the handle via dependency injection.
*
* @param bind if true, bind the current thread, if false, reserve a cpu which can be bound later.
* @return A handle for an affinity lock.
*/
public static AffinityLock acquireLock(boolean bind) {
return acquireLock(bind, -1, AffinityStrategies.ANY);
}
/**
* Assign a core(and all its cpus) which can be bound to the current thread or another thread.
*
* This can be used for defining your thread layout centrally and passing the handle via dependency injection.
*
* @param bind if true, bind the current thread, if false, reserve a cpu which can be bound later.
* @return A handle for an affinity lock.
*/
public static AffinityLock acquireCore(boolean bind) {
return acquireCore(bind, -1, AffinityStrategies.ANY);
}
private static AffinityLock acquireLock(boolean bind, int cpuId, @NotNull AffinityStrategy... strategies) {
return LOCK_INVENTORY.acquireLock(bind, cpuId, strategies);
}
private static AffinityLock acquireCore(boolean bind, int cpuId, @NotNull AffinityStrategy... strategies) {
return LOCK_INVENTORY.acquireCore(bind, cpuId, strategies);
}
/**
* Set the CPU layout for this machine. CPUs which are not mentioned will be ignored.
*
* Changing the layout will have no impact on thread which have already been assigned.
* It only affects subsequent assignments.
*
* @param cpuLayout for this application to use for this machine.
*/
public static void cpuLayout(@NotNull CpuLayout cpuLayout) {
LOCK_INVENTORY.set(cpuLayout);
}
/**
* @return The current CpuLayout for the application.
*/
@NotNull
public static CpuLayout cpuLayout() {
return LOCK_INVENTORY.getCpuLayout();
}
/**
* @return All the current locks as a String.
*/
@NotNull
public static String dumpLocks() {
return LOCK_INVENTORY.dumpLocks();
}
private static Field makeThreadFieldModifiable(String fieldName) {
try {
Field field = Thread.class.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
throw new RuntimeException(Thread.class.getName() + " class doesn't have a " + fieldName + " field! Quite unexpected!");
}
}
private static void changeGroupOfThread(Thread thread, ThreadGroup group) {
try {
GROUP_FIELD.set(thread, group);
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed changing " + Thread.class.getName() + "'s the '" + GROUP_FIELD.getName() + "' field! Reason: " + e.getMessage());
}
}
private static void wrapRunnableOfThread(Thread thread, final AffinityLock lock) {
try {
final Runnable originalRunnable = (Runnable) TARGET_FIELD.get(thread);
TARGET_FIELD.set(
thread,
new Runnable() {
@Override
public void run() {
lock.release();
originalRunnable.run();
}
}
);
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed wrapping " + Thread.class.getName() + "'s '" + TARGET_FIELD.getName() + "' field! Reason: " + e.getMessage());
}
}
@Override
public void bind(boolean wholeCore) {
super.bind(wholeCore);
Thread thread = Thread.currentThread();
changeGroupOfThread(thread, new ThreadTrackingGroup(thread.getThreadGroup(), this));
}
@Override
public void release() {
Thread thread = Thread.currentThread();
changeGroupOfThread(thread, thread.getThreadGroup().getParent());
super.release();
}
@Override
public void started(Thread t) {
wrapRunnableOfThread(t, this);
}
@Override
public void startFailed(Thread t) {
}
@Override
public void terminated(Thread t) {
}
}