Skip to content

Commit f23e3a0

Browse files
authored
Merge pull request #179 from OpenHFT/added-tests
Added tests for edge cases
2 parents 996d83c + 422fcfc commit f23e3a0

30 files changed

+766
-74
lines changed

affinity/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@
110110
<profile>
111111
<id>make-c</id>
112112
<activation>
113+
<os>
114+
<family>linux</family>
115+
<arch>!arm</arch>
116+
</os>
113117
<property>
114118
<name>!dontMake</name>
115119
</property>

affinity/src/main/java/net/openhft/affinity/AffinityLock.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public class AffinityLock implements Closeable {
6969
* Logical ID of the CPU to which this lock belongs to.
7070
*/
7171
private final int cpuId;
72+
private final int cpuId2;
7273
/**
7374
* CPU to which this lock belongs to is of general use.
7475
*/
@@ -88,9 +89,10 @@ public class AffinityLock implements Closeable {
8889
Throwable boundHere;
8990
private boolean resetAffinity = true;
9091

91-
AffinityLock(int cpuId, boolean base, boolean reservable, LockInventory lockInventory) {
92+
AffinityLock(int cpuId, int cpuId2, boolean base, boolean reservable, LockInventory lockInventory) {
9293
this.lockInventory = lockInventory;
9394
this.cpuId = cpuId;
95+
this.cpuId2 = cpuId2;
9496
this.base = base;
9597
this.reservable = reservable;
9698
}
@@ -133,7 +135,7 @@ private static BitSet getReservedAffinity0() {
133135
int end = reservedAffinity.length();
134136
for (int i = 0; i < longs.length; i++) {
135137
int begin = Math.max(0, end - 16);
136-
longs[i] = Long.parseLong(reservedAffinity.substring(begin, end), 16);
138+
longs[i] = Long.parseUnsignedLong(reservedAffinity.substring(begin, end), 16);
137139
end = begin;
138140
}
139141
return BitSet.valueOf(longs);
@@ -183,17 +185,20 @@ public static AffinityLock acquireLock(boolean bind) {
183185
* for defining your thread layout centrally and passing the handle via dependency injection.
184186
*
185187
* @param cpuId the CPU id to bind to
186-
* @return A handle for an affinity lock.
188+
* @return A handle for an affinity lock, or no lock if no available CPU in the array
187189
*/
188190
public static AffinityLock acquireLock(int cpuId) {
189-
checkCpuId(cpuId);
191+
if (isInvalidCpuId(cpuId))
192+
return LOCK_INVENTORY.noLock();
190193
return acquireLock(true, cpuId, AffinityStrategies.ANY);
191194
}
192195

193-
private static void checkCpuId(int cpuId) {
196+
private static boolean isInvalidCpuId(int cpuId) {
194197
if (cpuId < 0 || cpuId >= PROCESSORS) {
195198
LOGGER.warn("cpuId must be between 0 and {}: {}", PROCESSORS - 1, cpuId);
199+
return true;
196200
}
201+
return false;
197202
}
198203

199204
/**
@@ -207,7 +212,7 @@ private static void checkCpuId(int cpuId) {
207212
*/
208213
public static AffinityLock acquireLock(int[] cpus) {
209214
for (int cpu : cpus) {
210-
checkCpuId(cpu);
215+
if (isInvalidCpuId(cpu)) continue;
211216
AffinityLock lock = tryAcquireLock(true, cpu);
212217
if (lock != null) {
213218
LOGGER.info("Acquired lock on CPU {}", cpu);
@@ -471,6 +476,10 @@ public int cpuId() {
471476
return cpuId;
472477
}
473478

479+
public int cpuId2() {
480+
return cpuId2;
481+
}
482+
474483
/**
475484
* @return Was a cpu found to bind this lock to.
476485
*/

affinity/src/main/java/net/openhft/affinity/BootClassPath.java

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
2323

24+
import java.io.File;
2425
import java.io.IOException;
26+
import java.net.URI;
2527
import java.nio.file.*;
2628
import java.nio.file.attribute.BasicFileAttributes;
2729
import java.util.Collections;
@@ -39,21 +41,56 @@ enum BootClassPath {
3941
private static Set<String> getResourcesOnBootClasspath() {
4042
final Logger logger = LoggerFactory.getLogger(BootClassPath.class);
4143
final Set<String> resources = new HashSet<>();
44+
4245
final String bootClassPath = System.getProperty("sun.boot.class.path", "");
43-
logger.trace("Boot class-path is: {}", bootClassPath);
46+
if (!bootClassPath.isEmpty()) {
47+
logger.trace("Boot class-path is: {}", bootClassPath);
4448

45-
final String pathSeparator = System.getProperty("path.separator");
46-
logger.trace("Path separator is: '{}'", pathSeparator);
49+
final String pathSeparator = File.pathSeparator;
50+
logger.trace("Path separator is: '{}'", pathSeparator);
4751

48-
final String[] pathElements = bootClassPath.split(pathSeparator);
52+
final String[] pathElements = bootClassPath.split(pathSeparator);
4953

50-
for (final String pathElement : pathElements) {
51-
resources.addAll(findResources(Paths.get(pathElement), logger));
54+
for (final String pathElement : pathElements) {
55+
resources.addAll(findResources(Paths.get(pathElement), logger));
56+
}
57+
} else {
58+
resources.addAll(findResourcesInJrt(logger));
5259
}
5360

5461
return resources;
5562
}
5663

64+
private static Set<String> findResourcesInJrt(final Logger logger) {
65+
final Set<String> jrtResources = new HashSet<>();
66+
try {
67+
FileSystem fs;
68+
try {
69+
fs = FileSystems.getFileSystem(URI.create("jrt:/"));
70+
} catch (FileSystemNotFoundException | ProviderNotFoundException e) {
71+
fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
72+
}
73+
final Path modules = fs.getPath("/modules");
74+
Files.walkFileTree(modules, new SimpleFileVisitor<Path>() {
75+
@Override
76+
public @NotNull FileVisitResult visitFile(final @NotNull Path file,
77+
final @NotNull BasicFileAttributes attrs) throws IOException {
78+
if (file.getFileName().toString().endsWith(".class")) {
79+
Path relative = modules.relativize(file);
80+
if (relative.getNameCount() > 1) {
81+
Path classPath = relative.subpath(1, relative.getNameCount());
82+
jrtResources.add(classPath.toString());
83+
}
84+
}
85+
return FileVisitResult.CONTINUE;
86+
}
87+
});
88+
} catch (IOException e) {
89+
logger.warn("Error walking jrt filesystem", e);
90+
}
91+
return jrtResources;
92+
}
93+
5794
private static Set<String> findResources(final Path path, final Logger logger) {
5895
if (!Files.exists(path)) {
5996
return Collections.emptySet();

affinity/src/main/java/net/openhft/affinity/CpuLayout.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,10 @@ public interface CpuLayout {
4949
* @return which thread on a core this cpu is on.
5050
*/
5151
int threadId(int cpuId);
52+
53+
/**
54+
* @param cpuId the logical processor number
55+
* @return the hyperthreaded pair number or 0 if not hyperthreaded.
56+
*/
57+
int pair(int cpuId);
5258
}

affinity/src/main/java/net/openhft/affinity/LockCheck.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package net.openhft.affinity;
1919

20+
import net.openhft.affinity.impl.Utilities;
2021
import net.openhft.affinity.lockchecker.FileLockBasedLockChecker;
2122
import net.openhft.affinity.lockchecker.LockChecker;
2223
import org.slf4j.Logger;
@@ -39,9 +40,7 @@ public enum LockCheck {
3940
private static final LockChecker lockChecker = FileLockBasedLockChecker.getInstance();
4041

4142
public static long getPID() {
42-
String processName =
43-
java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
44-
return Long.parseLong(processName.split("@")[0]);
43+
return Utilities.currentProcessId();
4544
}
4645

4746
static boolean canOSSupportOperation() {
@@ -55,8 +54,8 @@ public static boolean isCpuFree(int cpu) {
5554
return isLockFree(cpu);
5655
}
5756

58-
static boolean replacePid(int cpu, long processID) throws IOException {
59-
return storePid(processID, cpu);
57+
static boolean replacePid(int cpu, int cpu2, long processID) throws IOException {
58+
return storePid(processID, cpu, cpu2);
6059
}
6160

6261
public static boolean isProcessRunning(long pid) {
@@ -70,15 +69,18 @@ public static boolean isProcessRunning(long pid) {
7069
* stores the pid in a file, named by the core, the pid is written to the file with the date
7170
* below
7271
*/
73-
private synchronized static boolean storePid(long processID, int cpu) throws IOException {
74-
return lockChecker.obtainLock(cpu, Long.toString(processID));
72+
private synchronized static boolean storePid(long processID, int cpu, int cpu2) throws IOException {
73+
return lockChecker.obtainLock(cpu, cpu2, Long.toString(processID));
7574
}
7675

7776
private synchronized static boolean isLockFree(int id) {
7877
return lockChecker.isLockFree(id);
7978
}
8079

8180
public static int getProcessForCpu(int core) throws IOException {
81+
if (!canOSSupportOperation())
82+
return EMPTY_PID;
83+
8284
String meta = lockChecker.getMetaInfo(core);
8385

8486
if (meta != null && !meta.isEmpty()) {
@@ -91,10 +93,10 @@ public static int getProcessForCpu(int core) throws IOException {
9193
return EMPTY_PID;
9294
}
9395

94-
static boolean updateCpu(int cpu) throws IOException {
96+
static boolean updateCpu(int cpu, int cpu2) throws IOException {
9597
if (!canOSSupportOperation())
9698
return true;
97-
return replacePid(cpu, getPID());
99+
return replacePid(cpu, cpu2, getPID());
98100
}
99101

100102
public static void releaseLock(int cpu) {

affinity/src/main/java/net/openhft/affinity/LockInventory.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ private static boolean isAnyCpu(final int cpuId) {
8282
*/
8383
private static boolean updateLockForCurrentThread(final boolean bind, final AffinityLock al, final boolean wholeCore) throws ClosedByInterruptException {
8484
try {
85-
if (LockCheck.updateCpu(al.cpuId())) {
85+
if (LockCheck.updateCpu(al.cpuId(), wholeCore ? al.cpuId2() : 0)) {
8686
al.assignCurrentThread(bind, wholeCore);
8787
return true;
8888
}
@@ -108,7 +108,9 @@ public final synchronized void set(CpuLayout cpuLayout) {
108108
final boolean base = AffinityLock.BASE_AFFINITY.get(i);
109109
final boolean reservable = AffinityLock.RESERVED_AFFINITY.get(i);
110110
LOGGER.trace("cpu {} base={} reservable= {}", i, base, reservable);
111-
AffinityLock lock = logicalCoreLocks[i] = newLock(i, base, reservable);
111+
assert logicalCoreLocks != null;
112+
@SuppressWarnings("resource")
113+
AffinityLock lock = logicalCoreLocks[i] = newLock(i, cpuLayout.pair(i), base, reservable);
112114

113115
int layoutId = lock.cpuId();
114116
int physicalCore = toPhysicalCore(layoutId);
@@ -277,8 +279,8 @@ public final synchronized String dumpLocks() {
277279
return dumpLocks(logicalCoreLocks);
278280
}
279281

280-
protected AffinityLock newLock(int cpuId, boolean base, boolean reservable) {
281-
return new AffinityLock(cpuId, base, reservable, this);
282+
protected AffinityLock newLock(int cpuId, int cpuId2, boolean base, boolean reservable) {
283+
return new AffinityLock(cpuId, cpuId2, base, reservable, this);
282284
}
283285

284286
private void reset(CpuLayout cpuLayout) {
@@ -301,6 +303,6 @@ private void releaseAffinityLock(final Thread t, final AffinityLock al, final St
301303
}
302304

303305
public AffinityLock noLock() {
304-
return newLock(AffinityLock.ANY_CPU, false, false);
306+
return newLock(AffinityLock.ANY_CPU, 0, false, false);
305307
}
306308
}

affinity/src/main/java/net/openhft/affinity/impl/NoCpuLayout.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,9 @@ public int coreId(int cpuId) {
6464
public int threadId(int cpuId) {
6565
return 0;
6666
}
67+
68+
@Override
69+
public int pair(int cpuId) {
70+
return 0;
71+
}
6772
}

affinity/src/main/java/net/openhft/affinity/impl/NullAffinity.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
2323

24-
import java.lang.management.ManagementFactory;
2524
import java.util.BitSet;
2625

2726
/**
@@ -48,8 +47,7 @@ public int getCpu() {
4847

4948
@Override
5049
public int getProcessId() {
51-
final String name = ManagementFactory.getRuntimeMXBean().getName();
52-
return Integer.parseInt(name.split("@")[0]);
50+
return Utilities.currentProcessId();
5351
}
5452

5553
@Override

affinity/src/main/java/net/openhft/affinity/impl/OSXJNAAffinity.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.slf4j.Logger;
2525
import org.slf4j.LoggerFactory;
2626

27-
import java.lang.management.ManagementFactory;
2827
import java.util.BitSet;
2928

3029
/**
@@ -55,8 +54,7 @@ public int getCpu() {
5554

5655
@Override
5756
public int getProcessId() {
58-
final String name = ManagementFactory.getRuntimeMXBean().getName();
59-
return Integer.parseInt(name.split("@")[0]);
57+
return Utilities.currentProcessId();
6058
}
6159

6260
@Override

affinity/src/main/java/net/openhft/affinity/impl/SolarisJNAAffinity.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.slf4j.Logger;
2525
import org.slf4j.LoggerFactory;
2626

27-
import java.lang.management.ManagementFactory;
2827
import java.util.BitSet;
2928

3029
/**
@@ -55,8 +54,7 @@ public int getCpu() {
5554

5655
@Override
5756
public int getProcessId() {
58-
final String name = ManagementFactory.getRuntimeMXBean().getName();
59-
return Integer.parseInt(name.split("@")[0]);
57+
return Utilities.currentProcessId();
6058
}
6159

6260
@Override

affinity/src/main/java/net/openhft/affinity/impl/Utilities.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,30 @@ private static boolean is64Bit0() {
7979
systemProp = System.getProperty("java.vm.version");
8080
return systemProp != null && systemProp.contains("_64");
8181
}
82+
83+
/**
84+
* Returns the current process id. Uses {@code ProcessHandle} when running
85+
* on Java&nbsp;9 or later and falls back to parsing
86+
* {@code RuntimeMXBean#getName()} on earlier versions.
87+
*
88+
* @return the process id or {@code -1} if it cannot be determined
89+
*/
90+
public static int currentProcessId() {
91+
try {
92+
// Java 9+ provides ProcessHandle which has a pid() method.
93+
Class<?> phClass = Class.forName("java.lang.ProcessHandle");
94+
Object current = phClass.getMethod("current").invoke(null);
95+
long pid = (Long) phClass.getMethod("pid").invoke(current);
96+
return (int) pid;
97+
} catch (Throwable ignored) {
98+
// ignore and fallback to the pre-Java 9 approach
99+
}
100+
101+
try {
102+
String name = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
103+
return Integer.parseInt(name.split("@")[0]);
104+
} catch (Throwable e) {
105+
return -1;
106+
}
107+
}
82108
}

affinity/src/main/java/net/openhft/affinity/impl/VanillaCpuLayout.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,19 @@ public int threadId(int cpuId) {
177177
return cpuDetails.get(cpuId).threadId;
178178
}
179179

180+
@Override
181+
public int pair(int cpuId) {
182+
for (int i = 0; i < cpuDetails.size(); i++) {
183+
CpuInfo info = cpuDetails.get(i);
184+
if (info.socketId == cpuDetails.get(cpuId).socketId &&
185+
info.coreId == cpuDetails.get(cpuId).coreId &&
186+
info.threadId != cpuDetails.get(cpuId).threadId) {
187+
return i;
188+
}
189+
}
190+
return 0;
191+
}
192+
180193
@NotNull
181194
@Override
182195
public String toString() {

affinity/src/main/java/net/openhft/affinity/impl/WindowsJNAAffinity.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ public void setAffinity(final BitSet affinity) {
8989
throw new IllegalStateException("SetThreadAffinityMask((" + pid + ") , &(" + affinity + ") ) errorNo=" + e.getErrorCode(), e);
9090
}
9191
BitSet affinity2 = getAffinity0();
92-
if (!affinity2.equals(affinity)) {
93-
LoggerFactory.getLogger(WindowsJNAAffinity.class).warn("Tried to set affinity to " + affinity + " but was " + affinity2 + " you may have insufficient access rights");
92+
assert affinity2 != null;
93+
if (!affinity2.intersects(affinity)) {
94+
LoggerFactory.getLogger(WindowsJNAAffinity.class).warn("Tried to set affinity to {} but was {} you may have insufficient access rights", affinity, affinity2);
9495
}
9596
currentAffinity.set((BitSet) affinity.clone());
9697
}

0 commit comments

Comments
 (0)