undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OperationsPerInvocation;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
public class Atomic {
public AtomicInteger aInteger;
public AtomicLong aLong;
public AtomicBoolean aBool;
public Object testObject1;
public Object testObject2;
public AtomicReference<Object> aReference;
/**
* The test variables are allocated every iteration so you can assume they are initialized to get similar behaviour
* across iterations
*/
@Setup(Level.Iteration)
public void setupIteration() {
testObject1 = new Object();
testObject2 = new Object();
aInteger = new AtomicInteger(0);
aBool = new AtomicBoolean(false);
aReference = new AtomicReference<>(testObject1);
aLong = new AtomicLong(0);
}
/** Always swap in value. This test should be compiled into a CAS */
@Benchmark
@OperationsPerInvocation(2)
public void testAtomicIntegerAlways(Blackhole bh) {
bh.consume(aInteger.compareAndSet(0, 2));
bh.consume(aInteger.compareAndSet(2, 0));
}
/** Never write a value just return the old one. This test should be compiled into a CAS */
@Benchmark
public void testAtomicIntegerNever(Blackhole bh) {
bh.consume(aInteger.compareAndSet(1, 3));
}
/** Flips an atomic boolean on and off */
@Benchmark
@OperationsPerInvocation(2)
public void testAtomicBooleanFlip(Blackhole bh) {
bh.consume(aBool.getAndSet(true));
bh.consume(aBool.getAndSet(false));
}
/** Writes same value over and over */
@Benchmark
public void testAtomicBooleanSame(Blackhole bh) {
bh.consume(aBool.getAndSet(true));
}
/** Increment and get over multiple threads */
@Benchmark
public void testAtomicIntegerGetAndIncrement(Blackhole bh) {
bh.consume(aInteger.getAndIncrement());
}
/** Increment and get over multiple threads */
@Benchmark
public void testAtomicLongGetAndIncrement(Blackhole bh) {
bh.consume(aLong.getAndIncrement());
}
/** Swap a few references */
@Benchmark
@OperationsPerInvocation(2)
public void testAtomicReference(Blackhole bh) {
bh.consume(aReference.compareAndSet(testObject1, testObject2));
bh.consume(aReference.compareAndSet(testObject2, testObject1));
}
}

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;
/**
* Benchmarks assesses the performance of new Atomic* API.
*
* Implementation notes:
* - atomic instances are padded to eliminate false sharing
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
public class AtomicIntegerUpdateAndGet {
private PaddedAtomicInteger count;
private int value = 42;
private IntUnaryOperator captureOp;
private IntUnaryOperator noCaptureOp;
@Setup
public void setup() {
count = new PaddedAtomicInteger();
noCaptureOp = new IntUnaryOperator() {
public int applyAsInt(int v) {
return v + 42;
}
};
captureOp = new IntUnaryOperator() {
public int applyAsInt(int v) {
return v + value;
}
};
}
@Benchmark
public int testAddAndGet() {
return count.addAndGet(42);
}
@Benchmark
public int testInnerNoCapture() {
return count.updateAndGet(new IntUnaryOperator() {
public int applyAsInt(int v) {
return v + 42;
}
});
}
@Benchmark
public int testInnerCapture() {
return count.updateAndGet(new IntUnaryOperator() {
public int applyAsInt(int v) {
return v + value;
}
});
}
@Benchmark
public int testInnerCaptureCached() {
return count.updateAndGet(captureOp);
}
@Benchmark
public int testInnerNoCaptureCached() {
return count.updateAndGet(noCaptureOp);
}
@Benchmark
public int testLambdaNoCapture() {
return count.updateAndGet(x -> x + 42);
}
@Benchmark
public int testLambdaCapture() {
return count.updateAndGet(x -> x + value);
}
private static class PaddedAtomicInteger extends AtomicInteger {
private volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06, pad07;
private volatile long pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
}
}

View file

@ -0,0 +1,166 @@
/*
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
@BenchmarkMode(Mode.AverageTime)
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(1)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
public class CopyOnWriteArrayListBenchmark {
private static byte[] getSerializedBytes(CopyOnWriteArrayList<?> list) throws IOException {
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ObjectOutputStream objectOut = new ObjectOutputStream(bytesOut);
objectOut.writeObject(list);
objectOut.close();
return bytesOut.toByteArray();
}
private Collection<Object> emptyCollection = new ArrayList<>();
private Object[] emptyArray = new Object[0];
private Collection<Object> oneItemCollection = Arrays.asList("");
private Object[] oneItemArray = new Object[] { "" };
private CopyOnWriteArrayList<?> emptyInstance = new CopyOnWriteArrayList<>();
private CopyOnWriteArrayList<?> oneItemInstance = new CopyOnWriteArrayList<>(oneItemArray);
private byte[] emptyInstanceBytes;
private byte[] oneInstanceBytes;
public CopyOnWriteArrayListBenchmark() {
try {
emptyInstanceBytes = getSerializedBytes(emptyInstance);
oneInstanceBytes = getSerializedBytes(oneItemInstance);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Benchmark
public void clear() {
// have to create a new instance on each execution
((CopyOnWriteArrayList<?>) oneItemInstance.clone()).clear();
}
@Benchmark
public void clearEmpty() {
emptyInstance.clear();
}
@Benchmark
public CopyOnWriteArrayList<?> createInstanceArray() {
return new CopyOnWriteArrayList<>(oneItemArray);
}
@Benchmark
public CopyOnWriteArrayList<?> createInstanceArrayEmpty() {
return new CopyOnWriteArrayList<>(emptyArray);
}
@Benchmark
public CopyOnWriteArrayList<?> createInstanceCollection() {
return new CopyOnWriteArrayList<>(oneItemCollection);
}
@Benchmark
public CopyOnWriteArrayList<?> createInstanceCollectionEmpty() {
return new CopyOnWriteArrayList<>(emptyCollection);
}
@Benchmark
public CopyOnWriteArrayList<?> createInstanceDefault() {
return new CopyOnWriteArrayList<Object>();
}
@Benchmark
public CopyOnWriteArrayList<?> readInstance() throws IOException, ClassNotFoundException {
try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(oneInstanceBytes))) {
return (CopyOnWriteArrayList<?>) objIn.readObject();
}
}
@Benchmark
public CopyOnWriteArrayList<?> readInstanceEmpty() throws IOException, ClassNotFoundException {
try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(emptyInstanceBytes))) {
return (CopyOnWriteArrayList<?>) objIn.readObject();
}
}
@Benchmark
public CopyOnWriteArrayList<?> removeObjectLastRemaining() {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("");
list.remove("");
return list;
}
@Benchmark
public CopyOnWriteArrayList<?> removeIndexLastRemaining() {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("");
list.remove(0);
return list;
}
@Benchmark
public CopyOnWriteArrayList<?> removeObject() {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("");
list.add("a");
list.remove("");
return list;
}
@Benchmark
public CopyOnWriteArrayList<?> remove() {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("");
list.add("a");
list.remove(0);
return list;
}
}

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import org.openjdk.jmh.annotations.*;
/**
* Benchmark to compare delayed task scheduling with ScheduledThreadPoolExcutor and ForkJoinPool.
*/
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@Fork(value = 3)
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
public class DelayedTasks {
private Supplier<ScheduledExecutorService> stpeSupplier;
private Supplier<ScheduledExecutorService> fjpSupplier;
@Setup
public void setup() {
stpeSupplier = () -> {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
((ScheduledThreadPoolExecutor) executor).setRemoveOnCancelPolicy(true);
return executor;
};
int nprocs = Runtime.getRuntime().availableProcessors();
fjpSupplier = () -> new ForkJoinPool(nprocs);
}
@Param({"100", "1000", "10000"})
int delayedTasks;
// delayed tasks cancelled by main thread
private void mainThreadCancels(Supplier<ScheduledExecutorService> supplier) {
try (ScheduledExecutorService ses = supplier.get()) {
var futures = new ScheduledFuture[delayedTasks];
for (int i = 0; i < delayedTasks; i++) {
futures[i] = ses.schedule(() -> { }, 30L, TimeUnit.MINUTES);
}
for (ScheduledFuture<?> f : futures) {
f.cancel(false);
}
}
}
// delayed tasks cancelled by virtual threads
private void virtualThreadCancels(Supplier<ScheduledExecutorService> supplier) throws Exception {
try (ScheduledExecutorService ses = supplier.get()) {
var futures = new ScheduledFuture[delayedTasks];
var threads = new Thread[delayedTasks];
for (int i = 0; i < delayedTasks; i++) {
ScheduledFuture<?> future = ses.schedule(() -> { }, 30L, TimeUnit.MINUTES);
futures[i] = future;
threads[i] = Thread.ofVirtual().start(() -> future.cancel(false));
}
for (Thread t : threads) {
t.join();
}
}
}
// delayed task executes
private void delayedTaskExecutes(Supplier<ScheduledExecutorService> supplier) throws Exception {
try (ScheduledExecutorService ses = supplier.get()) {
var futures = new ScheduledFuture[delayedTasks];
for (int i = 0; i < delayedTasks; i++) {
futures[i] = ses.schedule(() -> { }, 10L, TimeUnit.MILLISECONDS);
}
for (ScheduledFuture<?> f : futures) {
f.get();
}
}
}
@Benchmark
public void spteMainThreadCancels() {
mainThreadCancels(stpeSupplier);
}
@Benchmark
public void spteVirtualThreadCancels() throws Exception {
virtualThreadCancels(stpeSupplier);
}
@Benchmark
public void spteDelayedTaskExecutes() throws Exception {
delayedTaskExecutes(stpeSupplier);
}
@Benchmark
public void fjpMainThreadCancels() {
mainThreadCancels(fjpSupplier);
}
@Benchmark
public void fjpVirtualThreadCancels() throws Exception {
virtualThreadCancels(fjpSupplier);
}
@Benchmark
public void fjpDelayedTaskExecutes() throws Exception {
delayedTaskExecutes(fjpSupplier);
}
}

