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 161 162 163
|
/*
* Copyright (c) 2022, 2023, 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
* @summary Stress test ScopedValue with many bindings and rebindings
* @enablePreview
* @library /test/lib
* @key randomness
* @run junit ManyBindings
*/
import java.lang.ScopedValue.Carrier;
import java.util.Arrays;
import java.util.Objects;
import java.util.Random;
import jdk.test.lib.RandomFactory;
import jdk.test.lib.thread.VThreadRunner;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class ManyBindings {
private static final Random RND = RandomFactory.getRandom();
// number of scoped values to create
private static final int SCOPED_VALUE_COUNT = 16;
// recursive depth to test
private static final int MAX_DEPTH = 24;
/**
* Stress test bindings on platform thread.
*/
@Test
void testPlatformThread() {
test();
}
/**
* Stress test bindings on virtual thread.
*/
@Test
void testVirtualThread() throws Exception {
VThreadRunner.run(() -> test());
}
/**
* Scoped value and its expected value (or null if not bound).
*/
record KeyAndValue<T>(ScopedValue<T> key, T value) {
KeyAndValue() {
this(ScopedValue.newInstance(), null);
}
}
/**
* Stress test bindings on current thread.
*/
private void test() {
KeyAndValue<Integer>[] array = new KeyAndValue[SCOPED_VALUE_COUNT];
for (int i = 0; i < array.length; i++) {
array[i] = new KeyAndValue<>();
}
test(array, 1);
}
/**
* Test that the scoped values in the array have the expected value, then
* recursively call this method with some of the scoped values bound to a
* new value.
*
* @param array the scoped values and their expected value
* @param depth current recurive depth
*/
private void test(KeyAndValue<Integer>[] array, int depth) {
if (depth > MAX_DEPTH)
return;
// check that the scoped values have the expected values
check(array);
// try to pollute the cache
lotsOfReads(array);
// create a Carrier to bind/rebind some of the scoped values
int len = array.length;
Carrier carrier = null;
KeyAndValue<Integer>[] newArray = Arrays.copyOf(array, len);
int n = Math.max(1, RND.nextInt(len / 2));
while (n > 0) {
int index = RND.nextInt(len);
ScopedValue<Integer> key = array[index].key;
int newValue = RND.nextInt();
if (carrier == null) {
carrier = ScopedValue.where(key, newValue);
} else {
carrier = carrier.where(key, newValue);
}
newArray[index] = new KeyAndValue<>(key, newValue);
n--;
}
// invoke recursively
carrier.run(() -> {
test(newArray, depth+1);
});
// check that the scoped values have the original values
check(array);
}
/**
* Check that the given scoped values have the expected value.
*/
private void check(KeyAndValue<Integer>[] array) {
for (int i = 0; i < array.length; i++) {
ScopedValue<Integer> key = array[i].key;
Integer value = array[i].value;
if (value == null) {
assertFalse(key.isBound());
} else {
assertEquals(value, key.get());
}
}
}
/**
* Do lots of reads of the scoped values, to pollute the SV cache.
*/
private void lotsOfReads(KeyAndValue<Integer>[] array) {
for (int k = 0; k < 1000; k++) {
int index = RND.nextInt(array.length);
Integer value = array[index].value;
if (value != null) {
ScopedValue<Integer> key = array[index].key;
assertEquals(value, key.get());
}
}
}
}
|