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.LooselyConsistentValue; 40 import jdk.internal.vm.annotation.NullRestricted; 41 import jdk.internal.vm.annotation.Strict; 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 * @requires vm.flagless 51 * @modules java.base/jdk.internal.value 52 * java.base/jdk.internal.vm.annotation 53 * @enablePreview 54 * @compile ValueTearing.java 55 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 56 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable= 57 * -DSTEP_COUNT=10000 -XX:+UseFieldFlattening -XX:+UseArrayFlattening 58 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 59 * runtime.valhalla.inlinetypes.ValueTearing 60 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=* 61 * -DSTEP_COUNT=10000 -XX:+UseFieldFlattening -XX:+UseArrayFlattening 62 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 63 * runtime.valhalla.inlinetypes.ValueTearing 64 * @run main/othervm -DSTEP_COUNT=10000000 -XX:+UseFieldFlattening -XX:+UseArrayFlattening 65 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 66 * runtime.valhalla.inlinetypes.ValueTearing 67 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable= 68 * -DTEAR_MODE=fieldonly -XX:+UseFieldFlattening -XX:+UseArrayFlattening 69 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 70 * runtime.valhalla.inlinetypes.ValueTearing 71 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable= 72 * -DTEAR_MODE=arrayonly -XX:+UseFieldFlattening -XX:+UseArrayFlattening 73 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 74 * runtime.valhalla.inlinetypes.ValueTearing 75 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=* 76 * -DTEAR_MODE=both -XX:+UseFieldFlattening -XX:+UseArrayFlattening 77 * -Xbootclasspath/a:. -XX:+WhiteBoxAPI 78 * runtime.valhalla.inlinetypes.ValueTearing 79 */ 80 public class ValueTearing { 81 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); 82 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 83 private static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); 84 private static final boolean ALWAYS_ATOMIC = WHITE_BOX.getStringVMFlag("ForceNonTearable").contains("*"); 85 private static final String TEAR_MODE = System.getProperty("TEAR_MODE", "both"); 86 private static final boolean TEAR_FIELD = !TEAR_MODE.equals("arrayonly"); 87 private static final boolean TEAR_ARRAY = !TEAR_MODE.equals("fieldonly"); 88 private static final int STEP_COUNT = Integer.getInteger("STEP_COUNT", 100_000); 89 private static final boolean TFIELD_FLAT, TARRAY_FLAT; 90 private static final boolean NTFIELD_FLAT, NTARRAY_FLAT; 91 static { 92 try { 93 Field TPB_field = TPointBox.class.getDeclaredField("field"); 94 Field TPB_array = TPointBox.class.getDeclaredField("array"); 95 Field NTPB_field = NTPointBox.class.getDeclaredField("field"); 96 Field NTPB_array = NTPointBox.class.getDeclaredField("array"); 97 TFIELD_FLAT = UNSAFE.isFlatField(TPB_field); 98 TARRAY_FLAT = UNSAFE.isFlatArray(TPB_array.getType()); 99 NTFIELD_FLAT = UNSAFE.isFlatField(NTPB_field); 100 NTARRAY_FLAT = UNSAFE.isFlatArray(NTPB_array.getType()); 101 } catch (ReflectiveOperationException ex) { 102 throw new AssertionError(ex); 103 } 104 } 105 private static final String SETTINGS = 106 String.format("USE_COMPILER=%s ALWAYS_ATOMIC=%s TEAR_MODE=%s STEP_COUNT=%s FLAT TF/TA=%s/%s NTF/NTA=%s/%s", 107 USE_COMPILER, ALWAYS_ATOMIC, TEAR_MODE, STEP_COUNT, 108 TFIELD_FLAT, TARRAY_FLAT, NTFIELD_FLAT, NTARRAY_FLAT); 109 private static final String NOTE_TORN_POINT = "Note: torn point"; 110 111 public static void main(String[] args) throws Exception { 112 System.out.println(SETTINGS); 113 ValueTearing valueTearing = new ValueTearing(); 114 valueTearing.run(); 115 // Extra representation check: 116 assert(!NTFIELD_FLAT) : "NT field must be indirect not flat"; 117 assert(!NTARRAY_FLAT) : "NT array must be indirect not flat"; 118 if (ALWAYS_ATOMIC) { 119 assert(!TFIELD_FLAT) : "field must be indirect not flat"; 120 assert(!TARRAY_FLAT) : "array must be indirect not flat"; 121 } 122 } 123 124 // A tearable value. 125 @LooselyConsistentValue 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 @Strict 147 @NullRestricted 148 TPoint field = new TPoint(0, 0); 149 TPoint[] array = (TPoint[])ValueClass.newNullRestrictedNonAtomicArray(TPoint.class, 1, new TPoint(0, 0)); 150 // Step the points forward by incrementing their components 151 // "simultaneously". A racing thread will catch flaws in the 152 // simultaneity. 153 TPoint step(TPoint p) { 154 return new TPoint(p.x + 1, p.y + 1); 155 } 156 public @Override 157 void step() { 158 if (TEAR_FIELD) { 159 field = step(field); 160 } 161 if (TEAR_ARRAY) { 162 array[0] = step(array[0]); 163 } 164 check(); 165 } 166 // Invariant: The components of each point are "always" equal. 167 // As long as simultaneity is preserved, this is true. 168 public @Override 169 void check() { 170 if (TEAR_FIELD) { 171 check(field, "field"); 172 } 173 if (TEAR_ARRAY) { 174 check(array[0], "array element"); 175 } 176 } 177 void check(TPoint p, String where) { 178 if (p.x == p.y) return; 179 String msg = String.format("%s %s in %s; settings = %s", 180 NOTE_TORN_POINT, 181 p, where, SETTINGS); 182 throw new TooTearable(msg, p); 183 } 184 public String toString() { 185 return String.format("TPB[%s, {%s}]", field, array[0]); 186 } 187 } 188 189 190 // A non-tearable version of TPoint. 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 @Strict 199 @NullRestricted 200 NTPoint field = new NTPoint(0, 0); 201 NTPoint[] array = (NTPoint[])ValueClass.newNullRestrictedNonAtomicArray(NTPoint.class, 1, new NTPoint(0, 0)); 202 // Step the points forward by incrementing their components 203 // "simultaneously". A racing thread will catch flaws in the 204 // simultaneity. 205 NTPoint step(NTPoint p) { 206 return new NTPoint(p.x + 1, p.y + 1); 207 } 208 public @Override 209 void step() { 210 field = step(field); 211 array[0] = step(array[0]); 212 check(); 213 } 214 // Invariant: The components of each point are "always" equal. 215 public @Override 216 void check() { 217 check(field, "field"); 218 check(array[0], "array element"); 219 } 220 void check(NTPoint p, String where) { 221 if (p.x == p.y) return; 222 String msg = String.format("%s *NonTearable* %s in %s; settings = %s", 223 NOTE_TORN_POINT, 224 p, where, SETTINGS); 225 throw new TooTearable(msg, p); 226 } 227 public String toString() { 228 return String.format("NTPB[%s, {%s}]", field, array[0]); 229 } 230 } 231 232 class AsyncObserver extends Thread { 233 volatile boolean done; 234 long observationCount; 235 final PointBox pointBox; 236 volatile Object badPointObserved; 237 AsyncObserver(PointBox pointBox) { 238 this.pointBox = pointBox; 239 } 240 public void run() { 241 try { 242 while (!done) { 243 observationCount++; 244 pointBox.check(); 245 } 246 } catch (TooTearable ex) { 247 done = true; 248 badPointObserved = ex.badPoint; 249 System.out.println(ex); 250 if (ALWAYS_ATOMIC || !badPointObserved.getClass().isAnnotationPresent(LooselyConsistentValue.class)) { 251 throw ex; 252 } 253 } 254 } 255 } 256 257 public void run() throws Exception { 258 System.out.println("Test for tearing of NTPoint, which must not happen..."); 259 run(new NTPointBox(), false); 260 System.out.println("Test for tearing of TPoint, which "+ 261 (ALWAYS_ATOMIC ? "must not" : "is allowed to")+ 262 " happen..."); 263 run(new TPointBox(), ALWAYS_ATOMIC ? false : true); 264 } 265 public void run(PointBox pointBox, boolean canTear) throws Exception { 266 var observer = new AsyncObserver(pointBox); 267 observer.start(); 268 for (int i = 0; i < STEP_COUNT; i++) { 269 pointBox.step(); 270 if (observer.done) break; 271 } 272 observer.done = true; 273 observer.join(); 274 var obCount = observer.observationCount; 275 var badPoint = observer.badPointObserved; 276 System.out.println(String.format("finished after %d observations at %s; %s", 277 obCount, pointBox, 278 (badPoint == null 279 ? "no tearing observed" 280 : "bad point = " + badPoint))); 281 if (canTear && badPoint == null) { 282 var complain = String.format("%s NOT observed after %d observations", 283 NOTE_TORN_POINT, obCount); 284 System.out.println("?????? "+complain); 285 if (STEP_COUNT >= 3_000_000) { 286 // If it's a small count, OK, but if it's big the test is broken. 287 throw new AssertionError(complain + ", but it should have been"); 288 } 289 } 290 if (!canTear && badPoint != null) { 291 throw new AssertionError("should not reach here; other thread must throw"); 292 } 293 } 294 }