1 /*
  2  * Copyright (c) 2011, 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 /* @test
 25  * @bug 4243978 8336671
 26  * @summary Test if Reference.enqueue() works properly with pending references
 27  */
 28 import java.lang.ref.*;
 29 import java.util.Arrays;
 30 
 31 public class ReferenceEnqueuePending {
 32 
 33     record Numbered(int number) {}
 34 
 35     static class NumberedWeakReference extends WeakReference<Numbered> {
 36         //  Add an integer to identify the weak reference object.
 37         int number;
 38 
 39         NumberedWeakReference(Numbered referent, ReferenceQueue<Numbered> q, int i) {
 40             super(referent, q);
 41             number = i;
 42         }
 43     }
 44 
 45     static final boolean debug = System.getProperty("test.debug") != null;
 46     static final int iterations = 1000;
 47     static final int gc_trigger = 99;
 48     static int[] a = new int[2 * iterations];
 49     // Keep all weak references alive with the following array.
 50     static NumberedWeakReference[] b = new NumberedWeakReference[iterations];
 51 
 52     public static void main(String[] argv) throws Exception {
 53         if (debug) {
 54             System.out.println("Starting the test.");
 55         }
 56         // Raise thread priority to match the referenceHandler
 57         // priority, so that they can race also on a uniprocessor.
 58         raisePriority();
 59 
 60         ReferenceQueue<Numbered> refQueue = new ReferenceQueue<>();
 61 
 62         // Our objective is to let the mutator enqueue
 63         // a Reference object that may already be in the
 64         // pending state because of having been identified
 65         // as weakly reachable at a previous garbage collection.
 66         // To this end, we create many Reference objects, each with
 67         // a unique Numbered object as its referant.
 68         // We let the referents become eligible for collection,
 69         // while racing with the garbage collector which may
 70         // have pended some of these Reference objects.
 71         // Finally, we check that all of the Reference objects
 72         // end up on their queue. The test was originally
 73         // submitted to show that such races could break the
 74         // pending list and/or the reference queue, because of sharing
 75         // the same link ("next") for maintaining both lists, thus
 76         // losing some of the Reference objects on either queue.
 77 
 78         Numbered obj = new Numbered(0);
 79         NumberedWeakReference weaky = new NumberedWeakReference(obj, refQueue, 0);
 80         for (int i = 1; i < iterations; i++) {
 81             // Create a new object, dropping the only strong reference to
 82             // the previous Numbered object.
 83             obj = new Numbered(i);
 84             // Trigger gc each gc_trigger iterations.
 85             if ((i % gc_trigger) == 0) {
 86                 forceGc(0);
 87             }
 88             // Enqueue every other weaky.
 89             if ((i % 2) == 0) {
 90                 weaky.enqueue();
 91             }
 92             // Remember the Reference objects, for testing later.
 93             b[i - 1] = weaky;
 94             // Get a new weaky for the Numbered object just
 95             // created, which may be explicitly enqueued in
 96             // our next trip around the loop.
 97             weaky = new NumberedWeakReference(obj, refQueue, i);
 98         }
 99 
100         // Do a final collection to discover and process all
101         // Reference objects created above, allowing some time
102         // for the ReferenceHandler thread to queue the References.
103         forceGc(100);
104         forceGc(100);
105 
106         // Verify that all WeakReference objects ended up queued.
107         checkResult(refQueue, iterations-1);
108 
109         // Ensure the final weaky is live but won't be enqueued during
110         // result checking, by ensuring its referent remains live.
111         // This eliminates behavior changes resulting from different
112         // compiler optimizations.
113         Reference.reachabilityFence(weaky);
114         Reference.reachabilityFence(obj);
115 
116         System.out.println("Test passed.");
117     }
118 
119     private static NumberedWeakReference waitForReference(ReferenceQueue<Numbered> queue) {
120         try {
121             return (NumberedWeakReference) queue.remove(30000); // 30sec
122         } catch (InterruptedException ie) {
123             return null;
124         }
125     }
126 
127     private static void checkResult(ReferenceQueue<Numbered> queue,
128                                     int expected) {
129         if (debug) {
130             System.out.println("Reading the queue");
131         }
132 
133         // Empty the queue and record numbers into a[];
134         NumberedWeakReference weakRead = waitForReference(queue);
135         int length = 0;
136         while (weakRead != null) {
137             a[length++] = weakRead.number;
138             if (length < expected) {
139                 weakRead = waitForReference(queue);
140             } else {            // Check for unexpected extra entries.
141                 weakRead = (NumberedWeakReference) queue.poll();
142             }
143         }
144         if (debug) {
145             System.out.println("Reference Queue had " + length + " elements");
146         }
147 
148 
149         // verify the queued references: all but the last Reference object
150         // should have been in the queue.
151         if (debug) {
152             System.out.println("Start of final check");
153         }
154 
155         // Sort the first "length" elements in array "a[]".
156         Arrays.sort(a, 0, length);
157 
158         boolean fail = (length != expected);
159         for (int i = 0; i < length; i++) {
160             if (a[i] != i) {
161                 if (debug) {
162                     System.out.println("a[" + i + "] is not " + i + " but " + a[i]);
163                 }
164                 fail = true;
165             }
166         }
167         if (fail) {
168              printMissingElements(length, expected);
169              throw new RuntimeException("TEST FAILED: only " + length
170                     + " reference objects have been queued out of "
171                     + expected);
172         }
173     }
174 
175     private static void printMissingElements(int length, int expected) {
176         System.out.println("The following numbers were not found in the reference queue: ");
177         int missing = 0;
178         int element = 0;
179         for (int i = 0; i < length; i++) {
180             while ((a[i] != element) & (element < expected)) {
181                 System.out.print(element + " ");
182                 if (missing % 20 == 19) {
183                     System.out.println(" ");
184                 }
185                 missing++;
186                 element++;
187             }
188             element++;
189         }
190         System.out.print("\n");
191     }
192 
193     private static void forceGc(long millis) throws InterruptedException {
194         Runtime.getRuntime().gc();
195         Thread.sleep(millis);
196     }
197 
198     // Raise thread priority so as to increase the
199     // probability of the mutator succeeding in enqueueing
200     // an object that is still in the pending state.
201     // This is (probably) only required for a uniprocessor.
202     static void raisePriority() {
203         Thread tr = Thread.currentThread();
204         tr.setPriority(Thread.MAX_PRIORITY);
205     }
206 }   // End of class ReferenceEnqueuePending