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 }