1/*
2 * Copyright (c) 2011, 2017, 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 4268317 8132306 8175797
26 * @summary Test if Reference.enqueue() works properly with GC
27 * @run main ReferenceEnqueue
28 * @run main/othervm -Djdk.lang.ref.disableClearBeforeEnqueue=true ReferenceEnqueue
29 */
30
31import java.lang.ref.*;
32import java.util.ArrayList;
33import java.util.List;
34
35public class ReferenceEnqueue {
36
37    public static void main(String args[]) throws Exception {
38        for (int i=0; i < 5; i++) {
39            new WeakRef().run();
40            new ExplicitEnqueue().run();
41        }
42        System.out.println("Test passed.");
43    }
44
45    static class WeakRef {
46        final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
47        final Reference<Object> ref;
48        final int iterations = 1000;
49
50        WeakRef() {
51            this.ref = new WeakReference<Object>(new Object(), queue);
52        }
53
54        void run() throws InterruptedException {
55            System.gc();
56            for (int i = 0; i < iterations; i++) {
57                System.gc();
58                if (ref.isEnqueued()) {
59                    break;
60                }
61
62                Thread.sleep(100);
63            }
64
65            if (ref.isEnqueued() == false) {
66                // GC have not enqueued refWeak for the timeout period
67                System.out.println("Reference not enqueued yet");
68                return;
69            }
70
71            if (ref.enqueue() == true) {
72                // enqueue() should return false since
73                // ref is already enqueued by the GC
74                throw new RuntimeException("Error: enqueue() returned true;"
75                        + " expected false");
76            }
77
78            if (queue.poll() == null) {
79                // poll() should return ref enqueued by the GC
80                throw new RuntimeException("Error: poll() returned null;"
81                        + " expected ref object");
82            }
83        }
84    }
85
86    static class ExplicitEnqueue {
87        final ReferenceQueue<Object> queue = new ReferenceQueue<>();
88        final List<Reference<Object>> refs = new ArrayList<>();
89        final int iterations = 1000;
90        final boolean disableClearBeforeEnqueue =
91            Boolean.getBoolean("jdk.lang.ref.disableClearBeforeEnqueue");
92
93        ExplicitEnqueue() {
94            this.refs.add(new SoftReference<>(new Object(), queue));
95            this.refs.add(new WeakReference<>(new Object(), queue));
96            // Can't test PhantomReference because get() always returns null.
97        }
98
99        void run() throws InterruptedException {
100            for (Reference<Object> ref : refs) {
101                if (ref.enqueue() == false) {
102                    throw new RuntimeException("Error: enqueue failed");
103                }
104                if (disableClearBeforeEnqueue && ref.get() == null) {
105                    throw new RuntimeException("Error: clearing should be disabled");
106                }
107                if (!disableClearBeforeEnqueue && ref.get() != null) {
108                    throw new RuntimeException("Error: referent must be cleared");
109                }
110            }
111
112            System.gc();
113            for (int i = 0; refs.size() > 0 && i < iterations; i++) {
114                Reference<Object> ref = (Reference<Object>)queue.poll();
115                if (ref == null) {
116                    System.gc();
117                    Thread.sleep(100);
118                    continue;
119                }
120
121                if (refs.remove(ref) == false) {
122                    throw new RuntimeException("Error: unknown reference " + ref);
123                }
124            }
125
126            if (!refs.isEmpty()) {
127                throw new RuntimeException("Error: not all references are removed");
128            }
129        }
130    }
131}
132