View file

@ -0,0 +1,187 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
/**
* Benchmark assesses ForkJoinPool forking infrastructure.
*
* @author Aleksey Shipilev (aleksey.shipilev@oracle.com)
*/
@OutputTimeUnit(TimeUnit.MINUTES)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 5, time = 2)
@Fork(3)
public class ForkJoinPoolForking {
/**
* Implementation notes:
*
* This test harnesses forking infrastructure within FJP.
* As such, no slack is given for allocating any humble number of tasks: the goal is to fork a lot.
* The approximate number of tasks is (SIZE / THRESHOLD).
*
* Raw baseline gives the idea for compute bound for this benchmark.
* FJP could be faster than baseline, because the baseline is single-threaded.
*/
@Param("10000000")
private int size;
/** Encapsulate all the state depended on only by actual test. This avoids running baselines for every parameter value. */
@State(Scope.Benchmark)
public static class TestState {
@Param("0")
private int workers;
@Param({"1", "2", "3", "4", "5", "6", "7", "8"})
private int threshold;
private ForkJoinPool fjpSync;
private ForkJoinPool fjpAsync;
@Setup
public void setup() {
if (workers == 0) {
workers = Runtime.getRuntime().availableProcessors();
}
fjpSync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, false);
fjpAsync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
}
@TearDown
public void teardown() {
fjpSync.shutdownNow();
fjpAsync.shutdownNow();
}
}
private Problem problem;
@Setup
public void setup() {
problem = new Problem(size);
}
@Benchmark
public Long testExplicit_Sync(TestState state) throws ExecutionException, InterruptedException {
return state.fjpSync.invoke(new ExplicitTask(problem, 0, problem.size(), state.threshold));
}
@Benchmark
public Long testExplicit_Async(TestState state) throws ExecutionException, InterruptedException {
return state.fjpAsync.invoke(new ExplicitTask(problem, 0, problem.size(), state.threshold));
}
@Benchmark
public Long testStandard_Sync(TestState state) throws ExecutionException, InterruptedException {
return state.fjpSync.invoke(new StandardTask(problem, 0, problem.size(), state.threshold));
}
@Benchmark
public Long testStandard_Async(TestState state) throws ExecutionException, InterruptedException {
return state.fjpAsync.invoke(new StandardTask(problem, 0, problem.size(), state.threshold));
}
private static class ExplicitTask extends RecursiveTask<Long> {
private final Problem problem;
private final int l;
private final int r;
private final int thresh;
public ExplicitTask(Problem p, int l, int r, int thresh) {
this.problem = p;
this.l = l;
this.r = r;
this.thresh = thresh;
}
@Override
protected Long compute() {
if (r - l <= thresh) {
return problem.solve(l, r);
}
int mid = (l + r) >>> 1;
ForkJoinTask<Long> t1 = new ExplicitTask(problem, l, mid, thresh);
ForkJoinTask<Long> t2 = new ExplicitTask(problem, mid, r, thresh);
t1.fork();
t2.fork();
long res = 0;
res += t2.join();
res += t1.join();
return res;
}
}
private static class StandardTask extends RecursiveTask<Long> {
private final Problem problem;
private final int l;
private final int r;
private final int thresh;
public StandardTask(Problem p, int l, int r, int thresh) {
this.problem = p;
this.l = l;
this.r = r;
this.thresh = thresh;
}
@Override
protected Long compute() {
if (r - l <= thresh) {
return problem.solve(l, r);
}
int mid = (l + r) >>> 1;
ForkJoinTask<Long> t1 = new StandardTask(problem, l, mid, thresh);
ForkJoinTask<Long> t2 = new StandardTask(problem, mid, r, thresh);
ForkJoinTask.invokeAll(t1, t2);
long res = 0;
res += t1.join();
res += t2.join();
return res;
}
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Benchmark assesses general ForkJoinPool performance with simple tasks
*
* @author Aleksey Shipilev (aleksey.shipilev@oracle.com)
*/
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 5, time = 2)
@Fork(3)
public class ForkJoinPoolRawCallable {
/**
* Implementation notes:
*
* This test submits empty callables.
* Callables are submitted in batches, to prevent convoying by driver threads.
* One driver thread can saturate up to BATCH_SIZE threads.
*
* One baseline includes raw throughput, without submissions to executors.
* This is not considered as fair comparison, but left around as basic compute baseline.
* Executors could not possibly be faster than that.
*
* Another baseline includes ThreadPoolExecutor.
* Note that this baseline is inherently non-scalable with ABQ backing TPE.
* The size of ABQ is chosen to accommodate tons of threads, which can also suffer due to cache effects.
*
* Tasks are reading public volatile field to break opportunistic optimizations in loops.
* Tasks are pre-allocated to negate instantiation costs.
*/
@Param("0")
private int workers;
@Param("1000")
private int batchSize;
private ThreadPoolExecutor tpe;
private ForkJoinPool fjpSync;
private ForkJoinPool fjpAsync;
private List<SampleTask> tasks;
public volatile int arg = 42;
@Setup
public void setup() {
SampleTask task = new SampleTask();
tasks = new ArrayList<>();
for (int c = 0; c < batchSize; c++) {
tasks.add(task);
}
if (workers == 0) {
workers = Runtime.getRuntime().availableProcessors();
}
tpe = new ThreadPoolExecutor(workers, workers, 1, TimeUnit.HOURS, new ArrayBlockingQueue<>(batchSize * batchSize));
fjpSync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, false);
fjpAsync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
}
@TearDown
public void teardown() {
tpe.shutdownNow();
fjpSync.shutdownNow();
fjpAsync.shutdownNow();
}
@Benchmark
public int baseline_raw() throws Exception {
int s = 0;
for (SampleTask t : tasks) {
s += t.call();
}
return s;
}
@Benchmark
public int baseline_TPE() throws Exception {
return doWork(tpe);
}
@Benchmark
public int testSync() throws ExecutionException, InterruptedException {
return doWork(fjpSync);
}
@Benchmark
public int testAsync() throws ExecutionException, InterruptedException {
return doWork(fjpAsync);
}
public int doWork(ExecutorService service) throws ExecutionException, InterruptedException {
List<Future<Integer>> futures = new ArrayList<>(tasks.size());
for (SampleTask task : tasks) {
futures.add(service.submit(task));
}
int s = 0;
for (Future<Integer> future : futures) {
s += future.get();
}
return s;
}
public class SampleTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return arg;
}
}
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
/**
* Benchmark assesses ForkJoinPool performance with dependence on threshold.
*/
@OutputTimeUnit(TimeUnit.MINUTES)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 5, time = 2)
@Fork(3)
public class ForkJoinPoolThresholdAutoQueued {
/**
* Implementation notes:
*
* This test solves the problem with threshold = 1, and adaptive heuristics. The optimal level is static,
* and lies somewhere in 1..2 interval. Note the test degrades significantly when heuristic starts to fail,
* and the throughput is buried under FJP overheads.
*
* Baseline includes solving problem sequentially. Hence, each test provides the speedup for parallel execution
* versus sequential version.
*/
@Param("10000000")
private int size;
/** Encapsulate all the state depended on only by actual test. This avoids running baselines for every parameter value. */
@State(Scope.Benchmark)
public static class TestState {
@Param("0")
private int workers;
@Param({"1", "2", "3", "4", "5", "6", "7", "8"})
private int threshold;
private ForkJoinPool fjp;
@Setup
public void setup() {
if (workers == 0) {
workers = Runtime.getRuntime().availableProcessors();
}
fjp = new ForkJoinPool(workers);
}
@TearDown
public void teardown() {
fjp.shutdownNow();
}
}
private Problem problem;
@Setup
public void setup() {
problem = new Problem(size);
}
@Benchmark
public long baselineRaw() {
return problem.solve();
}
@Benchmark
public Long test(TestState state) throws ExecutionException, InterruptedException {
return state.fjp.invoke(new AutoQueuedTask(state.threshold, problem, 0, problem.size()));
}
private static class AutoQueuedTask extends RecursiveTask<Long> {
private final int thr;
private final Problem problem;
private final int l;
private final int r;
public AutoQueuedTask(int thr, Problem p, int l, int r) {
this.thr = thr;
this.problem = p;
this.l = l;
this.r = r;
}
@Override
protected Long compute() {
if (r - l <= 1 || getQueuedTaskCount() >= thr) {
return problem.solve(l, r);
}
int mid = (l + r) >>> 1;
ForkJoinTask<Long> t1 = new AutoQueuedTask(thr, problem, l, mid);
ForkJoinTask<Long> t2 = new AutoQueuedTask(thr, problem, mid, r);
t2.fork();
long res = 0;
res += t1.invoke();
res += t2.join();
return res;
}
}
}

