1 /*
  2  * Copyright (c) 2020, 2023, 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 gc.shenandoah;
 25 
 26 /* @test id=satb
 27  * @requires vm.gc.Shenandoah
 28  * @library /test/lib
 29  * @build jdk.test.whitebox.WhiteBox
 30  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
 31  * @run main/othervm
 32  *      -Xbootclasspath/a:.
 33  *      -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 34  *      -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=satb
 35  *      gc.shenandoah.TestReferenceRefersToShenandoah
 36  */
 37 
 38 /* @test id=satb-100
 39  * @requires vm.gc.Shenandoah
 40  * @library /test/lib
 41  * @build jdk.test.whitebox.WhiteBox
 42  * @modules java.base
 43  * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
 44  * @run main/othervm
 45  *      -Xbootclasspath/a:.
 46  *      -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 47  *      -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=satb -XX:ShenandoahGarbageThreshold=100 -Xmx100m
 48  *      gc.shenandoah.TestReferenceRefersToShenandoah
 49  */
 50 
 51 /* @test id=generational
 52  * @requires vm.gc.Shenandoah
 53  * @library /test/lib
 54  * @build jdk.test.whitebox.WhiteBox
 55  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
 56  * @run main/othervm
 57  *      -Xbootclasspath/a:.
 58  *      -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 59  *      -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational
 60  *      gc.shenandoah.TestReferenceRefersToShenandoah
 61  */
 62 
 63 /* @test id=generational-100
 64  * @requires vm.gc.Shenandoah
 65  * @library /test/lib
 66  * @build jdk.test.whitebox.WhiteBox
 67  * @modules java.base
 68  * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
 69  * @run main/othervm
 70  *      -Xbootclasspath/a:.
 71  *      -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 72  *      -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ShenandoahGarbageThreshold=100 -Xmx100m
 73  *      gc.shenandoah.TestReferenceRefersToShenandoah
 74  */
 75 
 76 /* @test id=iu
 77  * @requires vm.gc.Shenandoah
 78  * @library /test/lib
 79  * @build jdk.test.whitebox.WhiteBox
 80  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
 81  * @run main/othervm
 82  *      -Xbootclasspath/a:.
 83  *      -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 84  *      -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu
 85  *      gc.shenandoah.TestReferenceRefersToShenandoah
 86  */
 87 
 88 /* @test id=iu-100
 89  * @requires vm.gc.Shenandoah
 90  * @library /test/lib
 91  * @build jdk.test.whitebox.WhiteBox
 92  * @modules java.base
 93  * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
 94  * @run main/othervm
 95  *      -Xbootclasspath/a:.
 96  *      -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 97  *      -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGarbageThreshold=100 -Xmx100m
 98  *      gc.shenandoah.TestReferenceRefersToShenandoah
 99  */
