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                 // This is just a warning, because failing would
153                 // be overspecifying for generational shenandoah,
154                 // which need not necessarily promote objects upon
155                 // a full GC.
156                 warn("object not promoted by full gc");
157             }
158         }
159     }
160 
161     private static void gcUntilOld() throws Exception {
162         gcUntilOld(testObjectNone);
163         gcUntilOld(testObject1);
164         gcUntilOld(testObject2);
165         gcUntilOld(testObject3);
166         gcUntilOld(testObject4);
167 
168         gcUntilOld(testPhantom1);
169 
170         gcUntilOld(testWeak2);
171         gcUntilOld(testWeak3);
172         gcUntilOld(testWeak4);
173     }
174 
175     private static void progress(String msg) {
176         System.out.println(msg);
177     }
178 
179     private static void fail(String msg) throws Exception {
180         throw new RuntimeException(msg);
181     }
182 
183     private static void warn(String msg) {
184         System.out.println("Warning: " + msg);
185     }
186 
187     private static void expectCleared(Reference<TestObject> ref,
188                                       String which) throws Exception {
189         expectNotValue(ref, testObjectNone, which);
190         if (!ref.refersTo(null)) {
191             fail("expected " + which + " to be cleared");
192         }
193     }
194 
195     private static void expectNotCleared(Reference<TestObject> ref,
196                                          String which) throws Exception {
197         expectNotValue(ref, testObjectNone, which);
198         if (ref.refersTo(null)) {
199             fail("expected " + which + " to not be cleared");
200         }
201     }
202 
203     private static void expectValue(Reference<TestObject> ref,
204                                     TestObject value,
205                                     String which) throws Exception {
206         expectNotValue(ref, testObjectNone, which);
207         expectNotCleared(ref, which);
208         if (!ref.refersTo(value)) {
209             fail(which + " doesn't refer to expected value");
210         }
211     }
212 
213     private static void expectNotValue(Reference<TestObject> ref,
214                                        TestObject value,
215                                        String which) throws Exception {
216         if (ref.refersTo(value)) {
217             fail(which + " refers to unexpected value");
218         }
219     }
220 
221     private static void checkInitialStates() throws Exception {
222         expectValue(testPhantom1, testObject1, "testPhantom1");
223         expectValue(testWeak2, testObject2, "testWeak2");
224         expectValue(testWeak3, testObject3, "testWeak3");
225         expectValue(testWeak4, testObject4, "testWeak4");
226     }
227 
228     private static void discardStrongReferences() {
229         // testObjectNone not dropped
230         testObject1 = null;
231         testObject2 = null;
232         // testObject3 not dropped
233         testObject4 = null;
234     }
235 
236     private static boolean isShenandoahIUMode() {
237         return "iu".equals(WB.getStringVMFlag("ShenandoahGCMode"));
238     }
239 
240     private static void testConcurrentCollection() throws Exception {
241         progress("setup concurrent collection test");
242         setup();
243         progress("gcUntilOld");
244         gcUntilOld();
245 
246         progress("acquire control of concurrent cycles");
247         WB.concurrentGCAcquireControl();
248         try {
249             progress("check initial states");
250             checkInitialStates();
251 
252             progress("discard strong references");
253             discardStrongReferences();
254 
255             progress("run GC to before marking completed");
256             WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED);
257 
258             progress("fetch test objects, possibly keeping some alive");
259             expectNotCleared(testPhantom1, "testPhantom1");
260             expectNotCleared(testWeak2, "testWeak2");
261             expectValue(testWeak3, testObject3, "testWeak3");
262 
263             // For some collectors, calling get() will keep testObject4 alive.
264             if (testWeak4.get() == null) {
265                 fail("testWeak4 unexpectedly == null");
266             }
267 
268             progress("finish collection");
269             WB.concurrentGCRunToIdle();
270 
271             progress("verify expected clears");
272             expectCleared(testPhantom1, "testPhantom1");
273             expectCleared(testWeak2, "testWeak2");
274             expectValue(testWeak3, testObject3, "testWeak3");
275             // This is true for all currently supported concurrent collectors,
276             // except Shenandoah+IU, which allows clearing refs even when
277             // accessed during concurrent marking.
278             if (isShenandoahIUMode()) {
279               expectCleared(testWeak4, "testWeak4");
280             } else {
281               expectNotCleared(testWeak4, "testWeak4");
282             }
283 
284             progress("verify get returns expected values");
285             if (testWeak2.get() != null) {
286                 fail("testWeak2.get() != null");
287             }
288 
289             TestObject obj3 = testWeak3.get();
290             if (obj3 == null) {
291                 fail("testWeak3.get() returned null");
292             } else if (obj3.value != 3) {
293                 fail("testWeak3.get().value is " + obj3.value);
294             }
295 
296             TestObject obj4 = testWeak4.get();
297             if (!isShenandoahIUMode()) {
298                 if (obj4 == null) {
299                     fail("testWeak4.get() returned null");
300                 } else if (obj4.value != 4) {
301                     fail("testWeak4.get().value is " + obj4.value);
302                 }
303             }
304 
305             progress("verify queue entries");
306             long timeout = 60000; // 1 minute of milliseconds.
307             while (true) {
308                 Reference<? extends TestObject> ref = queue.remove(timeout);
309                 if (ref == null) {
310                     break;
311                 } else if (ref == testPhantom1) {
312                     testPhantom1 = null;
313                 } else if (ref == testWeak2) {
314                     testWeak2 = null;
315                 } else if (ref == testWeak3) {
316                     testWeak3 = null;
317                 } else if (ref == testWeak4) {
318                     testWeak4 = null;
319                 } else {
320                     fail("unexpected reference in queue");
321                 }
322             }
323             if (testPhantom1 != null) {
324                 fail("testPhantom1 not notified");
325             } else if (testWeak2 != null) {
326                 fail("testWeak2 not notified");
327             } else if (testWeak3 == null) {
328                 fail("testWeak3 notified");
329             } else if (testWeak4 == null) {
330                 if (obj4 != null) {
331                     fail("testWeak4 notified");
332                 }
333             }
334 
335         } finally {
336             progress("release control of concurrent cycles");
337             WB.concurrentGCReleaseControl();
338         }
339         progress("finished concurrent collection test");
340     }
341 
342     private static void testSimpleCollection() throws Exception {
343         progress("setup simple collection test");
344         setup();
345         progress("gcUntilOld");
346         gcUntilOld();
347 
348         progress("check initial states");
349         checkInitialStates();
350 
351         progress("discard strong references");
352         TestObject tw4 = testWeak4.get(); // Keep testObject4 alive.
353         discardStrongReferences();
354 
355         progress("collect garbage");
356         WB.fullGC();
357 
358         progress("verify expected clears");
359         expectCleared(testPhantom1, "testPhantom1");
360         expectCleared(testWeak2, "testWeak2");
361         expectValue(testWeak3, testObject3, "testWeak3");
362         expectNotCleared(testWeak4, "testWeak4");
363 
364         progress("verify get returns expected values");
365         if (testWeak2.get() != null) {
366             fail("testWeak2.get() != null");
367         } else if (testWeak3.get() != testObject3) {
368             fail("testWeak3.get() is not expected value");
369         } else if (testWeak4.get() != tw4) {
370             fail("testWeak4.get() is not expected value");
371         }
372 
373         progress("finished simple collection test");
374     }
375 
376     public static void main(String[] args) throws Exception {
377         if (WB.supportsConcurrentGCBreakpoints()) {
378             testConcurrentCollection();
379         }
380         testSimpleCollection();
381     }
382 }