View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
/**
* Benchmark assesses ForkJoinPool performance with dependence on threshold.
*/
@OutputTimeUnit(TimeUnit.MINUTES)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 5, time = 2)
@Fork(3)
public class ForkJoinPoolThresholdAutoSurplus {
/**
* Implementation notes:
*
* This test solves the problem with threshold = 1, and adaptive heuristics. The optimal level is static,
* and lies somewhere in 1..2 interval. Note the test degrades significantly when heuristic starts to fail,
* and the throughput is buried under FJP overheads.
*
* Baseline includes solving problem sequentially. Hence, each test provides the speedup for parallel execution
* versus sequential version.
*/
@Param("10000000")
private int size;
/** Encapsulate all the state depended on only by actual test. This avoids running baselines for every parameter value. */
@State(Scope.Benchmark)
public static class TestState {
@Param("0")
private int workers;
@Param({"1", "2", "3", "4", "5", "6", "7", "8"})
private int threshold;
private ForkJoinPool fjp;
@Setup
public void setup() {
if (workers == 0) {
workers = Runtime.getRuntime().availableProcessors();
}
fjp = new ForkJoinPool(workers);
}
@TearDown
public void teardown() {
fjp.shutdownNow();
}
}
private Problem problem;
@Setup
public void setup() {
problem = new Problem(size);
}
@Benchmark
public long baselineRaw() {
return problem.solve();
}
@Benchmark
public Long test(TestState state) throws ExecutionException, InterruptedException {
return state.fjp.invoke(new AutoQueuedTask(state.threshold, problem, 0, problem.size()));
}
private static class AutoQueuedTask extends RecursiveTask<Long> {
private final int thr;
private final Problem problem;
private final int l;
private final int r;
public AutoQueuedTask(int thr, Problem p, int l, int r) {
this.thr = thr;
this.problem = p;
this.l = l;
this.r = r;
}
@Override
protected Long compute() {
if (r - l <= 1 || getSurplusQueuedTaskCount() >= thr) {
return problem.solve(l, r);
}
int mid = (l + r) >>> 1;
ForkJoinTask<Long> t1 = new AutoQueuedTask(thr, problem, l, mid);
ForkJoinTask<Long> t2 = new AutoQueuedTask(thr, problem, mid, r);
t2.fork();
long res = 0;
res += t1.invoke();
res += t2.join();
return res;
}
}
}

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
/**
* Benchmark assesses ForkJoinPool performance with dependence on threshold.
*/
@OutputTimeUnit(TimeUnit.MINUTES)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 5, time = 2)
@Fork(3)
public class ForkJoinPoolThresholdStatic {
/**
* Implementation notes:
*
* This test solves the problem on different threshold levels.
* The optimal level depends on available parallelism.
* Lower thresholds will suffer because of ForkJoinPool infrastructure overheads.
* Higher thresholds will suffer because of lower available task parallelism.
*
* Baseline includes solving problem sequentially.
* Hence, each test provides the speedup for parallel execution
* versus sequential version.
*/
@Param("10000000")
private int size;
/** Encapsulate all the state depended on only by actual test. This avoids running baselines for every parameter value. */
@State(Scope.Benchmark)
public static class TestState {
@Param("0")
private int workers;
@Param({"1", "5", "10", "100", "1000", "10000", "100000", "1000000", "10000000"})
private int threshold;
private ForkJoinPool fjp;
@Setup
public void setup() {
if (workers == 0) {
workers = Runtime.getRuntime().availableProcessors();
}
fjp = new ForkJoinPool(workers);
}
@TearDown
public void teardown() {
fjp.shutdownNow();
}
}
private Problem problem;
@Setup
public void setup() {
problem = new Problem(size);
}
@Benchmark
public long baselineRaw() {
return problem.solve();
}
@Benchmark
public Long test(TestState state) throws ExecutionException, InterruptedException {
return state.fjp.invoke(new AdjustableThreshTask(state.threshold, problem, 0, problem.size()));
}
private static class AdjustableThreshTask extends RecursiveTask<Long> {
private final int thr;
private final Problem problem;
private final int l;
private final int r;
public AdjustableThreshTask(int thr, Problem p, int l, int r) {
this.thr = thr;
this.problem = p;
this.l = l;
this.r = r;
}
@Override
protected Long compute() {
if (r - l <= thr) {
return problem.solve(l, r);
}
int mid = (l + r) >>> 1;
ForkJoinTask<Long> t1 = new AdjustableThreshTask(thr, problem, l, mid);
ForkJoinTask<Long> t2 = new AdjustableThreshTask(thr, problem, mid, r);
ForkJoinTask.invokeAll(t1, t2);
long res = 0;
res += t1.join();
res += t2.join();
return res;
}
}
}

