xref: /aosp_15_r20/art/test/952-invoke-custom/src/TestInvokeCustomWithConcurrentThreads.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker import annotations.BootstrapMethod;
18*795d594fSAndroid Build Coastguard Worker import annotations.CalledByIndy;
19*795d594fSAndroid Build Coastguard Worker import java.lang.invoke.CallSite;
20*795d594fSAndroid Build Coastguard Worker import java.lang.invoke.ConstantCallSite;
21*795d594fSAndroid Build Coastguard Worker import java.lang.invoke.MethodHandle;
22*795d594fSAndroid Build Coastguard Worker import java.lang.invoke.MethodHandles;
23*795d594fSAndroid Build Coastguard Worker import java.lang.invoke.MethodType;
24*795d594fSAndroid Build Coastguard Worker import java.util.concurrent.CyclicBarrier;
25*795d594fSAndroid Build Coastguard Worker import java.util.concurrent.atomic.AtomicInteger;
26*795d594fSAndroid Build Coastguard Worker 
27*795d594fSAndroid Build Coastguard Worker public class TestInvokeCustomWithConcurrentThreads extends TestBase implements Runnable {
28*795d594fSAndroid Build Coastguard Worker     private static final int NUMBER_OF_THREADS = 16;
29*795d594fSAndroid Build Coastguard Worker 
30*795d594fSAndroid Build Coastguard Worker     private static final AtomicInteger nextIndex = new AtomicInteger(0);
31*795d594fSAndroid Build Coastguard Worker 
32*795d594fSAndroid Build Coastguard Worker     private static final ThreadLocal<Integer> threadIndex =
33*795d594fSAndroid Build Coastguard Worker             new ThreadLocal<Integer>() {
34*795d594fSAndroid Build Coastguard Worker                 @Override
35*795d594fSAndroid Build Coastguard Worker                 protected Integer initialValue() {
36*795d594fSAndroid Build Coastguard Worker                     return nextIndex.getAndIncrement();
37*795d594fSAndroid Build Coastguard Worker                 }
38*795d594fSAndroid Build Coastguard Worker             };
39*795d594fSAndroid Build Coastguard Worker 
40*795d594fSAndroid Build Coastguard Worker     // Array of call sites instantiated, one per thread
41*795d594fSAndroid Build Coastguard Worker     private static final CallSite[] instantiated = new CallSite[NUMBER_OF_THREADS];
42*795d594fSAndroid Build Coastguard Worker 
43*795d594fSAndroid Build Coastguard Worker     // Array of counters for how many times each instantiated call site is called
44*795d594fSAndroid Build Coastguard Worker     private static final AtomicInteger[] called = new AtomicInteger[NUMBER_OF_THREADS];
45*795d594fSAndroid Build Coastguard Worker 
46*795d594fSAndroid Build Coastguard Worker     // Array of call site indices of which call site a thread invoked
47*795d594fSAndroid Build Coastguard Worker     private static final AtomicInteger[] targetted = new AtomicInteger[NUMBER_OF_THREADS];
48*795d594fSAndroid Build Coastguard Worker 
49*795d594fSAndroid Build Coastguard Worker     // Synchronization barrier all threads will wait on in the bootstrap method.
50*795d594fSAndroid Build Coastguard Worker     private static final CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS);
51*795d594fSAndroid Build Coastguard Worker 
TestInvokeCustomWithConcurrentThreads()52*795d594fSAndroid Build Coastguard Worker     private TestInvokeCustomWithConcurrentThreads() {}
53*795d594fSAndroid Build Coastguard Worker 
getThreadIndex()54*795d594fSAndroid Build Coastguard Worker     private static int getThreadIndex() {
55*795d594fSAndroid Build Coastguard Worker         return threadIndex.get().intValue();
56*795d594fSAndroid Build Coastguard Worker     }
57*795d594fSAndroid Build Coastguard Worker 
notUsed(int x)58*795d594fSAndroid Build Coastguard Worker     public static int notUsed(int x) {
59*795d594fSAndroid Build Coastguard Worker         return x;
60*795d594fSAndroid Build Coastguard Worker     }
61*795d594fSAndroid Build Coastguard Worker 
run()62*795d594fSAndroid Build Coastguard Worker     public void run() {
63*795d594fSAndroid Build Coastguard Worker         int x = setCalled(-1 /* argument dropped */);
64*795d594fSAndroid Build Coastguard Worker         notUsed(x);
65*795d594fSAndroid Build Coastguard Worker     }
66*795d594fSAndroid Build Coastguard Worker 
67*795d594fSAndroid Build Coastguard Worker     @CalledByIndy(
68*795d594fSAndroid Build Coastguard Worker         bootstrapMethod =
69*795d594fSAndroid Build Coastguard Worker                 @BootstrapMethod(
70*795d594fSAndroid Build Coastguard Worker                     enclosingType = TestInvokeCustomWithConcurrentThreads.class,
71*795d594fSAndroid Build Coastguard Worker                     name = "linkerMethod",
72*795d594fSAndroid Build Coastguard Worker                     parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}
73*795d594fSAndroid Build Coastguard Worker                 ),
74*795d594fSAndroid Build Coastguard Worker         fieldOrMethodName = "setCalled",
75*795d594fSAndroid Build Coastguard Worker         returnType = int.class,
76*795d594fSAndroid Build Coastguard Worker         parameterTypes = {int.class}
77*795d594fSAndroid Build Coastguard Worker     )
setCalled(int index)78*795d594fSAndroid Build Coastguard Worker     private static int setCalled(int index) {
79*795d594fSAndroid Build Coastguard Worker         called[index].getAndIncrement();
80*795d594fSAndroid Build Coastguard Worker         targetted[getThreadIndex()].set(index);
81*795d594fSAndroid Build Coastguard Worker         return 0;
82*795d594fSAndroid Build Coastguard Worker     }
83*795d594fSAndroid Build Coastguard Worker 
84*795d594fSAndroid Build Coastguard Worker     @SuppressWarnings("unused")
linkerMethod( MethodHandles.Lookup caller, String name, MethodType methodType)85*795d594fSAndroid Build Coastguard Worker     private static CallSite linkerMethod(
86*795d594fSAndroid Build Coastguard Worker             MethodHandles.Lookup caller, String name, MethodType methodType) throws Throwable {
87*795d594fSAndroid Build Coastguard Worker         MethodHandle mh =
88*795d594fSAndroid Build Coastguard Worker                 caller.findStatic(TestInvokeCustomWithConcurrentThreads.class, name, methodType);
89*795d594fSAndroid Build Coastguard Worker         assertEquals(methodType, mh.type());
90*795d594fSAndroid Build Coastguard Worker         assertEquals(mh.type().parameterCount(), 1);
91*795d594fSAndroid Build Coastguard Worker         mh = MethodHandles.insertArguments(mh, 0, getThreadIndex());
92*795d594fSAndroid Build Coastguard Worker         mh = MethodHandles.dropArguments(mh, 0, int.class);
93*795d594fSAndroid Build Coastguard Worker         assertEquals(mh.type().parameterCount(), 1);
94*795d594fSAndroid Build Coastguard Worker         assertEquals(methodType, mh.type());
95*795d594fSAndroid Build Coastguard Worker 
96*795d594fSAndroid Build Coastguard Worker         // Wait for all threads to be in this method.
97*795d594fSAndroid Build Coastguard Worker         // Multiple call sites should be created, but only one
98*795d594fSAndroid Build Coastguard Worker         // invoked.
99*795d594fSAndroid Build Coastguard Worker         barrier.await();
100*795d594fSAndroid Build Coastguard Worker 
101*795d594fSAndroid Build Coastguard Worker         instantiated[getThreadIndex()] = new ConstantCallSite(mh);
102*795d594fSAndroid Build Coastguard Worker         return instantiated[getThreadIndex()];
103*795d594fSAndroid Build Coastguard Worker     }
104*795d594fSAndroid Build Coastguard Worker 
test()105*795d594fSAndroid Build Coastguard Worker     public static void test() throws Throwable {
106*795d594fSAndroid Build Coastguard Worker         // Initialize counters for which call site gets invoked
107*795d594fSAndroid Build Coastguard Worker         for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
108*795d594fSAndroid Build Coastguard Worker             called[i] = new AtomicInteger(0);
109*795d594fSAndroid Build Coastguard Worker             targetted[i] = new AtomicInteger(0);
110*795d594fSAndroid Build Coastguard Worker         }
111*795d594fSAndroid Build Coastguard Worker 
112*795d594fSAndroid Build Coastguard Worker         // Run threads that each invoke-custom the call site
113*795d594fSAndroid Build Coastguard Worker         Thread[] threads = new Thread[NUMBER_OF_THREADS];
114*795d594fSAndroid Build Coastguard Worker         for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
115*795d594fSAndroid Build Coastguard Worker             threads[i] = new Thread(new TestInvokeCustomWithConcurrentThreads());
116*795d594fSAndroid Build Coastguard Worker             threads[i].start();
117*795d594fSAndroid Build Coastguard Worker         }
118*795d594fSAndroid Build Coastguard Worker 
119*795d594fSAndroid Build Coastguard Worker         // Wait for all threads to complete
120*795d594fSAndroid Build Coastguard Worker         for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
121*795d594fSAndroid Build Coastguard Worker             threads[i].join();
122*795d594fSAndroid Build Coastguard Worker         }
123*795d594fSAndroid Build Coastguard Worker 
124*795d594fSAndroid Build Coastguard Worker         // Check one call site instance won
125*795d594fSAndroid Build Coastguard Worker         int winners = 0;
126*795d594fSAndroid Build Coastguard Worker         int votes = 0;
127*795d594fSAndroid Build Coastguard Worker         for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
128*795d594fSAndroid Build Coastguard Worker             assertNotEquals(instantiated[i], null);
129*795d594fSAndroid Build Coastguard Worker             if (called[i].get() != 0) {
130*795d594fSAndroid Build Coastguard Worker                 winners++;
131*795d594fSAndroid Build Coastguard Worker                 votes += called[i].get();
132*795d594fSAndroid Build Coastguard Worker             }
133*795d594fSAndroid Build Coastguard Worker         }
134*795d594fSAndroid Build Coastguard Worker 
135*795d594fSAndroid Build Coastguard Worker         System.out.println("Winners " + winners + " Votes " + votes);
136*795d594fSAndroid Build Coastguard Worker 
137*795d594fSAndroid Build Coastguard Worker         // We assert this below but output details when there's an error as
138*795d594fSAndroid Build Coastguard Worker         // it's non-deterministic.
139*795d594fSAndroid Build Coastguard Worker         if (winners != 1) {
140*795d594fSAndroid Build Coastguard Worker             System.out.println("Threads did not the same call-sites:");
141*795d594fSAndroid Build Coastguard Worker             for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
142*795d594fSAndroid Build Coastguard Worker                 System.out.format(
143*795d594fSAndroid Build Coastguard Worker                         " Thread % 2d invoked call site instance #%02d\n", i, targetted[i].get());
144*795d594fSAndroid Build Coastguard Worker             }
145*795d594fSAndroid Build Coastguard Worker         }
146*795d594fSAndroid Build Coastguard Worker 
147*795d594fSAndroid Build Coastguard Worker         // We assert this below but output details when there's an error as
148*795d594fSAndroid Build Coastguard Worker         // it's non-deterministic.
149*795d594fSAndroid Build Coastguard Worker         if (votes != NUMBER_OF_THREADS) {
150*795d594fSAndroid Build Coastguard Worker             System.out.println("Call-sites invocations :");
151*795d594fSAndroid Build Coastguard Worker             for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
152*795d594fSAndroid Build Coastguard Worker                 System.out.format(
153*795d594fSAndroid Build Coastguard Worker                         " Call site instance #%02d was invoked % 2d times\n", i, called[i].get());
154*795d594fSAndroid Build Coastguard Worker             }
155*795d594fSAndroid Build Coastguard Worker         }
156*795d594fSAndroid Build Coastguard Worker 
157*795d594fSAndroid Build Coastguard Worker         assertEquals(winners, 1);
158*795d594fSAndroid Build Coastguard Worker         assertEquals(votes, NUMBER_OF_THREADS);
159*795d594fSAndroid Build Coastguard Worker     }
160*795d594fSAndroid Build Coastguard Worker }
161