100 
101 import java.lang.ref.PhantomReference;
102 import java.lang.ref.Reference;
103 import java.lang.ref.ReferenceQueue;
104 import java.lang.ref.WeakReference;
105 import jdk.test.whitebox.WhiteBox;
106 
107 public class TestReferenceRefersToShenandoah {
108     private static final WhiteBox WB = WhiteBox.getWhiteBox();
109 
110     private static final class TestObject {
111         public final int value;
112 
113         public TestObject(int value) {
114             this.value = value;
115         }
116     }
117 
118     private static volatile TestObject testObjectNone = null;
119     private static volatile TestObject testObject1 = null;
120     private static volatile TestObject testObject2 = null;
121     private static volatile TestObject testObject3 = null;
122     private static volatile TestObject testObject4 = null;
123 
124     private static ReferenceQueue<TestObject> queue = null;
125 
126     private static PhantomReference<TestObject> testPhantom1 = null;
127 
128     private static WeakReference<TestObject> testWeak2 = null;
129     private static WeakReference<TestObject> testWeak3 = null;
130     private static WeakReference<TestObject> testWeak4 = null;
131 
132     private static void setup() {
133         testObjectNone = new TestObject(0);
134         testObject1 = new TestObject(1);
135         testObject2 = new TestObject(2);
136         testObject3 = new TestObject(3);
137         testObject4 = new TestObject(4);
138 
139         queue = new ReferenceQueue<TestObject>();
140 
141         testPhantom1 = new PhantomReference<TestObject>(testObject1, queue);
142 
143         testWeak2 = new WeakReference<TestObject>(testObject2, queue);
144         testWeak3 = new WeakReference<TestObject>(testObject3, queue);
145         testWeak4 = new WeakReference<TestObject>(testObject4, queue);
146     }
147 
148     private static void gcUntilOld(Object o) throws Exception {
149         if (!WB.isObjectInOldGen(o)) {
150             WB.fullGC();
151             if (!WB.isObjectInOldGen(o)) {
152                 fail("object not promoted by full gc");
153             }
154         }
155     }
156 
157     private static void gcUntilOld() throws Exception {
158         gcUntilOld(testObjectNone);
159         gcUntilOld(testObject1);
160         gcUntilOld(testObject2);
161         gcUntilOld(testObject3);
162         gcUntilOld(testObject4);
163 
164         gcUntilOld(testPhantom1);
165 
166         gcUntilOld(testWeak2);
167         gcUntilOld(testWeak3);
168         gcUntilOld(testWeak4);
169     }
170 
171     private static void progress(String msg) {
172         System.out.println(msg);
173     }
174 
175     private static void fail(String msg) throws Exception {
176         throw new RuntimeException(msg);
177     }
178 
179     private static void expectCleared(Reference<TestObject> ref,
180                                       String which) throws Exception {
181         expectNotValue(ref, testObjectNone, which);
182         if (!ref.refersTo(null)) {
183             fail("expected " + which + " to be cleared");
184         }
185     }
186 
187     private static void expectNotCleared(Reference<TestObject> ref,
188                                          String which) throws Exception {
189         expectNotValue(ref, testObjectNone, which);
190         if (ref.refersTo(null)) {
191             fail("expected " + which + " to not be cleared");
192         }
193     }
194 
195     private static void expectValue(Reference<TestObject> ref,
196                                     TestObject value,
197                                     String which) throws Exception {
198         expectNotValue(ref, testObjectNone, which);
199         expectNotCleared(ref, which);
200         if (!ref.refersTo(value)) {
201             fail(which + " doesn't refer to expected value");
202         }
203     }
204 
205     private static void expectNotValue(Reference<TestObject> ref,
206                                        TestObject value,
207                                        String which) throws Exception {
208         if (ref.refersTo(value)) {
209             fail(which + " refers to unexpected value");
210         }
211     }
212 
213     private static void checkInitialStates() throws Exception {
214         expectValue(testPhantom1, testObject1, "testPhantom1");
215         expectValue(testWeak2, testObject2, "testWeak2");
216         expectValue(testWeak3, testObject3, "testWeak3");
217         expectValue(testWeak4, testObject4, "testWeak4");
218     }
219 
220     private static void discardStrongReferences() {
221         // testObjectNone not dropped
222         testObject1 = null;
223         testObject2 = null;
224         // testObject3 not dropped
225         testObject4 = null;
226     }
227 
228     private static boolean isShenandoahIUMode() {
229         return "iu".equals(WB.getStringVMFlag("ShenandoahGCMode"));
230     }
231 
232     private static void testConcurrentCollection() throws Exception {
233         progress("setup concurrent collection test");
234         setup();
235         progress("gcUntilOld");
236         gcUntilOld();
237 
238         progress("acquire control of concurrent cycles");
239         WB.concurrentGCAcquireControl();
240         try {
241             progress("check initial states");
242             checkInitialStates();
243 
244             progress("discard strong references");
245             discardStrongReferences();
246 
247             progress("run GC to before marking completed");
248             WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED);
249 
250             progress("fetch test objects, possibly keeping some alive");
251             expectNotCleared(testPhantom1, "testPhantom1");
252             expectNotCleared(testWeak2, "testWeak2");
253             expectValue(testWeak3, testObject3, "testWeak3");
254 
255             // For some collectors, calling get() will keep testObject4 alive.
256             if (testWeak4.get() == null) {
257                 fail("testWeak4 unexpectedly == null");
258             }
259 
260             progress("finish collection");
261             WB.concurrentGCRunToIdle();
262 
263             progress("verify expected clears");
264             expectCleared(testPhantom1, "testPhantom1");
265             expectCleared(testWeak2, "testWeak2");
266             expectValue(testWeak3, testObject3, "testWeak3");
267             // This is true for all currently supported concurrent collectors,
268             // except Shenandoah+IU, which allows clearing refs even when
269             // accessed during concurrent marking.
270             if (isShenandoahIUMode()) {
271               expectCleared(testWeak4, "testWeak4");
272             } else {
273               expectNotCleared(testWeak4, "testWeak4");
274             }
275 
276             progress("verify get returns expected values");
277             if (testWeak2.get() != null) {
278                 fail("testWeak2.get() != null");
279             }
280 
281             TestObject obj3 = testWeak3.get();
282             if (obj3 == null) {
283                 fail("testWeak3.get() returned null");
284             } else if (obj3.value != 3) {
285                 fail("testWeak3.get().value is " + obj3.value);
286             }
287 
288             TestObject obj4 = testWeak4.get();
289             if (!isShenandoahIUMode()) {
290                 if (obj4 == null) {
291                     fail("testWeak4.get() returned null");
292                 } else if (obj4.value != 4) {
293                     fail("testWeak4.get().value is " + obj4.value);
294                 }
295             }
296 
297             progress("verify queue entries");
298             long timeout = 60000; // 1 minute of milliseconds.
299             while (true) {
300                 Reference<? extends TestObject> ref = queue.remove(timeout);
301                 if (ref == null) {
302                     break;
303                 } else if (ref == testPhantom1) {
304                     testPhantom1 = null;
305                 } else if (ref == testWeak2) {
306                     testWeak2 = null;
307                 } else if (ref == testWeak3) {
308                     testWeak3 = null;
309                 } else if (ref == testWeak4) {
310                     testWeak4 = null;
311                 } else {
312                     fail("unexpected reference in queue");
313                 }
314             }
315             if (testPhantom1 != null) {
316                 fail("testPhantom1 not notified");
317             } else if (testWeak2 != null) {
318                 fail("testWeak2 not notified");
319             } else if (testWeak3 == null) {
320                 fail("testWeak3 notified");
321             } else if (testWeak4 == null) {
322                 if (obj4 != null) {
323                     fail("testWeak4 notified");
324                 }
325             }
326 
327         } finally {
328             progress("release control of concurrent cycles");
329             WB.concurrentGCReleaseControl();
330         }
331         progress("finished concurrent collection test");
332     }
333 
334     private static void testSimpleCollection() throws Exception {
335         progress("setup simple collection test");
336         setup();
337         progress("gcUntilOld");
338         gcUntilOld();
339 
340         progress("check initial states");
341         checkInitialStates();
342 
343         progress("discard strong references");
344         TestObject tw4 = testWeak4.get(); // Keep testObject4 alive.
345         discardStrongReferences();
346 
347         progress("collect garbage");
348         WB.fullGC();
349 
350         progress("verify expected clears");
351         expectCleared(testPhantom1, "testPhantom1");
352         expectCleared(testWeak2, "testWeak2");
353         expectValue(testWeak3, testObject3, "testWeak3");
354         expectNotCleared(testWeak4, "testWeak4");
355 
356         progress("verify get returns expected values");
357         if (testWeak2.get() != null) {
358             fail("testWeak2.get() != null");
359         } else if (testWeak3.get() != testObject3) {
360             fail("testWeak3.get() is not expected value");
361         } else if (testWeak4.get() != tw4) {
362             fail("testWeak4.get() is not expected value");
363         }
364 
365         progress("finished simple collection test");
366     }
367 
368     public static void main(String[] args) throws Exception {
369         if (WB.supportsConcurrentGCBreakpoints()) {
370             testConcurrentCollection();
371         }
372         testSimpleCollection();
373     }
374 }