blob: 82f1a6c6118c88ca1e70773481a60197715badaa [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package art;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.function.Function;
public class Test1922 {
// Set to true to run with all combinations of locks. This isn't really needed for the test to be
// useful and fully representative.
public final static boolean ALL_COMBOS = false;
// A runnable that lets us know when a different thread is paused.
public static class ThreadPauser implements Runnable {
private boolean suspend;
private volatile Thread paused_thread;
public ThreadPauser(boolean suspend) {
paused_thread = null;
this.suspend = suspend;
}
public ThreadPauser() {
paused_thread = null;
this.suspend = false;
}
@Override
public void run() {
this.paused_thread = Thread.currentThread();
if (suspend) {
Suspension.suspend(paused_thread);
}
while (this.paused_thread != null) {}
}
public void waitForOtherThreadToPause() {
while (this.paused_thread == null) {}
if (suspend) {
while (!Suspension.isSuspended(this.paused_thread)) {}
}
}
public void wakeupOtherThread() {
if (this.paused_thread == null) {
throw new Error("Other thread is not paused!");
}
if (suspend) {
Suspension.resume(this.paused_thread);
while (Suspension.isSuspended(this.paused_thread)) {}
}
this.paused_thread = null;
}
}
// A class with a number of monitor operations in its methods.
public static class Target {
public String name;
public Target(String name) { this.name = name; }
public String toString() { return "Target(\"" + name + "\")"; }
// synchronize on Target.class
public void lockClass(Runnable safepoint) {
synchronized (this.getClass()) {
safepoint.run();
}
}
// use java synchronized method.
public synchronized void lockSync(Runnable safepoint) {
safepoint.run();
}
// use java synchronized method and synchronize on another object.
public synchronized void lockExtra(Object l, Runnable safepoint) {
synchronized (l) {
safepoint.run();
}
}
// monitor enter the object 'l' in native code.
public native void lockNative(Object l, Runnable safepoint);
// monitor enter 'this' in native code.
public native void lockThisNative(Runnable safepoint);
// synchronize on 'l'
public void lockOther(Object l, Runnable safepoint) {
synchronized (l) {
safepoint.run();
}
}
// Don't do anything. Just call the next method.
public void callSafepoint(Runnable safepoint) {
safepoint.run();
}
}
// A lock with a toString.
public static class NamedLock {
public String name;
public NamedLock(String name) { this.name = name; }
public String toString() { return "NamedLock(\"" + name + "\")"; }
}
private static Object[] sortByString(Object[] arr) {
Arrays.sort(arr, (a, b) -> a.toString().compareTo(b.toString()));
return arr;
}
public static class PrintOwnedMonitorsStackDepthRunnable implements Runnable {
public final Thread target;
public PrintOwnedMonitorsStackDepthRunnable(Thread target) {
this.target = target;
}
public void run() {
System.out.println("Owned monitors: " +
Arrays.toString(sortByString(getOwnedMonitorStackDepthInfo(target))));
}
}
public static class PrintOwnedMonitorsRunnable implements Runnable {
public final Thread target;
public PrintOwnedMonitorsRunnable(Thread target) {
this.target = target;
}
public void run() {
System.out.println("Owned monitors: " +
Arrays.toString(sortByString(getOwnedMonitors(target))));
}
}
public static void run() throws Exception {
setupTest();
System.out.println("owner-monitors, This thread");
runTestsCurrentThread("owned-monitor",
new PrintOwnedMonitorsRunnable(Thread.currentThread()));
System.out.println("owner-monitors, no suspend, Other thread");
runTestsOtherThread("owned-monitor", false,
(t) -> { new PrintOwnedMonitorsRunnable(t).run(); });
System.out.println("owner-monitors, suspend, Other thread");
runTestsOtherThread("owned-monitor", true,
(t) -> { new PrintOwnedMonitorsRunnable(t).run(); });
System.out.println("owner-monitors-stack-depth, This thread");
runTestsCurrentThread("owned-stack-depth",
new PrintOwnedMonitorsStackDepthRunnable(Thread.currentThread()));
System.out.println("owner-monitors-stack-depth, no suspend, other thread");
runTestsOtherThread("owned-stack-depth", false,
(t) -> { new PrintOwnedMonitorsStackDepthRunnable(t).run(); });
System.out.println("owner-monitors-stack-depth, suspend, other thread");
runTestsOtherThread("owned-stack-depth", true,
(t) -> { new PrintOwnedMonitorsStackDepthRunnable(t).run(); });
}
public static void runTestsOtherThread(String name, boolean suspend, Consumer<Thread> printer) {
final Target t = new Target("Other thread test (suspend: " + suspend + "): " + name);
final NamedLock l1 = new NamedLock("Lock 1");
final NamedLock l2 = new NamedLock("Lock 2");
final NamedLock l3 = new NamedLock("Lock 3");
List<Function<Runnable, Runnable>> MkSafepoints = Arrays.asList(
(r) -> { return new CallLockOther(t, l1, r); },
(r) -> { return new CallLockExtra(t, l2, r); },
(r) -> { return new CallLockNative(t, l3, r); },
(r) -> { return new CallLockThisNative(t, r); },
(r) -> { return new CallLockClass(t, r); },
(r) -> { return new CallLockSync(t, r); },
(r) -> { return new CallSafepoint(t, r); }
);
// Use ListIterators so we can have elements in the test multiple times.
ListIterator<Function<Runnable, Runnable>> li1 = MkSafepoints.listIterator();
for (Function<Runnable, Runnable> r1 = li1.next(); li1.hasNext(); r1 = li1.next()) {
ListIterator<Function<Runnable, Runnable>> li2 =
MkSafepoints.listIterator(ALL_COMBOS ? 0 : li1.previousIndex());
for (Function<Runnable, Runnable> r2 = li2.next(); li2.hasNext(); r2 = li2.next()) {
ListIterator<Function<Runnable, Runnable>> li3 =
MkSafepoints.listIterator(ALL_COMBOS ? 0 : li2.previousIndex());
for (Function<Runnable, Runnable> r3 = li3.next(); li3.hasNext(); r3 = li3.next()) {
System.out.println("Running: " + Arrays.toString(
new Object[] {
r1.apply(null).getClass(),
r2.apply(null).getClass(),
r3.apply(null).getClass(),
}));
try {
final ThreadPauser pause = new ThreadPauser(suspend);
final Thread thr = new Thread(r1.apply(r2.apply(r3.apply(pause))));
thr.start();
pause.waitForOtherThreadToPause();
printer.accept(thr);
pause.wakeupOtherThread();
thr.join();
} catch (Exception e) {
throw new Error("Exception in test." , e);
}
}
}
}
}
public static void runTestsCurrentThread(String name, Runnable printer) {
final Target t = new Target("Current thread test: " + name);
final NamedLock l1 = new NamedLock("Lock 1");
final NamedLock l2 = new NamedLock("Lock 2");
final NamedLock l3 = new NamedLock("Lock 3");
List<Function<Runnable, Runnable>> MkSafepoints = Arrays.asList(
(r) -> { return new CallLockOther(t, l1, r); },
(r) -> { return new CallLockExtra(t, l2, r); },
(r) -> { return new CallLockNative(t, l3, r); },
(r) -> { return new CallLockThisNative(t, r); },
(r) -> { return new CallLockClass(t, r); },
(r) -> { return new CallLockSync(t, r); },
(r) -> { return new CallSafepoint(t, r); }
);
ListIterator<Function<Runnable, Runnable>> li1 = MkSafepoints.listIterator();
for (Function<Runnable, Runnable> r1 = li1.next(); li1.hasNext(); r1 = li1.next()) {
ListIterator<Function<Runnable, Runnable>> li2 =
MkSafepoints.listIterator(ALL_COMBOS ? 0 : li1.previousIndex());
for (Function<Runnable, Runnable> r2 = li2.next(); li2.hasNext(); r2 = li2.next()) {
ListIterator<Function<Runnable, Runnable>> li3 =
MkSafepoints.listIterator(ALL_COMBOS ? 0 : li2.previousIndex());
for (Function<Runnable, Runnable> r3 = li3.next(); li3.hasNext(); r3 = li3.next()) {
System.out.println("Running: " + Arrays.toString(
new Object[] {
r1.apply(null).getClass(),
r2.apply(null).getClass(),
r3.apply(null).getClass(),
}));
r1.apply(r2.apply(r3.apply(printer))).run();
}
}
}
}
public static native void setupTest();
public static native Object[] getOwnedMonitors(Thread thr);
public static native MonitorStackDepthInfo[] getOwnedMonitorStackDepthInfo(Thread thr);
public static class MonitorStackDepthInfo {
public final int depth;
public final Object monitor;
public MonitorStackDepthInfo(int depth, Object monitor) {
this.depth = depth;
this.monitor = monitor;
}
public String toString() {
return "{ depth: " + depth + ", monitor: \"" + monitor.toString() + "\" }";
}
}
// We want to avoid synthetic methods that would mess up our stack-depths so we make everything
// explicit here.
public static class CallSafepoint implements Runnable {
public final Target target;
public final Runnable safepoint;
public CallSafepoint(Target target, Runnable safepoint) {
this.target = target;
this.safepoint = safepoint;
}
public void run() {
target.callSafepoint(safepoint);
}
}
public static class CallLockOther implements Runnable {
public final Target target;
public final Object l;
public final Runnable safepoint;
public CallLockOther(Target target, Object l, Runnable safepoint) {
this.target = target;
this.l = l;
this.safepoint = safepoint;
}
public void run() {
target.lockOther(l, safepoint);
}
}
public static class CallLockExtra implements Runnable {
public final Target target;
public final Object l;
public final Runnable safepoint;
public CallLockExtra(Target target, Object l, Runnable safepoint) {
this.target = target;
this.l = l;
this.safepoint = safepoint;
}
public void run() {
target.lockExtra(l, safepoint);
}
}
public static class CallLockThisNative implements Runnable {
public final Target target;
public final Runnable safepoint;
public CallLockThisNative(Target target, Runnable safepoint) {
this.target = target;
this.safepoint = safepoint;
}
public void run() {
target.lockThisNative(safepoint);
}
}
public static class CallLockNative implements Runnable {
public final Target target;
public final Object l;
public final Runnable safepoint;
public CallLockNative(Target target, Object l, Runnable safepoint) {
this.target = target;
this.l = l;
this.safepoint = safepoint;
}
public void run() {
target.lockNative(l, safepoint);
}
}
public static class CallLockClass implements Runnable {
public final Target target;
public final Runnable safepoint;
public CallLockClass(Target target, Runnable safepoint) {
this.target = target;
this.safepoint = safepoint;
}
public void run() {
target.lockClass(safepoint);
}
}
public static class CallLockSync implements Runnable {
public final Target target;
public final Runnable safepoint;
public CallLockSync(Target target, Runnable safepoint) {
this.target = target;
this.safepoint = safepoint;
}
public void run() {
target.lockSync(safepoint);
}
}
}