1 /*
  2  * Copyright (c) 2020, 2022, 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 import java.lang.ref.PhantomReference;
 52 import java.lang.ref.Reference;
 53 import java.lang.ref.ReferenceQueue;
 54 import java.lang.ref.WeakReference;
 55 import jdk.test.whitebox.WhiteBox;
 56 
 57 public class TestReferenceRefersToShenandoah {
 58     private static final WhiteBox WB = WhiteBox.getWhiteBox();
 59 
 60     private static final class TestObject {
 61         public final int value;
 62 
 63         public TestObject(int value) {
 64             this.value = value;
 65         }
 66     }
 67 
 68     private static volatile TestObject testObjectNone = null;
 69     private static volatile TestObject testObject1 = null;
 70     private static volatile TestObject testObject2 = null;
 71     private static volatile TestObject testObject3 = null;
 72     private static volatile TestObject testObject4 = null;
 73 
 74     private static ReferenceQueue<TestObject> queue = null;
 75 
 76     private static PhantomReference<TestObject> testPhantom1 = null;
 77 
 78     private static WeakReference<TestObject> testWeak2 = null;
 79     private static WeakReference<TestObject> testWeak3 = null;
 80     private static WeakReference<TestObject> testWeak4 = null;
 81 
 82     private static void setup() {
 83         testObjectNone = new TestObject(0);
 84         testObject1 = new TestObject(1);
 85         testObject2 = new TestObject(2);
 86         testObject3 = new TestObject(3);
 87         testObject4 = new TestObject(4);
 88 
 89         queue = new ReferenceQueue<TestObject>();
 90 
 91         testPhantom1 = new PhantomReference<TestObject>(testObject1, queue);
 92 
 93         testWeak2 = new WeakReference<TestObject>(testObject2, queue);
 94         testWeak3 = new WeakReference<TestObject>(testObject3, queue);
 95         testWeak4 = new WeakReference<TestObject>(testObject4, queue);
 96     }
 97 
 98     private static void gcUntilOld(Object o) throws Exception {
 99         if (!WB.isObjectInOldGen(o)) {
100             WB.fullGC();
101             if (!WB.isObjectInOldGen(o)) {
102                 fail("object not promoted by full gc");
103             }
104         }
105     }
106 
107     private static void gcUntilOld() throws Exception {
108         gcUntilOld(testObjectNone);
109         gcUntilOld(testObject1);
110         gcUntilOld(testObject2);
111         gcUntilOld(testObject3);
112         gcUntilOld(testObject4);
113 
114         gcUntilOld(testPhantom1);
115 
116         gcUntilOld(testWeak2);
117         gcUntilOld(testWeak3);
118         gcUntilOld(testWeak4);
119     }
120 
121     private static void progress(String msg) {
122         System.out.println(msg);
123     }
124 
125     private static void fail(String msg) throws Exception {
126         throw new RuntimeException(msg);
127     }
128 
129     private static void expectCleared(Reference<TestObject> ref,
130                                       String which) throws Exception {
131         expectNotValue(ref, testObjectNone, which);
132         if (!ref.refersTo(null)) {
133             fail("expected " + which + " to be cleared");
134         }
135     }
136 
137     private static void expectNotCleared(Reference<TestObject> ref,
138                                          String which) throws Exception {
139         expectNotValue(ref, testObjectNone, which);
140         if (ref.refersTo(null)) {
141             fail("expected " + which + " to not be cleared");
142         }
143     }
144 
145     private static void expectValue(Reference<TestObject> ref,
146                                     TestObject value,
147                                     String which) throws Exception {
148         expectNotValue(ref, testObjectNone, which);
149         expectNotCleared(ref, which);
150         if (!ref.refersTo(value)) {
151             fail(which + " doesn't refer to expected value");
152         }
153     }
154 
155     private static void expectNotValue(Reference<TestObject> ref,
156                                        TestObject value,
157                                        String which) throws Exception {
158         if (ref.refersTo(value)) {
159             fail(which + " refers to unexpected value");
160         }
161     }
162 
163     private static void checkInitialStates() throws Exception {
164         expectValue(testPhantom1, testObject1, "testPhantom1");
165         expectValue(testWeak2, testObject2, "testWeak2");
166         expectValue(testWeak3, testObject3, "testWeak3");
167         expectValue(testWeak4, testObject4, "testWeak4");
168     }
169 
170     private static void discardStrongReferences() {
171         // testObjectNone not dropped
172         testObject1 = null;
173         testObject2 = null;
174         // testObject3 not dropped
175         testObject4 = null;
176     }
177 
178     private static void testConcurrentCollection() throws Exception {
179         progress("setup concurrent collection test");
180         setup();
181         progress("gcUntilOld");
182         gcUntilOld();
183 
184         progress("acquire control of concurrent cycles");
185         WB.concurrentGCAcquireControl();
186         try {
187             progress("check initial states");
188             checkInitialStates();
189 
190             progress("discard strong references");
191             discardStrongReferences();
192 
193             progress("run GC to before marking completed");
194             WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED);
195 
196             progress("fetch test objects, possibly keeping some alive");
197             expectNotCleared(testPhantom1, "testPhantom1");
198             expectNotCleared(testWeak2, "testWeak2");
199             expectValue(testWeak3, testObject3, "testWeak3");
200 
201             // For some collectors, calling get() will keep testObject4 alive.
202             if (testWeak4.get() == null) {
203                 fail("testWeak4 unexpectedly == null");
204             }
205 
206             progress("finish collection");
207             WB.concurrentGCRunToIdle();
208 
209             progress("verify expected clears");
210             expectCleared(testPhantom1, "testPhantom1");
211             expectCleared(testWeak2, "testWeak2");
212             expectValue(testWeak3, testObject3, "testWeak3");
213             expectNotCleared(testWeak4, "testWeak4");
214 
215             progress("verify get returns expected values");
216             if (testWeak2.get() != null) {
217                 fail("testWeak2.get() != null");
218             }
219 
220             TestObject obj3 = testWeak3.get();
221             if (obj3 == null) {
222                 fail("testWeak3.get() returned null");
223             } else if (obj3.value != 3) {
224                 fail("testWeak3.get().value is " + obj3.value);
225             }
226 
227             TestObject obj4 = testWeak4.get();
228             if (obj4 == null) {
229                 fail("testWeak4.get() returned null");
230             } else if (obj4.value != 4) {
231                 fail("testWeak4.get().value is " + obj4.value);
232             }
233 
234             progress("verify queue entries");
235             long timeout = 60000; // 1 minute of milliseconds.
236             while (true) {
237                 Reference<? extends TestObject> ref = queue.remove(timeout);
238                 if (ref == null) {
239                     break;
240                 } else if (ref == testPhantom1) {
241                     testPhantom1 = null;
242                 } else if (ref == testWeak2) {
243                     testWeak2 = null;
244                 } else if (ref == testWeak3) {
245                     testWeak3 = null;
246                 } else if (ref == testWeak4) {
247                     testWeak4 = null;
248                 } else {
249                     fail("unexpected reference in queue");
250                 }
251             }
252             if (testPhantom1 != null) {
253                 fail("testPhantom1 not notified");
254             } else if (testWeak2 != null) {
255                 fail("testWeak2 not notified");
256             } else if (testWeak3 == null) {
257                 fail("testWeak3 notified");
258             } else if (testWeak4 == null) {
259                 if (obj4 != null) {
260                     fail("testWeak4 notified");
261                 }
262             }
263 
264         } finally {
265             progress("release control of concurrent cycles");
266             WB.concurrentGCReleaseControl();
267         }
268         progress("finished concurrent collection test");
269     }
270 
271     private static void testSimpleCollection() throws Exception {
272         progress("setup simple collection test");
273         setup();
274         progress("gcUntilOld");
275         gcUntilOld();
276 
277         progress("check initial states");
278         checkInitialStates();
279 
280         progress("discard strong references");
281         TestObject tw4 = testWeak4.get(); // Keep testObject4 alive.
282         discardStrongReferences();
283 
284         progress("collect garbage");
285         WB.fullGC();
286 
287         progress("verify expected clears");
288         expectCleared(testPhantom1, "testPhantom1");
289         expectCleared(testWeak2, "testWeak2");
290         expectValue(testWeak3, testObject3, "testWeak3");
291         expectNotCleared(testWeak4, "testWeak4");
292 
293         progress("verify get returns expected values");
294         if (testWeak2.get() != null) {
295             fail("testWeak2.get() != null");
296         } else if (testWeak3.get() != testObject3) {
297             fail("testWeak3.get() is not expected value");
298         } else if (testWeak4.get() != tw4) {
299             fail("testWeak4.get() is not expected value");
300         }
301 
302         progress("finished simple collection test");
303     }
304 
305     public static void main(String[] args) throws Exception {
306         if (WB.supportsConcurrentGCBreakpoints()) {
307             testConcurrentCollection();
308         }
309         testSimpleCollection();
310     }
311 }