bd4160cea8
Co-authored-by: Erik Österlund <eosterlund@openjdk.org> Co-authored-by: Stefan Karlsson <stefank@openjdk.org> Co-authored-by: Coleen Phillimore <coleenp@openjdk.org> Reviewed-by: rkennke, coleenp, dcubed
245 lines
8.9 KiB
Java
245 lines
8.9 KiB
Java
/*
|
|
* Copyright (c) 2024, 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.
|
|
*/
|
|
|
|
/**
|
|
* @test id=NormalDeflation
|
|
* @summary A collection of small tests using synchronized, wait, notify to try
|
|
* and achieve good cheap coverage of UseObjectMonitorTable.
|
|
* @library /test/lib
|
|
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
|
* -XX:+UseObjectMonitorTable
|
|
* UseObjectMonitorTableTest
|
|
*/
|
|
|
|
/**
|
|
* @test id=ExtremeDeflation
|
|
* @summary Run the same tests but with deflation running constantly.
|
|
* @library /test/lib
|
|
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
|
* -XX:GuaranteedAsyncDeflationInterval=1
|
|
* -XX:+UseObjectMonitorTable
|
|
* UseObjectMonitorTableTest
|
|
*/
|
|
|
|
import jdk.test.lib.Utils;
|
|
|
|
import java.lang.Runnable;
|
|
import java.util.concurrent.BrokenBarrierException;
|
|
import java.util.concurrent.CyclicBarrier;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ThreadFactory;
|
|
import java.util.Random;
|
|
import java.util.stream.Stream;
|
|
|
|
public class UseObjectMonitorTableTest {
|
|
static final ThreadFactory TF = Executors.defaultThreadFactory();
|
|
|
|
static class WaitNotifyTest implements Runnable {
|
|
static final int ITERATIONS = 10_000;
|
|
static final int THREADS = 10;
|
|
final WaitNotifySyncChannel startLatchChannel = new WaitNotifySyncChannel();
|
|
final WaitNotifySyncChannel endLatchChannel = new WaitNotifySyncChannel();
|
|
int count = 0;
|
|
|
|
static class WaitNotifyCountDownLatch {
|
|
int latch;
|
|
WaitNotifyCountDownLatch(int count) {
|
|
latch = count;
|
|
}
|
|
synchronized void await() {
|
|
while (latch != 0) {
|
|
try {
|
|
wait();
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException("WaitNotifyTest: Unexpected interrupt", e);
|
|
}
|
|
}
|
|
}
|
|
synchronized void countDown() {
|
|
if (latch != 0) {
|
|
latch--;
|
|
if (latch == 0) {
|
|
notifyAll();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static class WaitNotifySyncChannel extends WaitNotifyCountDownLatch {
|
|
WaitNotifyCountDownLatch object;
|
|
WaitNotifySyncChannel() { super(0); }
|
|
synchronized void send(WaitNotifyCountDownLatch object, int count) {
|
|
await();
|
|
latch = count;
|
|
this.object = object;
|
|
notifyAll();
|
|
}
|
|
synchronized WaitNotifyCountDownLatch receive() {
|
|
while (latch == 0) {
|
|
try {
|
|
wait();
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException("WaitNotifyTest: Unexpected interrupt", e);
|
|
}
|
|
}
|
|
countDown();
|
|
return object;
|
|
}
|
|
}
|
|
synchronized int getCount() {
|
|
return count;
|
|
}
|
|
synchronized void increment() {
|
|
count++;
|
|
}
|
|
public void run() {
|
|
System.out.println("WaitNotifyTest started.");
|
|
for (int t = 0; t < THREADS; t++) {
|
|
TF.newThread(() -> {
|
|
for (int i = 0; i < ITERATIONS; i++) {
|
|
startLatchChannel.receive().await();
|
|
increment();
|
|
endLatchChannel.receive().countDown();
|
|
}
|
|
}).start();
|
|
}
|
|
for (int i = 0; i < ITERATIONS; i++) {
|
|
WaitNotifyCountDownLatch startLatch = new WaitNotifyCountDownLatch(1);
|
|
WaitNotifyCountDownLatch endLatch = new WaitNotifyCountDownLatch(THREADS);
|
|
int count = getCount();
|
|
if (count != i * THREADS) {
|
|
throw new RuntimeException("WaitNotifyTest: Invalid Count " + count +
|
|
" pre-iteration " + i);
|
|
}
|
|
startLatchChannel.send(startLatch, 10);
|
|
startLatch.countDown();
|
|
endLatchChannel.send(endLatch, 10);
|
|
endLatch.await();
|
|
}
|
|
int count = getCount();
|
|
if (count != ITERATIONS * THREADS) {
|
|
throw new RuntimeException("WaitNotifyTest: Invalid Count " + count);
|
|
}
|
|
System.out.println("WaitNotifyTest passed.");
|
|
}
|
|
}
|
|
|
|
static class RandomDepthTest implements Runnable {
|
|
static final int THREADS = 10;
|
|
static final int ITERATIONS = 10_000;
|
|
static final int MAX_DEPTH = 20;
|
|
static final int MAX_RECURSION_COUNT = 10;
|
|
static final double RECURSION_CHANCE = .25;
|
|
final Random random = Utils.getRandomInstance();
|
|
final Locker lockers[] = new Locker[MAX_DEPTH];
|
|
final CyclicBarrier syncBarrier = new CyclicBarrier(THREADS + 1);
|
|
int count = 0;
|
|
|
|
class Locker {
|
|
final int depth;
|
|
Locker(int depth) {
|
|
this.depth = depth;
|
|
}
|
|
synchronized int getCount() {
|
|
if (depth == MAX_DEPTH) {
|
|
return count;
|
|
}
|
|
return lockers[depth].getCount();
|
|
}
|
|
synchronized void increment(int recursion_count) {
|
|
if (recursion_count != MAX_RECURSION_COUNT &&
|
|
random.nextDouble() < RECURSION_CHANCE) {
|
|
this.increment(recursion_count + 1);
|
|
return;
|
|
}
|
|
if (depth == MAX_DEPTH) {
|
|
count++;
|
|
return;
|
|
}
|
|
lockers[depth + random.nextInt(MAX_DEPTH - depth)].increment(recursion_count);
|
|
}
|
|
synchronized Locker create() {
|
|
if (depth != MAX_DEPTH) {
|
|
lockers[depth] = (new Locker(depth + 1)).create();
|
|
}
|
|
return this;
|
|
}
|
|
}
|
|
int getCount() {
|
|
return lockers[0].getCount();
|
|
}
|
|
void increment() {
|
|
lockers[random.nextInt(MAX_DEPTH)].increment(0);
|
|
}
|
|
void create() {
|
|
lockers[0] = (new Locker(1)).create();
|
|
}
|
|
void syncPoint() {
|
|
try {
|
|
syncBarrier.await();
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException("RandomDepthTest: Unexpected interrupt", e);
|
|
} catch (BrokenBarrierException e) {
|
|
throw new RuntimeException("RandomDepthTest: Unexpected broken barrier", e);
|
|
}
|
|
}
|
|
public void run() {
|
|
System.out.println("RandomDepthTest started.");
|
|
for (int t = 0; t < THREADS; t++) {
|
|
TF.newThread(() -> {
|
|
syncPoint();
|
|
for (int i = 0; i < ITERATIONS; i++) {
|
|
increment();
|
|
}
|
|
syncPoint();
|
|
}).start();
|
|
}
|
|
create();
|
|
syncPoint();
|
|
syncPoint();
|
|
int count = getCount();
|
|
if (count != THREADS * ITERATIONS) {
|
|
throw new RuntimeException("RandomDepthTest: Invalid Count " + count);
|
|
}
|
|
System.out.println("RandomDepthTest passed.");
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
Stream.of(
|
|
TF.newThread(new WaitNotifyTest()),
|
|
TF.newThread(new RandomDepthTest())
|
|
).map(t -> {
|
|
t.start();
|
|
return t;
|
|
}).forEach(t -> {
|
|
try {
|
|
t.join();
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException("UseObjectMonitorTableTest: Unexpected interrupt", e);
|
|
}
|
|
});
|
|
|
|
System.out.println("UseObjectMonitorTableTest passed.");
|
|
}
|
|
}
|