#include "software_chronicle_enterprise_internals_impl_NativeAffinity.h"
/*
@@ -20,13 +21,13 @@ JNIEXPORT jlong JNICALL Java_software_chronicle_enterprise_internals_impl_Native
policy.affinity_tag = 0;
mach_msg_type_number_t count = THREAD_AFFINITY_POLICY_COUNT;
boolean_t get_default = FALSE;
-
+
if ((thread_policy_get(threadport,
THREAD_AFFINITY_POLICY, (thread_policy_t)&policy,
&count, &get_default)) != KERN_SUCCESS) {
return ~0LL;
}
-
+
return (jlong) policy.affinity_tag;
}
@@ -37,19 +38,21 @@ JNIEXPORT jlong JNICALL Java_software_chronicle_enterprise_internals_impl_Native
*/
JNIEXPORT void JNICALL Java_software_chronicle_enterprise_internals_impl_NativeAffinity_setAffinity0
(JNIEnv *env, jclass c, jlong affinity) {
-
+
thread_port_t threadport = pthread_mach_thread_np(pthread_self());
struct thread_enterprise_internals_policy policy;
policy.affinity_tag = affinity;
-
+
int rc = thread_policy_set(threadport,
THREAD_AFFINITY_POLICY, (thread_policy_t)&policy,
- THREAD_AFFINITY_POLICY_COUNT);
+ THREAD_AFFINITY_POLICY_COUNT);
if (rc != KERN_SUCCESS) {
jclass ex = (*env)->FindClass(env, "java/lang/RuntimeException");
- char msg[100];
- sprintf(msg, "Bad return value from thread_policy_set: %d", rc);
- (*env)->ThrowNew(env, ex, msg);
+ if (ex != NULL) {
+ char msg[100];
+ snprintf(msg, sizeof(msg), "Bad return value from thread_policy_set: %d", rc);
+ (*env)->ThrowNew(env, ex, msg);
+ }
}
}
diff --git a/affinity/src/main/java/net/openhft/affinity/Affinity.java b/affinity/src/main/java/net/openhft/affinity/Affinity.java
index 8dadd8a15..1dda9a97a 100644
--- a/affinity/src/main/java/net/openhft/affinity/Affinity.java
+++ b/affinity/src/main/java/net/openhft/affinity/Affinity.java
@@ -3,7 +3,6 @@
*/
package net.openhft.affinity;
-import com.sun.jna.Native;
import net.openhft.affinity.impl.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
@@ -25,43 +24,46 @@ public enum Affinity {
static final Logger LOGGER = LoggerFactory.getLogger(Affinity.class);
@NotNull
private static final IAffinity AFFINITY_IMPL;
- private static Boolean JNAAvailable;
+ private static volatile 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;
+ IAffinity impl;
+ try {
+ String osName = System.getProperty("os.name");
+ if (osName.contains("Win") && isWindowsJNAAffinityUsable()) {
+ LOGGER.trace("Using Windows JNA-based affinity control implementation");
+ impl = WindowsJNAAffinity.INSTANCE;
+
+ } else if (osName.contains("x")) {
+ if (osName.startsWith("Linux") && isLinuxJNAAffinityUsable()) {
+ LOGGER.trace("Using Linux JNA-based affinity control implementation");
+ impl = LinuxJNAAffinity.INSTANCE;
+
+ } else if (isPosixJNAAffinityUsable()) {
+ LOGGER.trace("Using Posix JNA-based affinity control implementation");
+ impl = PosixJNAAffinity.INSTANCE;
+
+ } else {
+ LOGGER.info("Unsupported POSIX OS: {} with an 'x'. Using dummy affinity control implementation", osName);
+ impl = NullAffinity.INSTANCE;
+ }
+ } else if (osName.contains("Mac") && isMacJNAAffinityUsable()) {
+ LOGGER.trace("Using MAC OSX JNA-based thread id implementation");
+ impl = OSXJNAAffinity.INSTANCE;
+
+ } else if (osName.contains("SunOS") && isSolarisJNAAffinityUsable()) {
+ LOGGER.trace("Using Solaris JNA-based thread id implementation");
+ impl = SolarisJNAAffinity.INSTANCE;
} else {
- LOGGER.info("Using dummy affinity control implementation");
- AFFINITY_IMPL = NullAffinity.INSTANCE;
+ LOGGER.info("Unsupported OS: {}. Using dummy affinity control implementation", osName);
+ 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;
+ } catch (Throwable t) {
+ LOGGER.warn("Falling back to dummy affinity control implementation because native init failed", t);
+ impl = NullAffinity.INSTANCE;
}
+ AFFINITY_IMPL = impl;
}
public static IAffinity getAffinityImpl() {
@@ -174,21 +176,40 @@ public static void setThreadId() {
}
public static boolean isJNAAvailable() {
- if (JNAAvailable == null) {
- int majorVersion = Integer.parseInt(Native.VERSION.split("\\.")[0]);
- if (majorVersion < 5) {
- LOGGER.warn("Affinity library requires JNA version >= 5");
- JNAAvailable = false;
- } else {
- try {
- Class.forName("com.sun.jna.Platform");
- JNAAvailable = true;
- } catch (ClassNotFoundException ignored) {
- JNAAvailable = false;
+ Boolean available = jnaAvailable;
+ if (available == null) {
+ synchronized (Affinity.class) {
+ available = jnaAvailable;
+ if (available == null) {
+ boolean result;
+ try {
+ Class> nativeClass = Class.forName("com.sun.jna.Native");
+ Field versionField = nativeClass.getField("VERSION");
+ versionField.setAccessible(true);
+ Object versionObj = versionField.get(null);
+ String version = versionObj == null ? "0" : versionObj.toString();
+ int majorVersion = Integer.parseInt(version.split("\\.")[0]);
+ if (majorVersion < 5) {
+ LOGGER.warn("Affinity library requires JNA version >= 5");
+ result = false;
+ } else {
+ try {
+ Class.forName("com.sun.jna.Platform");
+ result = true;
+ } catch (ClassNotFoundException ignored) {
+ result = false;
+ }
+ }
+ } catch (Throwable t) { // NoClassDefFoundError, UnsatisfiedLinkError, IllegalAccessException etc.
+ LOGGER.warn("JNA not available, falling back to NullAffinity", t);
+ result = false;
+ }
+ available = result;
+ jnaAvailable = available;
}
}
}
- return JNAAvailable;
+ return available;
}
public static AffinityLock acquireLock() {
diff --git a/affinity/src/main/java/net/openhft/affinity/BootClassPath.java b/affinity/src/main/java/net/openhft/affinity/BootClassPath.java
index 85efa3279..bbebdceb2 100644
--- a/affinity/src/main/java/net/openhft/affinity/BootClassPath.java
+++ b/affinity/src/main/java/net/openhft/affinity/BootClassPath.java
@@ -19,6 +19,13 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+/**
+ * Utility that inspects the JVM boot classpath (or JRT modules) to determine whether classes are
+ * provided by the platform.
+ *
+ * Used by affinity checks to avoid attempting to instrument or load classes already present in the
+ * bootstrap runtime.
+ */
enum BootClassPath {
INSTANCE;
diff --git a/affinity/src/main/java/net/openhft/affinity/LockInventory.java b/affinity/src/main/java/net/openhft/affinity/LockInventory.java
index b54615219..49db61b50 100644
--- a/affinity/src/main/java/net/openhft/affinity/LockInventory.java
+++ b/affinity/src/main/java/net/openhft/affinity/LockInventory.java
@@ -16,6 +16,13 @@
import static net.openhft.affinity.Affinity.getAffinityImpl;
+/**
+ * Maintains the mapping of logical and physical CPU cores to {@link AffinityLock} instances and
+ * coordinates allocation of locks to threads based on strategies.
+ *
+ * Handles initialisation from {@link CpuLayout}, reservation attempts, and core-level acquisition
+ * while tracking hyper-threading relationships.
+ */
class LockInventory {
private static final Logger LOGGER = LoggerFactory.getLogger(LockInventory.class);
diff --git a/affinity/src/main/java/net/openhft/affinity/impl/LinuxHelper.java b/affinity/src/main/java/net/openhft/affinity/impl/LinuxHelper.java
index a52404bba..5ead7422e 100644
--- a/affinity/src/main/java/net/openhft/affinity/impl/LinuxHelper.java
+++ b/affinity/src/main/java/net/openhft/affinity/impl/LinuxHelper.java
@@ -12,6 +12,13 @@
import java.util.Collections;
import java.util.List;
+/**
+ * JNI/JNA helpers for interacting with Linux CPU affinity and process metadata.
+ *
+ * Wraps libc calls such as {@code sched_getaffinity}, {@code sched_setaffinity}, {@code getpid}
+ * and {@code sched_getcpu} and exposes supporting structures for use elsewhere in the affinity
+ * module.
+ */
public class LinuxHelper {
private static final String LIBRARY_NAME = "c";
private static final VersionHelper UNKNOWN = new VersionHelper(0, 0, 0);
@@ -33,6 +40,11 @@ public class LinuxHelper {
version = ver;
}
+ /**
+ * Read the current process affinity mask.
+ *
+ * @return populated CPU set describing allowed processors
+ */
public static
@NotNull
cpu_set_t sched_getaffinity() {
@@ -52,10 +64,16 @@ cpu_set_t sched_getaffinity() {
return cpuset;
}
+ /**
+ * Set affinity mask for the current process.
+ */
public static void sched_setaffinity(final BitSet affinity) {
sched_setaffinity(0, affinity);
}
+ /**
+ * Set affinity mask for a specific pid.
+ */
public static void sched_setaffinity(final int pid, final BitSet affinity) {
final CLibrary lib = CLibrary.INSTANCE;
final cpu_set_t cpuset = new cpu_set_t();
@@ -80,6 +98,9 @@ public static void sched_setaffinity(final int pid, final BitSet affinity) {
}
}
+ /**
+ * Discover the current CPU via libc or syscall fallback.
+ */
public static int sched_getcpu() {
final CLibrary lib = CLibrary.INSTANCE;
try {
@@ -117,6 +138,9 @@ public static int sched_getcpu() {
}
}
+ /**
+ * Return the current process id.
+ */
public static int getpid() {
final CLibrary lib = CLibrary.INSTANCE;
try {
@@ -130,6 +154,9 @@ public static int getpid() {
}
}
+ /**
+ * Invoke an arbitrary syscall by number.
+ */
public static int syscall(int number, Object... args) {
final CLibrary lib = CLibrary.INSTANCE;
try {
@@ -234,6 +261,9 @@ public String getRelease() {
return new String(release, 0, length(release));
}
+ /**
+ * Parse the release string to extract a dotted numeric version (e.g. 5.10.0).
+ */
public String getRealeaseVersion() {
final String release = getRelease();
final int releaseLen = release.length();
@@ -268,6 +298,9 @@ public String toString() {
}
}
+ /**
+ * JNA view of {@code cpu_set_t} used by sched affinity calls.
+ */
public static class cpu_set_t extends Structure {
static final int __CPU_SETSIZE = 1024;
static final int __NCPUBITS = 8 * NativeLong.SIZE;
diff --git a/affinity/src/main/java/net/openhft/affinity/impl/LinuxJNAAffinity.java b/affinity/src/main/java/net/openhft/affinity/impl/LinuxJNAAffinity.java
index d16fc3beb..5022810cb 100644
--- a/affinity/src/main/java/net/openhft/affinity/impl/LinuxJNAAffinity.java
+++ b/affinity/src/main/java/net/openhft/affinity/impl/LinuxJNAAffinity.java
@@ -11,6 +11,12 @@
import java.util.BitSet;
+/**
+ * Linux {@link IAffinity} implementation that delegates to libc via JNA.
+ *
+ * Resolves process/thread ids, reads and sets CPU affinity masks, and caches thread ids per
+ * thread. Guards against missing native libraries by exposing a {@link #LOADED} flag.
+ */
public enum LinuxJNAAffinity implements IAffinity {
INSTANCE;
public static final boolean LOADED;
@@ -27,6 +33,7 @@ public enum LinuxJNAAffinity implements IAffinity {
try {
pid = LinuxHelper.getpid();
} catch (NoClassDefFoundError | Exception ignored) {
+ // best effort: leave pid as -1 if native helper is unavailable
}
PROCESS_ID = pid;
}
@@ -45,6 +52,9 @@ public enum LinuxJNAAffinity implements IAffinity {
private final ThreadLocal THREAD_ID = new ThreadLocal<>();
+ /**
+ * Read the current affinity mask for this process.
+ */
@Override
public BitSet getAffinity() {
final LinuxHelper.cpu_set_t cpuset = LinuxHelper.sched_getaffinity();
@@ -58,21 +68,33 @@ public BitSet getAffinity() {
return ret;
}
+ /**
+ * Apply the given affinity mask to this process.
+ */
@Override
public void setAffinity(final BitSet affinity) {
LinuxHelper.sched_setaffinity(affinity);
}
+ /**
+ * Return the current CPU id.
+ */
@Override
public int getCpu() {
return LinuxHelper.sched_getcpu();
}
+ /**
+ * Cached process id obtained via {@link LinuxHelper#getpid()} where available.
+ */
@Override
public int getProcessId() {
return PROCESS_ID;
}
+ /**
+ * Thread id resolved via {@code SYS_gettid}, cached per thread to avoid repeated syscalls.
+ */
@Override
public int getThreadId() {
Integer tid = THREAD_ID.get();
diff --git a/affinity/src/main/java/net/openhft/affinity/impl/PosixJNAAffinity.java b/affinity/src/main/java/net/openhft/affinity/impl/PosixJNAAffinity.java
index 39aed93ab..b69fa6d98 100644
--- a/affinity/src/main/java/net/openhft/affinity/impl/PosixJNAAffinity.java
+++ b/affinity/src/main/java/net/openhft/affinity/impl/PosixJNAAffinity.java
@@ -28,7 +28,6 @@ public enum PosixJNAAffinity implements IAffinity {
INSTANCE;
public static final boolean LOADED;
private static final Logger LOGGER = LoggerFactory.getLogger(PosixJNAAffinity.class);
- private static final String LIBRARY_NAME = Platform.isWindows() ? "msvcrt" : "c";
private static final int PROCESS_ID;
private static final int SYS_gettid = Utilities.is64Bit() ? 186 : 224;
private static final Object[] NO_ARGS = {};
@@ -95,7 +94,6 @@ public BitSet getAffinity() {
@Override
public void setAffinity(final BitSet affinity) {
- int procs = Runtime.getRuntime().availableProcessors();
if (affinity.isEmpty()) {
throw new IllegalArgumentException("Cannot set zero affinity");
}
@@ -177,7 +175,7 @@ public int getThreadId() {
* @author BegemoT
*/
interface CLibrary extends Library {
- CLibrary INSTANCE = Native.load(LIBRARY_NAME, CLibrary.class);
+ CLibrary INSTANCE = Native.load(Platform.isWindows() ? "msvcrt" : "c", CLibrary.class);
int sched_setaffinity(final int pid,
final int cpusetsize,
diff --git a/affinity/src/main/java/net/openhft/affinity/impl/package-info.java b/affinity/src/main/java/net/openhft/affinity/impl/package-info.java
new file mode 100644
index 000000000..3a61a52ea
--- /dev/null
+++ b/affinity/src/main/java/net/openhft/affinity/impl/package-info.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2013-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
+ */
+/**
+ * Platform-specific implementations backing Chronicle thread affinity.
+ *
+ * Contains OS- and JNA-based helpers for querying CPU layouts and binding
+ * threads, used internally by the public affinity API.
+ */
+package net.openhft.affinity.impl;
diff --git a/affinity/src/main/java/net/openhft/affinity/lockchecker/FileLockBasedLockChecker.java b/affinity/src/main/java/net/openhft/affinity/lockchecker/FileLockBasedLockChecker.java
index eb2394ddd..cb14bf287 100644
--- a/affinity/src/main/java/net/openhft/affinity/lockchecker/FileLockBasedLockChecker.java
+++ b/affinity/src/main/java/net/openhft/affinity/lockchecker/FileLockBasedLockChecker.java
@@ -18,6 +18,7 @@
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
+import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
@@ -27,6 +28,13 @@
import static java.nio.file.StandardOpenOption.*;
import static net.openhft.affinity.impl.VanillaCpuLayout.MAX_CPUS_SUPPORTED;
+/**
+ * Lock checker that coordinates CPU reservations using file locks per core.
+ *
+ * Each CPU id is represented by a lock file; exclusive locks indicate ownership while shared
+ * locks signal availability checks. Includes simple retry handling to cope with concurrent file
+ * deletion.
+ */
public class FileLockBasedLockChecker implements LockChecker {
private static final int MAX_LOCK_RETRIES = 5;
@@ -38,7 +46,7 @@ public class FileLockBasedLockChecker implements LockChecker {
private final LockReference[] locks = new LockReference[MAX_CPUS_SUPPORTED];
protected FileLockBasedLockChecker() {
- //nothing
+ // nothing
}
public static LockChecker getInstance() {
@@ -158,7 +166,8 @@ private LockReference tryAcquireLockOnFile(int id, String metaInfo) throws IOExc
}
private void writeMetaInfoToFile(FileChannel fc, String metaInfo) throws IOException {
- byte[] content = String.format("%s%n%s", metaInfo, dfTL.get().format(new Date())).getBytes();
+ byte[] content = String.format("%s%n%s", metaInfo, dfTL.get().format(new Date()))
+ .getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.wrap(content);
while (buffer.hasRemaining()) {
//noinspection ResultOfMethodCallIgnored
@@ -211,7 +220,7 @@ public String getMetaInfo(int id) throws IOException {
private String readMetaInfoFromLockFileChannel(File lockFile, FileChannel lockFileChannel) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(64);
int len = lockFileChannel.read(buffer, 0);
- String content = len < 1 ? "" : new String(buffer.array(), 0, len);
+ String content = len < 1 ? "" : new String(buffer.array(), 0, len, StandardCharsets.UTF_8);
if (content.isEmpty()) {
LOGGER.warn("Empty lock file {}", lockFile.getAbsolutePath());
return null;
@@ -228,8 +237,9 @@ protected File toFile(int id) {
private File tmpDir() {
final File tempDir = new File(System.getProperty("java.io.tmpdir"));
- if (!tempDir.exists())
- tempDir.mkdirs();
+ if (!tempDir.exists() && !tempDir.mkdirs()) {
+ LOGGER.warn("Unable to create temp directory {}", tempDir);
+ }
return tempDir;
}
diff --git a/affinity/src/main/java/net/openhft/affinity/lockchecker/package-info.java b/affinity/src/main/java/net/openhft/affinity/lockchecker/package-info.java
new file mode 100644
index 000000000..7b8585e95
--- /dev/null
+++ b/affinity/src/main/java/net/openhft/affinity/lockchecker/package-info.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2013-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
+ */
+/**
+ * Utilities for detecting external locks on affinity resources.
+ *
+ *
These classes guard against multiple processes competing for the same CPU
+ * bindings by coordinating via file locks and related checks.
+ */
+package net.openhft.affinity.lockchecker;
diff --git a/affinity/src/main/java/net/openhft/affinity/package-info.java b/affinity/src/main/java/net/openhft/affinity/package-info.java
new file mode 100644
index 000000000..61348780a
--- /dev/null
+++ b/affinity/src/main/java/net/openhft/affinity/package-info.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2013-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
+ */
+/**
+ * Thread affinity utilities for pinning and coordinating CPU usage.
+ *
+ *
The public API here exposes strategies for binding threads to cores,
+ * acquiring {@link net.openhft.affinity.AffinityLock}s, and inspecting CPU
+ * layouts to deliver predictable latency on supported platforms.
+ */
+package net.openhft.affinity;
diff --git a/affinity/src/main/java/net/openhft/ticker/impl/package-info.java b/affinity/src/main/java/net/openhft/ticker/impl/package-info.java
new file mode 100644
index 000000000..6723f9b8e
--- /dev/null
+++ b/affinity/src/main/java/net/openhft/ticker/impl/package-info.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2013-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
+ */
+/**
+ * Concrete ticker implementations.
+ *
+ *
Provides system- and JNI-based clocks that supply {@code Ticker} instances
+ * with varying precision and overhead trade-offs.
+ */
+package net.openhft.ticker.impl;
diff --git a/affinity/src/main/java/net/openhft/ticker/package-info.java b/affinity/src/main/java/net/openhft/ticker/package-info.java
new file mode 100644
index 000000000..e2a45f671
--- /dev/null
+++ b/affinity/src/main/java/net/openhft/ticker/package-info.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2013-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
+ */
+/**
+ * Abstractions for low-jitter time sources.
+ *
+ *
Defines a simple ticker interface used by affinity and related utilities to
+ * obtain high-resolution timestamps without tying callers to a specific clock
+ * implementation.
+ */
+package net.openhft.ticker;
diff --git a/affinity/src/main/java/software/chronicle/enterprise/internals/impl/NativeAffinity.java b/affinity/src/main/java/software/chronicle/enterprise/internals/impl/NativeAffinity.java
index aa501a8f0..69835deec 100644
--- a/affinity/src/main/java/software/chronicle/enterprise/internals/impl/NativeAffinity.java
+++ b/affinity/src/main/java/software/chronicle/enterprise/internals/impl/NativeAffinity.java
@@ -7,26 +7,42 @@
import java.util.BitSet;
+/**
+ * Enterprise {@link IAffinity} backed by the native CEInternals library.
+ *
+ * Provides affinity operations and lightweight cycle timing via JNI; guarded by the {@link #LOADED}
+ * flag so callers can detect when the native library is unavailable.
+ */
public enum NativeAffinity implements IAffinity {
INSTANCE;
+ /**
+ * Indicates whether the native library loaded successfully.
+ */
public static final boolean LOADED;
static {
LOADED = loadAffinityNativeLibrary();
}
- private native static byte[] getAffinity0();
+ private static native byte[] getAffinity0();
+
+ private static native void setAffinity0(byte[] affinity);
- private native static void setAffinity0(byte[] affinity);
+ private static native int getCpu0();
- private native static int getCpu0();
+ private static native int getProcessId0();
- private native static int getProcessId0();
+ private static native int getThreadId0();
- private native static int getThreadId0();
+ private static native long rdtsc0();
- private native static long rdtsc0();
+ /**
+ * Read the current cycle counter if the native library supports it.
+ */
+ static long rdtsc() {
+ return rdtsc0();
+ }
@SuppressWarnings("restricted")
private static boolean loadAffinityNativeLibrary() {
@@ -38,6 +54,9 @@ private static boolean loadAffinityNativeLibrary() {
}
}
+ /**
+ * Read the affinity mask for the current thread.
+ */
@Override
public BitSet getAffinity() {
final byte[] buff = getAffinity0();
@@ -47,21 +66,33 @@ public BitSet getAffinity() {
return BitSet.valueOf(buff);
}
+ /**
+ * Apply the given affinity mask to the current thread.
+ */
@Override
public void setAffinity(BitSet affinity) {
setAffinity0(affinity.toByteArray());
}
+ /**
+ * Return the CPU id the current thread is running on.
+ */
@Override
public int getCpu() {
return getCpu0();
}
+ /**
+ * Return the current process id.
+ */
@Override
public int getProcessId() {
return getProcessId0();
}
+ /**
+ * Return the current thread id.
+ */
@Override
public int getThreadId() {
return getThreadId0();
diff --git a/affinity/src/test/java/net/openhft/affinity/AffinityThreadFactoryMain.java b/affinity/src/test/java/net/openhft/affinity/AffinityThreadFactoryMain.java
index 3dbd15396..9f2c19c08 100644
--- a/affinity/src/test/java/net/openhft/affinity/AffinityThreadFactoryMain.java
+++ b/affinity/src/test/java/net/openhft/affinity/AffinityThreadFactoryMain.java
@@ -3,7 +3,6 @@
*/
package net.openhft.affinity;
-import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -22,11 +21,15 @@ private AffinityThreadFactoryMain() {
}
public static void main(String... args) throws InterruptedException {
- for (int i = 0; i < 12; i++)
- ES.submit((Callable) () -> {
- Thread.sleep(100);
- return null;
+ for (int i = 0; i < 12; i++) {
+ ES.execute(() -> {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
});
+ }
Thread.sleep(200);
System.out.println("\nThe assignment of CPUs is\n" + AffinityLock.dumpLocks());
ES.shutdown();
diff --git a/affinity/src/test/java/net/openhft/affinity/AffinityThreadFactoryTest.java b/affinity/src/test/java/net/openhft/affinity/AffinityThreadFactoryTest.java
index 4c95b8bd4..533849c6c 100644
--- a/affinity/src/test/java/net/openhft/affinity/AffinityThreadFactoryTest.java
+++ b/affinity/src/test/java/net/openhft/affinity/AffinityThreadFactoryTest.java
@@ -32,7 +32,7 @@ public void threadsReceiveDistinctCpus() throws InterruptedException {
CountDownLatch finished = new CountDownLatch(nThreads);
for (int i = 0; i < nThreads; i++) {
- es.submit(() -> {
+ es.execute(() -> {
cpus.add(Affinity.getCpu());
ready.countDown();
try {
diff --git a/affinity/src/test/java/net/openhft/affinity/BaseAffinityTest.java b/affinity/src/test/java/net/openhft/affinity/BaseAffinityTest.java
index f147ce33b..3e9770ddb 100644
--- a/affinity/src/test/java/net/openhft/affinity/BaseAffinityTest.java
+++ b/affinity/src/test/java/net/openhft/affinity/BaseAffinityTest.java
@@ -15,7 +15,7 @@
public class BaseAffinityTest {
@Rule
- public TemporaryFolder folder = new TemporaryFolder();
+ public final TemporaryFolder folder = new TemporaryFolder();
private String originalTmpDir;
@Before
diff --git a/affinity/src/test/java/net/openhft/affinity/LockCheckTest.java b/affinity/src/test/java/net/openhft/affinity/LockCheckTest.java
index 3f19a4a2f..c67da9b2f 100644
--- a/affinity/src/test/java/net/openhft/affinity/LockCheckTest.java
+++ b/affinity/src/test/java/net/openhft/affinity/LockCheckTest.java
@@ -10,9 +10,11 @@
import org.junit.Test;
import java.io.File;
-import java.io.FileWriter;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
import static net.openhft.affinity.LockCheck.IS_LINUX;
@@ -69,7 +71,8 @@ public void shouldNotBlowUpIfPidFileIsCorrupt() throws Exception {
LockCheck.updateCpu(cpu, 0);
final File file = lockChecker.doToFile(cpu);
- try (final FileWriter writer = new FileWriter(file, false)) {
+ try (final OutputStreamWriter writer =
+ new OutputStreamWriter(new FileOutputStream(file, false), StandardCharsets.UTF_8)) {
writer.append("not a number\nnot a date");
}
diff --git a/affinity/src/test/java/net/openhft/affinity/MultiProcessAffinityTest.java b/affinity/src/test/java/net/openhft/affinity/MultiProcessAffinityTest.java
index 63dd9105b..24524f0aa 100644
--- a/affinity/src/test/java/net/openhft/affinity/MultiProcessAffinityTest.java
+++ b/affinity/src/test/java/net/openhft/affinity/MultiProcessAffinityTest.java
@@ -238,8 +238,7 @@ public static void main(String[] args) throws InterruptedException, IOException
try {
File lockFile = toFile(cpu);
try (final FileChannel fc = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
- final long maxValue = Long.MAX_VALUE; // a PID that never exists
- ByteBuffer buffer = ByteBuffer.wrap((maxValue + "\n").getBytes(StandardCharsets.UTF_8));
+ ByteBuffer buffer = ByteBuffer.wrap((Long.MAX_VALUE + "\n").getBytes(StandardCharsets.UTF_8));
while (buffer.hasRemaining()) {
//noinspection ResultOfMethodCallIgnored
fc.write(buffer);
diff --git a/affinity/src/test/java/net/openhft/affinity/impl/LinuxJNAAffinityTest.java b/affinity/src/test/java/net/openhft/affinity/impl/LinuxJNAAffinityTest.java
index cf0db12bd..278f68f28 100644
--- a/affinity/src/test/java/net/openhft/affinity/impl/LinuxJNAAffinityTest.java
+++ b/affinity/src/test/java/net/openhft/affinity/impl/LinuxJNAAffinityTest.java
@@ -22,7 +22,7 @@ public static void checkJniLibraryPresent() {
}
@Test
- public void LinuxJNA() {
+ public void linuxJna() {
int nbits = Runtime.getRuntime().availableProcessors();
BitSet affinity0 = LinuxJNAAffinity.INSTANCE.getAffinity();
System.out.println(affinity0);
diff --git a/affinity/src/test/java/net/openhft/ticker/impl/JNIClockTest.java b/affinity/src/test/java/net/openhft/ticker/impl/JNIClockTest.java
index 359c06e02..d14e4958b 100644
--- a/affinity/src/test/java/net/openhft/ticker/impl/JNIClockTest.java
+++ b/affinity/src/test/java/net/openhft/ticker/impl/JNIClockTest.java
@@ -9,6 +9,8 @@
import org.junit.Test;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
/*
* Created by Peter Lawrey on 13/07/15.
@@ -16,11 +18,11 @@
public class JNIClockTest extends BaseAffinityTest {
@Test
- @Ignore("TODO Fix")
public void testNanoTime() throws InterruptedException {
+ assumeTrue("JNIClock native library must be loaded", JNIClock.LOADED);
+
for (int i = 0; i < 20000; i++)
System.nanoTime();
- Affinity.setAffinity(2);
JNIClock instance = JNIClock.INSTANCE;
for (int i = 0; i < 50; i++) {
@@ -30,9 +32,17 @@ public void testNanoTime() throws InterruptedException {
long time0 = System.nanoTime();
long time1 = instance.ticks();
if (i > 1) {
- assertEquals(10_100_000, time0 - start0, 100_000);
- assertEquals(10_100_000, instance.toNanos(time1 - start1), 100_000);
- assertEquals(instance.toNanos(time1 - start1) / 1e3, instance.toMicros(time1 - start1), 0.6);
+ long deltaSys = time0 - start0;
+ long deltaClock = instance.toNanos(time1 - start1);
+ assertTrue("System.nanoTime delta should be positive", deltaSys > 0);
+ assertTrue("JNIClock delta should be positive", deltaClock > 0);
+
+ double ratio = (double) deltaClock / (double) deltaSys;
+ assertTrue("JNIClock and System.nanoTime deltas should agree within 2x, was " + ratio,
+ ratio > 0.5 && ratio < 2.0);
+
+ assertEquals("toMicros should be consistent with toNanos",
+ instance.toNanos(time1 - start1) / 1e3, instance.toMicros(time1 - start1), 0.6);
}
}
}
diff --git a/affinity/src/test/java/software/chronicle/enterprise/internals/NativeAffinityTest.java b/affinity/src/test/java/software/chronicle/enterprise/internals/NativeAffinityTest.java
index fa2bc7fba..7fc3543d0 100644
--- a/affinity/src/test/java/software/chronicle/enterprise/internals/NativeAffinityTest.java
+++ b/affinity/src/test/java/software/chronicle/enterprise/internals/NativeAffinityTest.java
@@ -5,7 +5,6 @@
import net.openhft.affinity.BaseAffinityTest;
import net.openhft.affinity.IAffinity;
-import net.openhft.affinity.impl.LinuxJNAAffinity;
import net.openhft.affinity.impl.Utilities;
import org.junit.*;
import software.chronicle.enterprise.internals.impl.NativeAffinity;
@@ -51,49 +50,7 @@ public void setAffinityCompletesGracefully() {
BitSet affinity = new BitSet(1);
affinity.set(0, true);
getImpl().setAffinity(affinity);
- }
-
- @Test
- @Ignore("TODO AFFINITY-25")
- public void getAffinityReturnsValuePreviouslySet() {
- String osName = System.getProperty("os.name");
- if (!osName.startsWith("Linux")) {
- System.out.println("Skipping Linux tests");
- return;
- }
- final IAffinity impl = NativeAffinity.INSTANCE;
- for (int core = 0; core < CORES; core++) {
- final BitSet mask = new BitSet();
- mask.set(core, true);
- getAffinityReturnsValuePreviouslySet(impl, mask);
- }
- }
-
- @Test
- @Ignore("TODO AFFINITY-25")
- public void JNAwithJNI() {
- String osName = System.getProperty("os.name");
- if (!osName.startsWith("Linux")) {
- System.out.println("Skipping Linux tests");
- return;
- }
- int nbits = Runtime.getRuntime().availableProcessors();
- BitSet affinity = new BitSet(nbits);
- affinity.set(1);
- NativeAffinity.INSTANCE.setAffinity(affinity);
- BitSet affinity2 = LinuxJNAAffinity.INSTANCE.getAffinity();
- assertEquals(1, NativeAffinity.INSTANCE.getCpu());
- assertEquals(affinity, affinity2);
-
- affinity.clear();
- affinity.set(2);
- LinuxJNAAffinity.INSTANCE.setAffinity(affinity);
- BitSet affinity3 = NativeAffinity.INSTANCE.getAffinity();
- assertEquals(2, LinuxJNAAffinity.INSTANCE.getCpu());
- assertEquals(affinity, affinity3);
-
- affinity.set(0, nbits);
- LinuxJNAAffinity.INSTANCE.setAffinity(affinity);
+ getAffinityReturnsValuePreviouslySet(getImpl(), affinity);
}
@Test