Skip to content

Commit 9cc670b

Browse files
committed
Use SharedSecrets for ThreadLocalRandomProbe; other tweaks
1 parent 6fe1a3b commit 9cc670b

File tree

5 files changed

+43
-41
lines changed

5 files changed

+43
-41
lines changed

src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java

+12-13
Original file line numberDiff line numberDiff line change
@@ -1273,15 +1273,17 @@ final int queueSize() {
12731273
/**
12741274
* Pushes a task. Called only by owner or if already locked
12751275
*
1276-
* @param task the task. Caller must ensure non-null.
1276+
* @param task the task; no-op if null
12771277
* @param pool the pool to signal if was previously empty, else null
12781278
* @param internal if caller owns this queue
12791279
* @throws RejectedExecutionException if array could not be resized
12801280
*/
12811281
final void push(ForkJoinTask<?> task, ForkJoinPool pool,
12821282
boolean internal) {
12831283
int s = top, b = base, m, cap, room; ForkJoinTask<?>[] a;
1284-
if ((a = array) != null && (cap = a.length) > 0) { // else disabled
1284+
if ((a = array) != null && (cap = a.length) > 0 && // else disabled
1285+
task != null) {
1286+
int pk = task.noUserHelp() + 1; // prev slot offset
12851287
if ((room = (m = cap - 1) - (s - b)) >= 0) {
12861288
top = s + 1;
12871289
long pos = slotOffset(m & s);
@@ -1296,9 +1298,7 @@ final void push(ForkJoinTask<?> task, ForkJoinPool pool,
12961298
unlockPhase();
12971299
if (room < 0)
12981300
throw new RejectedExecutionException("Queue capacity exceeded");
1299-
if ((room == 0 || // pad if no caller-run
1300-
a[m & (s - ((internal || task == null ||
1301-
task.noUserHelp() == 0) ? 1 : 2))] == null) &&
1301+
if ((room == 0 || a[m & (s - pk)] == null) &&
13021302
pool != null)
13031303
pool.signalWork(); // may have appeared empty
13041304
}
@@ -2016,14 +2016,13 @@ final void runWorker(WorkQueue w) {
20162016
}
20172017
else {
20182018
boolean propagate;
2019-
int nb = q.base = b + 1;
2019+
int nb = q.base = b + 1, prevSrc = src;
20202020
w.nsteals = ++nsteals;
2021-
int ts = t.status;
2022-
w.source = j; // volatile
2021+
w.source = src = j; // volatile
20232022
rescan = true;
2023+
int nh = t.noUserHelp();
20242024
if (propagate =
2025-
((src != (src = j) || t.noUserHelp() != 0) &&
2026-
a[nb & m] != null))
2025+
(prevSrc != src || nh != 0) && a[nb & m] != null)
20272026
signalWork();
20282027
w.topLevelExec(t, fifo);
20292028
if ((b = q.base) != nb && !propagate)
@@ -2062,8 +2061,8 @@ private int deactivate(WorkQueue w, int phase) {
20622061
((e & SHUTDOWN) != 0L && ac == 0 && quiescent() > 0) ||
20632062
(qs = queues) == null || (n = qs.length) <= 0)
20642063
return IDLE; // terminating
2065-
int prechecks = 3; // reactivation threshold
2066-
for (int k = Math.max(n << 2, SPIN_WAITS << 1);;) {
2064+
int k = Math.max(n << 2, SPIN_WAITS << 1);
2065+
for (int prechecks = k / n;;) { // reactivation threshold
20672066
WorkQueue q; int cap; ForkJoinTask<?>[] a; long c;
20682067
if (w.phase == activePhase)
20692068
return activePhase;
@@ -2072,7 +2071,7 @@ private int deactivate(WorkQueue w, int phase) {
20722071
if ((q = qs[k & (n - 1)]) == null)
20732072
Thread.onSpinWait();
20742073
else if ((a = q.array) != null && (cap = a.length) > 0 &&
2075-
a[q.base & (cap - 1)] != null && --prechecks < 0 &&
2074+
a[q.base & (cap - 1)] != null && --prechecks <= 0 &&
20762075
(int)(c = ctl) == activePhase &&
20772076
compareAndSetCtl(c, (sp & LMASK) | ((c + RC_UNIT) & UMASK)))
20782077
return w.phase = activePhase; // reactivate

src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@ final boolean casNext(Aux c, Aux v) { // used only in cancellation
273273
static final int ABNORMAL = 1 << 16;
274274
static final int THROWN = 1 << 17;
275275
static final int HAVE_EXCEPTION = DONE | ABNORMAL | THROWN;
276-
static final int NO_USER_HELP = 1 << 24; // no external caller-run helping
276+
static final int NUH_BIT = 24; // no external caller helping
277+
static final int NO_USER_HELP = 1 << NUH_BIT;
277278
static final int MARKER = 1 << 30; // utility marker
278279
static final int SMASK = 0xffff; // short bits for tags
279280
static final int UNCOMPENSATE = 1 << 16; // helpJoin sentinel
@@ -293,8 +294,8 @@ private int getAndBitwiseOrStatus(int v) {
293294
private boolean casStatus(int c, int v) {
294295
return U.compareAndSetInt(this, STATUS, c, v);
295296
}
296-
final int noUserHelp() { // nonvolatile read
297-
return U.getInt(this, STATUS) & NO_USER_HELP;
297+
final int noUserHelp() { // nonvolatile read; return 0 or 1
298+
return (U.getInt(this, STATUS) & NO_USER_HELP) >>> NUH_BIT;
298299
}
299300
final void setNoUserHelp() { // for use in constructors only
300301
U.putInt(this, STATUS, NO_USER_HELP);

src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java

+17-9
Original file line numberDiff line numberDiff line change
@@ -242,16 +242,18 @@ protected int next(int bits) {
242242
* the classes that use them. Briefly, a thread's "probe" value is
243243
* a non-zero hash code that (probably) does not collide with
244244
* other existing threads with respect to any power of two
245-
* collision space. When it does collide, it is pseudo-randomly
246-
* adjusted (using a Marsaglia XorShift). The nextSecondarySeed
247-
* method is used in the same contexts as ThreadLocalRandom, but
248-
* only for transient usages such as random adaptive spin/block
249-
* sequences for which a cheap RNG suffices and for which it could
250-
* in principle disrupt user-visible statistical properties of the
251-
* main ThreadLocalRandom if we were to use it.
245+
* collision space, based on carrier threads in the case of
246+
* VirtualThreads to reduce the expected collision rate. When it
247+
* does collide, it is pseudo-randomly adjusted (using a Marsaglia
248+
* XorShift). The nextSecondarySeed method is used in the same
249+
* contexts as ThreadLocalRandom, but only for transient usages
250+
* such as random adaptive spin/block sequences for which a cheap
251+
* RNG suffices and for which it could in principle disrupt
252+
* user-visible statistical properties of the main
253+
* ThreadLocalRandom if we were to use it.
252254
*
253-
* Note: Because of package-protection issues, versions of some
254-
* these methods also appear in some subpackage classes.
255+
* Note: jdk SharedSecrets are used enable use in jdk classes
256+
* outside this package.
255257
*/
256258

257259
/**
@@ -397,6 +399,12 @@ private static class Access {
397399
public int nextSecondaryThreadLocalRandomSeed() {
398400
return nextSecondarySeed();
399401
}
402+
public int getThreadLocalRandomProbe() {
403+
return getProbe();
404+
}
405+
public int advanceThreadLocalRandomProbe(int r) {
406+
return advanceProbe(r);
407+
}
400408
}
401409
);
402410
}

src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java

+8-16
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import java.util.concurrent.ThreadLocalRandom;
4444
import java.util.function.DoubleBinaryOperator;
4545
import java.util.function.LongBinaryOperator;
46+
import jdk.internal.access.SharedSecrets;
47+
import jdk.internal.access.JavaUtilConcurrentTLRAccess;
4648

4749
/**
4850
* A package-local class holding common representation and mechanics
@@ -188,24 +190,18 @@ final boolean casCellsBusy() {
188190
}
189191

190192
/**
191-
* Returns the probe value for the current thread.
192-
* Duplicated from ThreadLocalRandom because of packaging restrictions.
193+
* Returns the ThreadLocalRandom probe value for the current thread.
193194
*/
194195
static final int getProbe() {
195-
return (int) THREAD_PROBE.get(Thread.currentThread());
196+
return TLR.getThreadLocalRandomProbe();
196197
}
197198

198199
/**
199200
* Pseudo-randomly advances and records the given probe value for the
200201
* given thread.
201-
* Duplicated from ThreadLocalRandom because of packaging restrictions.
202202
*/
203203
static final int advanceProbe(int probe) {
204-
probe ^= probe << 13; // xorshift
205-
probe ^= probe >>> 17;
206-
probe ^= probe << 5;
207-
THREAD_PROBE.set(Thread.currentThread(), probe);
208-
return probe;
204+
return TLR.advanceThreadLocalRandomProbe(probe);
209205
}
210206

211207
/**
@@ -371,21 +367,17 @@ else if (casBase(v = base, apply(fn, v, x)))
371367
}
372368
}
373369

370+
private static final JavaUtilConcurrentTLRAccess TLR =
371+
SharedSecrets.getJavaUtilConcurrentTLRAccess();
372+
374373
// VarHandle mechanics
375374
private static final VarHandle BASE;
376375
private static final VarHandle CELLSBUSY;
377-
private static final VarHandle THREAD_PROBE;
378376
static {
379377
MethodHandles.Lookup l1 = MethodHandles.lookup();
380378

381379
BASE = MhUtil.findVarHandle(l1, "base", long.class);
382380
CELLSBUSY = MhUtil.findVarHandle(l1, "cellsBusy", int.class);
383-
try {
384-
MethodHandles.Lookup l2 = MethodHandles.privateLookupIn(Thread.class, l1);
385-
THREAD_PROBE = MhUtil.findVarHandle(l2, "threadLocalRandomProbe", int.class);
386-
} catch (ReflectiveOperationException e) {
387-
throw new ExceptionInInitializerError(e);
388-
}
389381
}
390382

391383
}

src/java.base/share/classes/jdk/internal/access/JavaUtilConcurrentTLRAccess.java

+2
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@
2727

2828
public interface JavaUtilConcurrentTLRAccess {
2929
int nextSecondaryThreadLocalRandomSeed();
30+
int getThreadLocalRandomProbe();
31+
int advanceThreadLocalRandomProbe(int r);
3032
}

0 commit comments

Comments
 (0)