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