Skip to content

Commit 9571555

Browse files
committed
Added support for setting thread affinity using a description, closes OpenHFT#54
1 parent d212a0f commit 9571555

5 files changed

Lines changed: 134 additions & 31 deletions

File tree

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

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,71 @@ public static AffinityLock acquireLock(int cpuId) {
175175
return acquireLock(true, cpuId, AffinityStrategies.ANY);
176176
}
177177

178+
/**
179+
* Allocate from the end.
180+
*
181+
* @param n positive number to allocate from.
182+
* @return the lock acquired
183+
*/
184+
public static AffinityLock acquireLockLastMinus(int n) {
185+
return acquireLock(true, PROCESSORS - n, AffinityStrategies.ANY);
186+
}
187+
188+
/**
189+
* Use a description to allocate a cpu.
190+
* <ul>
191+
* <li>"N" being a positive integer means allocate this CPU,</li>
192+
* <li>"last" or "last-N" means allocate from the end,</li>
193+
* <li>"any" means allow any</li>
194+
* <li>"none" or null means</li>
195+
* <li>"0" is not allowed</li>
196+
* </ul>
197+
*
198+
* @param desc of which cpu to pick
199+
* @return the AffinityLock obtained
200+
*/
201+
public static AffinityLock acquireLock(String desc) {
202+
if (desc == null)
203+
return LOCK_INVENTORY.noLock();
204+
205+
desc = desc.toLowerCase();
206+
int cpuId;
207+
if (desc.startsWith("last")) {
208+
String last = desc.substring(4);
209+
int lastN;
210+
if (last.isEmpty())
211+
lastN = 0;
212+
else
213+
try {
214+
lastN = Integer.parseInt(last);
215+
} catch (NumberFormatException e) {
216+
throw new IllegalArgumentException("Cannot parse '" + desc + "'", e);
217+
}
218+
if (lastN > 0)
219+
throw new IllegalArgumentException("Cannot parse '" + desc + "'");
220+
221+
cpuId = PROCESSORS + lastN - 1;
222+
223+
} else if (desc.equals("none")) {
224+
return LOCK_INVENTORY.noLock();
225+
226+
} else if (desc.equals("any")) {
227+
return acquireLock();
228+
229+
} else {
230+
try {
231+
cpuId = Integer.parseInt(desc);
232+
} catch (NumberFormatException e) {
233+
throw new IllegalArgumentException("Cannot parse '" + desc + "'", e);
234+
}
235+
}
236+
if (cpuId <= 0) {
237+
System.err.println("Cannot allocate 0 or negative cpuIds '" + desc + "'");
238+
return LOCK_INVENTORY.noLock();
239+
}
240+
return acquireLock(cpuId);
241+
}
242+
178243
/**
179244
* Assign a core(and all its cpus) which can be bound to the current thread or another thread.
180245
* <p> This can be used for defining your thread layout centrally and passing the handle via
@@ -256,12 +321,12 @@ public void bind(boolean wholeCore) {
256321
}
257322
}
258323

259-
final boolean canReserve() {
324+
final boolean canReserve(boolean specified) {
260325

261326
if (!LockCheck.isCpuFree(cpuId))
262327
return false;
263328

264-
if (!reservable) return false;
329+
if (!specified && !reservable) return false;
265330
if (assignedThread != null) {
266331
if (assignedThread.isAlive()) {
267332
return false;

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public final synchronized AffinityLock acquireLock(boolean bind, int cpuId, Affi
100100
final boolean specificCpuRequested = !isAnyCpu(cpuId);
101101
if (specificCpuRequested && cpuId != 0) {
102102
final AffinityLock required = logicalCoreLocks[cpuId];
103-
if (required.canReserve() && anyStrategyMatches(cpuId, cpuId, strategies)) {
103+
if (required.canReserve(true) && anyStrategyMatches(cpuId, cpuId, strategies)) {
104104
updateLockForCurrentThread(bind, required, false);
105105
return required;
106106
}
@@ -113,7 +113,7 @@ public final synchronized AffinityLock acquireLock(boolean bind, int cpuId, Affi
113113
// if you have only one core, this library is not appropriate in any case.
114114
for (int i = logicalCoreLocks.length - 1; i > 0; i--) {
115115
AffinityLock al = logicalCoreLocks[i];
116-
if (al.canReserve() && (isAnyCpu(cpuId) || strategy.matches(cpuId, al.cpuId()))) {
116+
if (al.canReserve(false) && (isAnyCpu(cpuId) || strategy.matches(cpuId, al.cpuId()))) {
117117
updateLockForCurrentThread(bind, al, false);
118118
return al;
119119
}
@@ -122,15 +122,15 @@ public final synchronized AffinityLock acquireLock(boolean bind, int cpuId, Affi
122122

123123
LOGGER.warn("No reservable CPU for {}", Thread.currentThread());
124124

125-
return newLock(AffinityLock.ANY_CPU, false, false);
125+
return noLock();
126126
}
127127

128128
public final synchronized AffinityLock acquireCore(boolean bind, int cpuId, AffinityStrategy... strategies) {
129129
for (AffinityStrategy strategy : strategies) {
130130
LOOP:
131131
for (AffinityLock[] als : physicalCoreLocks.descendingMap().values()) {
132132
for (AffinityLock al : als)
133-
if (!al.canReserve() || !strategy.matches(cpuId, al.cpuId()))
133+
if (!al.canReserve(false) || !strategy.matches(cpuId, al.cpuId()))
134134
continue LOOP;
135135

136136
final AffinityLock al = als[0];
@@ -212,4 +212,8 @@ private void releaseAffinityLock(final Thread t, final AffinityLock al, final St
212212

213213
LockCheck.releaseLock(al.cpuId());
214214
}
215+
216+
public AffinityLock noLock() {
217+
return newLock(AffinityLock.ANY_CPU, false, false);
218+
}
215219
}

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

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ public enum PosixJNAAffinity implements IAffinity {
4444
private static final Logger LOGGER = LoggerFactory.getLogger(PosixJNAAffinity.class);
4545
private static final String LIBRARY_NAME = Platform.isWindows() ? "msvcrt" : "c";
4646
private static final int PROCESS_ID;
47-
private static final boolean ISLINUX = "Linux".equals(System.getProperty("os.name"));
48-
private static final boolean IS64BIT = is64Bit0();
49-
private static final int SYS_gettid = is64Bit() ? 186 : 224;
47+
private static final int SYS_gettid = Utilities.is64Bit() ? 186 : 224;
5048
private static final Object[] NO_ARGS = {};
5149

5250
static {
@@ -72,24 +70,6 @@ public enum PosixJNAAffinity implements IAffinity {
7270

7371
private final ThreadLocal<Integer> THREAD_ID = new ThreadLocal<>();
7472

75-
public static boolean is64Bit() {
76-
return IS64BIT;
77-
}
78-
79-
private static boolean is64Bit0() {
80-
String systemProp;
81-
systemProp = System.getProperty("com.ibm.vm.bitmode");
82-
if (systemProp != null) {
83-
return "64".equals(systemProp);
84-
}
85-
systemProp = System.getProperty("sun.arch.data.model");
86-
if (systemProp != null) {
87-
return "64".equals(systemProp);
88-
}
89-
systemProp = System.getProperty("java.vm.version");
90-
return systemProp != null && systemProp.contains("_64");
91-
}
92-
9373
@Override
9474
public BitSet getAffinity() {
9575
final CLibrary lib = CLibrary.INSTANCE;
@@ -198,7 +178,7 @@ public int getProcessId() {
198178

199179
@Override
200180
public int getThreadId() {
201-
if (ISLINUX) {
181+
if (Utilities.ISLINUX) {
202182
Integer tid = THREAD_ID.get();
203183
if (tid == null)
204184
THREAD_ID.set(tid = CLibrary.INSTANCE.syscall(SYS_gettid, NO_ARGS));

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
* Created by andre on 20/06/15.
2626
*/
2727
public final class Utilities {
28+
public static final boolean ISLINUX = "Linux".equals(System.getProperty("os.name"));
29+
static final boolean IS64BIT = is64Bit0();
30+
2831
private Utilities() {
2932
throw new InstantiationError("Must not instantiate this class");
3033
}
@@ -58,4 +61,22 @@ public static String toBinaryString(BitSet set) {
5861

5962
return new String(out.toByteArray(), java.nio.charset.StandardCharsets.UTF_8);
6063
}
64+
65+
public static boolean is64Bit() {
66+
return IS64BIT;
67+
}
68+
69+
private static boolean is64Bit0() {
70+
String systemProp;
71+
systemProp = System.getProperty("com.ibm.vm.bitmode");
72+
if (systemProp != null) {
73+
return "64".equals(systemProp);
74+
}
75+
systemProp = System.getProperty("sun.arch.data.model");
76+
if (systemProp != null) {
77+
return "64".equals(systemProp);
78+
}
79+
systemProp = System.getProperty("java.vm.version");
80+
return systemProp != null && systemProp.contains("_64");
81+
}
6182
}

