blob: 2ea472e1fabed51939100381f191546edfa1ef6b [file] [log] [blame]
// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.classmerging.horizontal;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest.Host.Companion.HostA;
import com.android.tools.r8.classmerging.horizontal.ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest.Host.Companion.HostB;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest
extends TestBase {
@Parameter(0)
public TestParameters parameters;
@Parameter(1)
public int thread;
@Parameters(name = "{0}, thread: {1}")
public static List<Object[]> parameters() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(), ImmutableList.of(1, 2, 3));
}
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepRules(
"-keep class " + Main.class.getTypeName() + " {",
" public static void thread0();",
" public static void thread" + thread + "();",
"}")
// TODO(b/205611444): HostA and HostB should be merged when thread is 1.
.addHorizontallyMergedClassesInspector(
HorizontallyMergedClassesInspector::assertNoClassesMerged)
.addOptionsModification(
options ->
options.horizontalClassMergerOptions().setEnableClassInitializerDeadlockDetection())
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile();
}
static class Main {
// @Keep
public static void thread0() {
// Takes the lock for Host, then Companion, and then waits to take the lock for HostA.
System.out.println(Host.companion.a);
System.out.println(Host.companion.b);
}
// @Keep
public static void thread1() {
// Ditto. In this case there is no risk of a deadlock, since one of thread0 and thread1 will
// take the lock for Host and the other thread will then wait on the lock for Host to be
// released.
System.out.println(Host.companion.a);
System.out.println(Host.companion.b);
}
// @Keep
public static void thread2() {
// Takes the lock for Companion, then HostA, and then waits to take the lock for its
// superclass Host.
System.out.println(new Host.Companion());
}
// @Keep
public static void thread3() {
// Takes the lock for HostA, and then waits to take the lock for its superclass Host.
HostA.init();
}
}
static class Host {
static Companion companion = new Companion();
static class Companion {
Host a = new HostA();
Host b = new HostB();
static class HostA extends Host {
@NeverInline
static void init() {
System.out.println("HostA.init");
}
}
static class HostB extends Host {}
}
}
}