1 /* 2 * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package runtime.valhalla.inlinetypes; 25 26 import java.lang.reflect.Array; 27 import java.lang.reflect.Field; 28 import java.util.Arrays; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.function.Supplier; 32 33 import javax.swing.LayoutFocusTraversalPolicy; 34 35 import java.util.Optional; 36 37 import jdk.internal.misc.Unsafe; 38 import jdk.internal.value.ValueClass; 39 import jdk.internal.vm.annotation.ImplicitlyConstructible; 40 import jdk.internal.vm.annotation.LooselyConsistentValue; 41 import jdk.internal.vm.annotation.NullRestricted; 42 import jdk.test.whitebox.WhiteBox; 43 import static jdk.test.lib.Asserts.*; 44 45 /* 46 * @test ValueTearing 47 * @summary Test tearing of inline fields and array elements 48 * @modules java.base/jdk.internal.misc 49 * @library /test/lib 50 * @modules java.base/jdk.internal.value 51 * java.base/jdk.internal.vm.annotation 52 * @enablePreview 53 * @compile ValueTearing.java 54 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 55 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable= 56 * -DSTEP_COUNT=10000 -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1 57 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 58 * runtime.valhalla.inlinetypes.ValueTearing 59 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=* 60 * -DSTEP_COUNT=10000 -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1 61 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 62 * runtime.valhalla.inlinetypes.ValueTearing 63 * @run main/othervm -DSTEP_COUNT=10000000 -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1 64 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 65 * runtime.valhalla.inlinetypes.ValueTearing 66 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable= 67 * -DTEAR_MODE=fieldonly -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1 68 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 69 * runtime.valhalla.inlinetypes.ValueTearing 70 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable= 71 * -DTEAR_MODE=arrayonly -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1 72 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 73 * runtime.valhalla.inlinetypes.ValueTearing 74 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=* 75 * -DTEAR_MODE=both -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1 76 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 77 * runtime.valhalla.inlinetypes.ValueTearing 78 */ 79 public class ValueTearing { 80 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); 81 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 82 private static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); 83 private static final boolean ALWAYS_ATOMIC = WHITE_BOX.getStringVMFlag("ForceNonTearable").contains("*"); 84 private static final String TEAR_MODE = System.getProperty("TEAR_MODE", "both"); 85 private static final boolean TEAR_FIELD = !TEAR_MODE.equals("arrayonly"); 86 private static final boolean TEAR_ARRAY = !TEAR_MODE.equals("fieldonly"); 87 private static final int STEP_COUNT = Integer.getInteger("STEP_COUNT", 100_000); 88 private static final boolean TFIELD_FLAT, TARRAY_FLAT; 89 private static final boolean NTFIELD_FLAT, NTARRAY_FLAT; 90 static { 91 try { 92 Field TPB_field = TPointBox.class.getDeclaredField("field"); 93 Field TPB_array = TPointBox.class.getDeclaredField("array"); 94 Field NTPB_field = NTPointBox.class.getDeclaredField("field"); 95 Field NTPB_array = NTPointBox.class.getDeclaredField("array"); 96 TFIELD_FLAT = UNSAFE.isFlatField(TPB_field); 97 TARRAY_FLAT = UNSAFE.isFlatArray(TPB_array.getType()); 98 NTFIELD_FLAT = UNSAFE.isFlatField(NTPB_field); 99 NTARRAY_FLAT = UNSAFE.isFlatArray(NTPB_array.getType()); 100 } catch (ReflectiveOperationException ex) { 101 throw new AssertionError(ex); 102 } 103 } 104 private static final String SETTINGS = 105 String.format("USE_COMPILER=%s ALWAYS_ATOMIC=%s TEAR_MODE=%s STEP_COUNT=%s FLAT TF/TA=%s/%s NTF/NTA=%s/%s", 106 USE_COMPILER, ALWAYS_ATOMIC, TEAR_MODE, STEP_COUNT, 107 TFIELD_FLAT, TARRAY_FLAT, NTFIELD_FLAT, NTARRAY_FLAT); 108 private static final String NOTE_TORN_POINT = "Note: torn point"; 109 110 public static void main(String[] args) throws Exception { 111 System.out.println(SETTINGS); 112 ValueTearing valueTearing = new ValueTearing(); 113 valueTearing.run(); 114 // Extra representation check: 115 assert(!NTFIELD_FLAT) : "NT field must be indirect not flat"; 116 assert(!NTARRAY_FLAT) : "NT array must be indirect not flat"; 117 if (ALWAYS_ATOMIC) { 118 assert(!TFIELD_FLAT) : "field must be indirect not flat"; 119 assert(!TARRAY_FLAT) : "array must be indirect not flat"; 120 } 121 } 122 123 // A tearable value. 124 @LooselyConsistentValue 125 @ImplicitlyConstructible 126 static value class TPoint { 127 TPoint(long x, long y) { this.x = x; this.y = y; } 128 final long x, y; 129 public String toString() { return String.format("(%d,%d)", x, y); } 130 } 131 132 static class TooTearable extends AssertionError { 133 final Object badPoint; 134 TooTearable(String msg, Object badPoint) { 135 super(msg); 136 this.badPoint = badPoint; 137 } 138 } 139 140 interface PointBox { 141 void step(); // mutate inline value state 142 void check(); // check sanity of inline value state 143 } 144 145 class TPointBox implements PointBox { 146 @NullRestricted 147 TPoint field; 148 TPoint[] array = (TPoint[])ValueClass.newNullRestrictedArray(TPoint.class, 1); 149 // Step the points forward by incrementing their components 150 // "simultaneously". A racing thread will catch flaws in the 151 // simultaneity. 152 TPoint step(TPoint p) { 153 return new TPoint(p.x + 1, p.y + 1); 154 } 155 public @Override 156 void step() { 157 if (TEAR_FIELD) { 158 field = step(field); 159 } 160 if (TEAR_ARRAY) { 161 array[0] = step(array[0]); 162 } 163 check(); 164 } 165 // Invariant: The components of each point are "always" equal. 166 // As long as simultaneity is preserved, this is true. 167 public @Override 168 void check() { 169 if (TEAR_FIELD) { 170 check(field, "field"); 171 } 172 if (TEAR_ARRAY) { 173 check(array[0], "array element"); 174 } 175 } 176 void check(TPoint p, String where) { 177 if (p.x == p.y) return; 178 String msg = String.format("%s %s in %s; settings = %s", 179 NOTE_TORN_POINT, 180 p, where, SETTINGS); 181 throw new TooTearable(msg, p); 182 } 183 public String toString() { 184 return String.format("TPB[%s, {%s}]", field, array[0]); 185 } 186 } 187 188 189 // A non-tearable version of TPoint. 190 @ImplicitlyConstructible 191 static value class NTPoint { 192 NTPoint(long x, long y) { this.x = x; this.y = y; } 193 final long x, y; 194 public String toString() { return String.format("(%d,%d)", x, y); } 195 } 196 197 class NTPointBox implements PointBox { 198 @NullRestricted 199 NTPoint field; 200 NTPoint[] array = (NTPoint[])ValueClass.newNullRestrictedArray(NTPoint.class, 1); 201 // Step the points forward by incrementing their components 202 // "simultaneously". A racing thread will catch flaws in the 203 // simultaneity. 204 NTPoint step(NTPoint p) { 205 return new NTPoint(p.x + 1, p.y + 1); 206 } 207 public @Override 208 void step() { 209 field = step(field); 210 array[0] = step(array[0]); 211 check(); 212 } 213 // Invariant: The components of each point are "always" equal. 214 public @Override 215 void check() { 216 check(field, "field"); 217 check(array[0], "array element"); 218 } 219 void check(NTPoint p, String where) { 220 if (p.x == p.y) return; 221 String msg = String.format("%s *NonTearable* %s in %s; settings = %s", 222 NOTE_TORN_POINT, 223 p, where, SETTINGS); 224 throw new TooTearable(msg, p); 225 } 226 public String toString() { 227 return String.format("NTPB[%s, {%s}]", field, array[0]); 228 } 229 } 230 231 class AsyncObserver extends Thread { 232 volatile boolean done; 233 long observationCount; 234 final PointBox pointBox; 235 volatile Object badPointObserved; 236 AsyncObserver(PointBox pointBox) { 237 this.pointBox = pointBox; 238 } 239 public void run() { 240 try { 241 while (!done) { 242 observationCount++; 243 pointBox.check(); 244 } 245 } catch (TooTearable ex) { 246 done = true; 247 badPointObserved = ex.badPoint; 248 System.out.println(ex); 249 if (ALWAYS_ATOMIC || !badPointObserved.getClass().isAnnotationPresent(LooselyConsistentValue.class)) { 250 throw ex; 251 } 252 } 253 } 254 } 255 256 public void run() throws Exception { 257 System.out.println("Test for tearing of NTPoint, which must not happen..."); 258 run(new NTPointBox(), false); 259 System.out.println("Test for tearing of TPoint, which "+ 260 (ALWAYS_ATOMIC ? "must not" : "is allowed to")+ 261 " happen..."); 262 run(new TPointBox(), ALWAYS_ATOMIC ? false : true); 263 } 264 public void run(PointBox pointBox, boolean canTear) throws Exception { 265 var observer = new AsyncObserver(pointBox); 266 observer.start(); 267 for (int i = 0; i < STEP_COUNT; i++) { 268 pointBox.step(); 269 if (observer.done) break; 270 } 271 observer.done = true; 272 observer.join(); 273 var obCount = observer.observationCount; 274 var badPoint = observer.badPointObserved; 275 System.out.println(String.format("finished after %d observations at %s; %s", 276 obCount, pointBox, 277 (badPoint == null 278 ? "no tearing observed" 279 : "bad point = " + badPoint))); 280 if (canTear && badPoint == null) { 281 var complain = String.format("%s NOT observed after %d observations", 282 NOTE_TORN_POINT, obCount); 283 System.out.println("?????? "+complain); 284 if (STEP_COUNT >= 3_000_000) { 285 // If it's a small count, OK, but if it's big the test is broken. 286 throw new AssertionError(complain + ", but it should have been"); 287 } 288 } 289 if (!canTear && badPoint != null) { 290 throw new AssertionError("should not reach here; other thread must throw"); 291 } 292 } 293 }