View file

@ -0,0 +1,238 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
public class Locks {
private ReentrantLock reentrantLock;
private ReentrantLock fairReentrantLock;
private ReentrantReadWriteLock reentrantRWLock;
private ReentrantReadWriteLock fairReentrantRWLock;
private Semaphore semaphore;
private Semaphore fairSemaphore;
private Lock reentrantWriteLock;
private Mutex mutex;
@Setup
public void setup() {
reentrantLock = new ReentrantLock(false);
fairReentrantLock = new ReentrantLock(true);
reentrantRWLock = new ReentrantReadWriteLock(false);
fairReentrantRWLock = new ReentrantReadWriteLock(true);
semaphore = new Semaphore(1, false);
fairSemaphore = new Semaphore(1, true);
reentrantWriteLock = new ReentrantReadWriteLock(false).writeLock();
mutex = new Mutex();
}
@Benchmark
public void testSynchronizedBlock() {
synchronized (this) {
Blackhole.consumeCPU(10);
}
Blackhole.consumeCPU(5);
}
@Benchmark
public void testFairReentrantLock() {
fairReentrantLock.lock();
try {
Blackhole.consumeCPU(10);
} finally {
fairReentrantLock.unlock();
}
Blackhole.consumeCPU(5);
}
@Benchmark
public void testReentrantLock() {
reentrantLock.lock();
try {
Blackhole.consumeCPU(10);
} finally {
reentrantLock.unlock();
}
Blackhole.consumeCPU(5);
}
@Benchmark
public void testFairReentrantReadWriteLock() {
fairReentrantRWLock.readLock().lock();
try {
Blackhole.consumeCPU(10);
} finally {
fairReentrantRWLock.readLock().unlock();
}
fairReentrantRWLock.writeLock().lock();
try {
Blackhole.consumeCPU(10);
} finally {
fairReentrantRWLock.writeLock().unlock();
}
Blackhole.consumeCPU(5);
}
@Benchmark
public void testReentrantReadWriteLock() {
reentrantRWLock.readLock().lock();
try {
Blackhole.consumeCPU(10);
} finally {
reentrantRWLock.readLock().unlock();
}
reentrantRWLock.writeLock().lock();
try {
Blackhole.consumeCPU(10);
} finally {
reentrantRWLock.writeLock().unlock();
}
Blackhole.consumeCPU(5);
}
@Benchmark
public void testReentrantWriteLock() {
reentrantWriteLock.lock();
try {
Blackhole.consumeCPU(10);
} finally {
reentrantWriteLock.unlock();
}
Blackhole.consumeCPU(5);
}
@Benchmark
public void testFairSemaphore() throws InterruptedException {
fairSemaphore.acquire();
try {
Blackhole.consumeCPU(10);
} finally {
fairSemaphore.release();
}
Blackhole.consumeCPU(5);
}
@Benchmark
public void testSemaphore() throws InterruptedException {
semaphore.acquire();
try {
Blackhole.consumeCPU(10);
} finally {
semaphore.release();
}
Blackhole.consumeCPU(5);
}
@Benchmark
public void testAbstractQueueSynchronizer() {
mutex.lock();
try {
Blackhole.consumeCPU(10);
} finally {
mutex.unlock();
}
Blackhole.consumeCPU(5);
}
@SuppressWarnings("serial")
private final class Mutex extends AbstractQueuedSynchronizer implements Lock, java.io.Serializable {
@Override
public boolean isHeldExclusively() {
return getState() == 1;
}
@Override
public boolean tryAcquire(int acquires) {
return compareAndSetState(0, 1);
}
@Override
public boolean tryRelease(int releases) {
setState(0);
return true;
}
@Override
public Condition newCondition() {
return new ConditionObject();
}
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
@Override
public void lock() {
acquire(1);
}
@Override
public boolean tryLock() {
return tryAcquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
acquireInterruptibly(1);
}
@Override
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return tryAcquireNanos(1, unit.toNanos(timeout));
}
@Override
public void unlock() {
release(1);
}
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, Alibaba Group Holding Limited. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
public class Maps {
private SimpleRandom rng;
private Map<Integer, Integer> map;
private Map<Integer, Integer> staticMap;
private Integer[] key;
private int removesPerMaxRandom;
private int insertsPerMaxRandom;
private int total;
private int position;
@Param("10000")
private int nkeys;
@Setup
public void initTest() {
int pRemove = 10;
int pInsert = 90;
removesPerMaxRandom = (int) ((pRemove / 100.0 * 0x7FFFFFFFL));
insertsPerMaxRandom = (int) ((pInsert / 100.0 * 0x7FFFFFFFL));
rng = new SimpleRandom();
map = new ConcurrentHashMap<>();
staticMap = new ConcurrentHashMap<>();
total = 0;
key = new Integer[nkeys];
for (int i = 0; i < key.length; ++i) {
key[i] = rng.next();
staticMap.put(rng.next(), rng.next());
}
position = key.length / 2;
}
@Benchmark
@Threads(4)
public void testConcurrentHashMap() {
int pos = position;
// random-walk around key positions, bunching accesses
int r = rng.next();
pos += (r & 7) - 3;
while (pos >= key.length) {
pos -= key.length;
}
while (pos < 0) {
pos += key.length;
}
Integer k = key[pos];
Integer x = map.get(k);
if (x != null) {
if (x.intValue() != k.intValue()) {
throw new Error("bad mapping: " + x + " to " + k);
}
if (r < removesPerMaxRandom) {
if (map.remove(k) != null) {
pos = total % key.length; // move from position
}
}
} else if (r < insertsPerMaxRandom) {
++pos;
map.put(k, k);
}
total += r;
position = pos;
}
@Benchmark
public ConcurrentHashMap<Integer, Integer> testConcurrentHashMapCopyConstructor() {
return new ConcurrentHashMap<>(staticMap);
}
@Benchmark
public ConcurrentHashMap<Integer, Integer> testConcurrentHashMapPutAll() {
ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>(nkeys);
for (int i = 0; i < nkeys; ++i) {
map.put(rng.next(), rng.next());
}
map.putAll(staticMap);
return map;
}
@Benchmark
public int testConcurrentHashMapIterators() {
ConcurrentHashMap<Integer, Integer> map = (ConcurrentHashMap<Integer, Integer>) staticMap;
int sum = 0;
Enumeration it = map.elements();
while (it.hasMoreElements()) {
sum += (int) it.nextElement();
}
it = map.keys();
while (it.hasMoreElements()) {
sum += (int) it.nextElement();
}
return sum;
}
private static class SimpleRandom {
private final static long multiplier = 0x5DEECE66DL;
private final static long addend = 0xBL;
private final static long mask = (1L << 48) - 1;
private final static AtomicLong seq = new AtomicLong(1);
private long seed = System.nanoTime() + seq.getAndIncrement();
public int next() {
long nextSeed = (seed * multiplier + addend) & mask;
seed = nextSeed;
return ((int) (nextSeed >>> 17)) & 0x7FFFFFFF;
}
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
/**
* Generic problem for concurrency tests.
*
* @author Aleksey Shipilev (aleksey.shipilev@oracle.com)
*/
public class Problem {
/*
* Implementation notes:
*
* This problem makes its bidding to confuse loop unrolling and CSE, and as such break loop optimizations.
* Should loop optimizations be allowed, the performance with different (l, r) could change non-linearly.
*/
private final int[] data;
private final int size;
public Problem(int size) {
this.size = size;
data = new int[size];
}
public long solve() {
return solve(0, size);
}
public long solve(int l, int r) {
long sum = 0;
for (int c = l; c < r; c++) {
int v = hash(data[c]);
if (filter(v)) {
sum += v;
}
}
return sum;
}
public int size() {
return size;
}
public static int hash(int x) {
x ^= (x << 21);
x ^= (x >>> 31);
x ^= (x << 4);
return x;
}
public static boolean filter(int i) {
return ((i & 0b101) == 0);
}
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.infra.ThreadParams;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Tests the different blocking queues in the java.util.concurrent package.
* The tests are done with a single producer and a variable number of consumers.
* The tests are created from Doug Lea's concurrent test suite.
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
public class ProducerConsumer {
@Param("100") // Will be expanded to at least the number of threads used
private int capacity;
@Param
private QueueType type;
private BlockingQueue<Integer> q;
private Producer prod;
@Setup
public void prepare(ThreadParams params) {
capacity = Math.max(params.getThreadCount(), capacity);
switch (type) {
case ABQ_F:
q = new ArrayBlockingQueue<>(capacity, true);
break;
case ABQ_NF:
q = new ArrayBlockingQueue<>(capacity, false);
break;
case LBQ:
q = new LinkedBlockingQueue<>(capacity);
break;
case PBQ:
q = new PriorityBlockingQueue<>(capacity);
break;
default:
throw new RuntimeException();
}
prod = new Producer(q);
prod.start();
}
@TearDown
public void teardown() {
prod.halt();
}
@Benchmark
public void test() {
try {
int last = -1;
int v = q.take();
if (v < last) {
throw new Error("Out-of-Order transfer");
}
Blackhole.consumeCPU(10);
} catch (Exception ie) {
}
}
public enum QueueType {
LBQ,
ABQ_NF,
ABQ_F,
PBQ,
}
private class Producer extends Thread {
private final BlockingQueue<Integer> queue;
private int i = 0;
private volatile boolean running;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
running = true;
try {
while (running) {
queue.put(i++);
}
} catch (Exception ie) {
}
}
public void halt() {
running = false;
this.interrupt();
}
}
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.infra.ThreadParams;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
public class Queues {
@Param("100") // Will be expanded to at least the number of threads used
private int capacity;
@Param
private QueueType type;
public enum QueueType {
LBQ,
ABQ_NF,
ABQ_F,
PBQ,
}
private BlockingQueue<Integer> q;
@Setup
public void setup(ThreadParams params) {
capacity = Math.max(params.getThreadCount(), capacity);
switch (type) {
case ABQ_F:
q = new ArrayBlockingQueue<>(capacity, true);
break;
case ABQ_NF:
q = new ArrayBlockingQueue<>(capacity, false);
break;
case LBQ:
q = new LinkedBlockingQueue<>(capacity);
break;
case PBQ:
q = new PriorityBlockingQueue<>(capacity);
break;
default:
throw new RuntimeException();
}
}
@Benchmark
public void test() {
try {
int l = (int) System.nanoTime();
Integer item = q.poll();
if (item != null) {
Blackhole.consumeCPU(5);
} else {
Blackhole.consumeCPU(10);
while (!q.offer(l)) {
Blackhole.consumeCPU(5);
}
}
} catch (Exception ie) {
throw new Error("iteration failed");
}
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
public class ThreadLocalRandomNextInt {
@State(Scope.Benchmark)
public static class Global {
public ThreadLocal<Random> tlr;
private List<ThreadLocal<Integer>> contaminators; // reachable, non-garbage-collectable
@Setup(Level.Trial)
public void setup() {
tlr = new ThreadLocal<Random>() {
@Override
protected Random initialValue() {
return java.util.concurrent.ThreadLocalRandom.current();
}
};
// contaminate ThreadLocals
int contaminatorCount = Integer.getInteger("contaminators", 0);
contaminators = new ArrayList<>(contaminatorCount);
for (int i = 0; i < contaminatorCount; i++) {
final int finalI = i;
ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return finalI;
}
};
contaminators.add(tl);
tl.get();
}
}
}
@State(Scope.Thread)
public static class Local {
public java.util.concurrent.ThreadLocalRandom tlr;
@Setup(Level.Trial)
public void setup() {
tlr = java.util.concurrent.ThreadLocalRandom.current();
}
}
@Benchmark
public int baseline(Local l) {
return l.tlr.nextInt();
}
@Benchmark
public int testJUC() {
return java.util.concurrent.ThreadLocalRandom.current().nextInt();
}
@Benchmark
public int testLang(Global g) {
return g.tlr.get().nextInt();
}
}

View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
@Fork(1)
@Threads(1)
@Warmup(iterations = 5, time = 5)
@Measurement(iterations = 5, time = 5)
public class UnparkBenchSleepersAfter {
/*
This micro creates thousands of sleeper threads after the threads doing the barrier await
to see if that has any effect on the barrier performance.
*/
@Param({"4000"})
int idles;
@Param({"2"})
int workers;
CyclicBarrier barrier;
@Benchmark
public void barrier() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(workers);
for (int i = 0; i < workers; i++) {
exec.submit(() ->
{
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
barrier.reset();
} finally {
latch.countDown();
}
});
}
latch.await();
}
IdleRunnable[] idleRunnables;
ExecutorService exec;
@Setup
public void setup() throws InterruptedException {
barrier = new CyclicBarrier(workers);
exec = Executors.newFixedThreadPool(workers);
CountDownLatch latch = new CountDownLatch(workers);
for (int i = 0; i < workers; i++) { // warmup exec
exec.submit(() -> {
try {
Thread.sleep(0);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
latch.countDown();
}
});
}
latch.await();
idleRunnables = new IdleRunnable[idles];
for(int i = 0; i < idles; i++) {
IdleRunnable r = new IdleRunnable();
idleRunnables[i] = r;
new Thread(r).start();
}
}
@TearDown
public void tearDown() {
for (IdleRunnable r : idleRunnables) {
r.stop();
}
exec.shutdown();
}
public static class IdleRunnable implements Runnable {
volatile boolean done;
Thread myThread;
@Override
public void run() {
myThread = Thread.currentThread();
while (!done) {
LockSupport.park();
}
}
public void stop() {
done = true;
LockSupport.unpark(myThread);
}
}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.concurrent;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Control;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.LockSupport;
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
@Fork(1)
@Threads(1)
@Warmup(iterations = 5, time = 5)
@Measurement(iterations = 5, time = 5)
public class UnparkBenchSleepersBefore {
/*
This micro creates thousands of sleeper threads before the threads doing the barrier await
to see if that has any effect on the barrier performance, as seen with JDK-8305670.
*/
@Param({"4000"})
int idles;
@Param({"2"})
int workers;
CyclicBarrier barrier;
@Benchmark
public void barrier() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(workers);
for (int i = 0; i < workers; i++) {
exec.submit(() -> {
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
barrier.reset();
} finally {
latch.countDown();
}
});
}
latch.await();
}
IdleRunnable[] idleRunnables;
ExecutorService exec;
@Setup
public void setup() {
idleRunnables = new IdleRunnable[idles];
for(int i = 0; i < idleRunnables.length; i++) {
idleRunnables[i] = new IdleRunnable();
new Thread(idleRunnables[i]).start();
}
barrier = new CyclicBarrier(workers);
exec = Executors.newFixedThreadPool(workers); // order is important, create this executor only after idle threads
}
@TearDown
public void tearDown() {
for (IdleRunnable r : idleRunnables) {
r.stop();
}
exec.shutdown();
}
public static class IdleRunnable implements Runnable {
volatile boolean done;
Thread myThread;
@Override
public void run() {
myThread = Thread.currentThread();
while (!done) {
LockSupport.park();
}
}
public void stop() {
done = true;
LockSupport.unpark(myThread);
}
}
}