affinity/src/test/java/net/openhft/affinity/AffinityLockTest.java

Lines changed: 36 additions & 3 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.impl.VanillaCpuLayout;
2122
import net.openhft.affinity.testimpl.TestFileBasedLockChecker;
2223
import org.junit.Test;
@@ -30,6 +31,7 @@
3031
import java.util.ArrayList;
3132
import java.util.List;
3233

34+
import static net.openhft.affinity.AffinityLock.PROCESSORS;
3335
import static org.hamcrest.CoreMatchers.is;
3436
import static org.junit.Assert.*;
3537
import static org.junit.Assume.assumeTrue;
@@ -174,7 +176,7 @@ public void testIssue21() throws IOException {
174176

175177
@Test
176178
public void testIssue19() {
177-
System.out.println("AffinityLock.PROCESSORS=" + AffinityLock.PROCESSORS);
179+
System.out.println("AffinityLock.PROCESSORS=" + PROCESSORS);
178180

179181
AffinityLock al = AffinityLock.acquireLock();
180182
List<AffinityLock> locks = new ArrayList<>();
@@ -231,8 +233,8 @@ public void shouldReturnLockForSpecifiedCpu() {
231233

232234
@Test
233235
public void lockFilesShouldBeRemovedOnRelease() {
234-
if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
235-
return;//doesn't work on Windows
236+
if (!Utilities.ISLINUX) {
237+
return;
236238
}
237239
final AffinityLock lock = AffinityLock.acquireLock();
238240

@@ -246,4 +248,35 @@ public void lockFilesShouldBeRemovedOnRelease() {
246248
private void displayStatus() {
247249
System.out.println(Thread.currentThread() + " on " + Affinity.getCpu() + "\n" + AffinityLock.dumpLocks());
248250
}
251+
252+
@Test
253+
public void testAffinityLockDescriptions() {
254+
if (!Utilities.ISLINUX) {
255+
return;
256+
}
257+
try (AffinityLock lock = AffinityLock.acquireLock("last")) {
258+
assertEquals(PROCESSORS - 1, Affinity.getCpu());
259+
}
260+
try (AffinityLock lock = AffinityLock.acquireLock("last")) {
261+
assertEquals(PROCESSORS - 1, Affinity.getCpu());
262+
}
263+
try (AffinityLock lock = AffinityLock.acquireLock("last-1")) {
264+
assertEquals(PROCESSORS - 2, Affinity.getCpu());
265+
}
266+
try (AffinityLock lock = AffinityLock.acquireLock("1")) {
267+
assertEquals(1, Affinity.getCpu());
268+
}
269+
try (AffinityLock lock = AffinityLock.acquireLock("any")) {
270+
assertTrue(lock.bound);
271+
}
272+
try (AffinityLock lock = AffinityLock.acquireLock("none")) {
273+
assertFalse(lock.bound);
274+
}
275+
try (AffinityLock lock = AffinityLock.acquireLock((String) null)) {
276+
assertFalse(lock.bound);
277+
}
278+
try (AffinityLock lock = AffinityLock.acquireLock("0")) {
279+
assertFalse(lock.bound);
280+
}
281+
}
249282
}

0 commit comments

Comments
 (0)