ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/jtreg/util/WeakHashMap/GCDuringIteration.java
Revision: 1.13
Committed: Wed May 9 17:43:01 2018 UTC (6 years ago) by jsr166
Branch: MAIN
CVS Tags: HEAD
Changes since 1.12: +3 -4 lines
Log Message:
forceFullGc: avoid rare failures due to back-to-back GCs, seen only at Google

File Contents

# Content
1 /*
2 * Copyright (c) 2007, 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 /*
25 * @test
26 * @bug 6499848
27 * @library /test/lib
28 * @build jdk.test.lib.RandomFactory
29 * @run main GCDuringIteration
30 * @summary Check that iterators work properly in the presence of
31 * concurrent finalization and removal of elements.
32 * @key randomness
33 */
34
35 import jdk.test.lib.RandomFactory;
36
37 import java.lang.ref.ReferenceQueue;
38 import java.lang.ref.WeakReference;
39 import java.util.Arrays;
40 import java.util.Iterator;
41 import java.util.Map;
42 import java.util.NoSuchElementException;
43 import java.util.Random;
44 import java.util.WeakHashMap;
45 import java.util.concurrent.CountDownLatch;
46 import java.util.function.BooleanSupplier;
47
48 import static java.util.concurrent.TimeUnit.MILLISECONDS;
49
50 public class GCDuringIteration {
51
52 /** No guarantees, but effective in practice. */
53 static void forceFullGc() {
54 long timeoutMillis = 1000L;
55 CountDownLatch finalized = new CountDownLatch(1);
56 ReferenceQueue<Object> queue = new ReferenceQueue<>();
57 WeakReference<Object> ref = new WeakReference<>(
58 new Object() { protected void finalize() { finalized.countDown(); }},
59 queue);
60 try {
61 for (int tries = 3; tries--> 0; ) {
62 System.gc();
63 if (finalized.await(timeoutMillis, MILLISECONDS)
64 && queue.remove(timeoutMillis) != null
65 && ref.get() == null) {
66 System.runFinalization(); // try to pick up stragglers
67 return;
68 }
69 timeoutMillis *= 4;
70 }
71 } catch (InterruptedException unexpected) {
72 throw new AssertionError("unexpected InterruptedException");
73 }
74 throw new AssertionError("failed to do a \"full\" gc");
75 }
76
77 static void gcAwait(BooleanSupplier s) {
78 for (int i = 0; i < 10; i++) {
79 if (s.getAsBoolean())
80 return;
81 forceFullGc();
82 }
83 throw new AssertionError("failed to satisfy condition");
84 }
85
86 // A class with the traditional pessimal hashCode implementation,
87 // to ensure that all instances end up in the same bucket.
88 static class Foo { public int hashCode() { return 42; }}
89
90 <K,V> void put(Map<K,V> map, K k, V v) {
91 check(! map.containsKey(k));
92 equal(map.get(k), null);
93 equal(map.put(k, v), null);
94 equal(map.get(k), v);
95 check(map.containsKey(k));
96 equal(map.put(k, v), v);
97 equal(map.get(k), v);
98 check(map.containsKey(k));
99 check(! map.isEmpty());
100 equal(map.keySet().iterator().next(), k);
101 equal(map.values().iterator().next(), v);
102 }
103
104 static final Random rnd = RandomFactory.getRandom();
105
106 void checkIterator(final Iterator<Map.Entry<Foo, Integer>> it, int first) {
107 for (int i = first; i >= 0; --i) {
108 if (rnd.nextBoolean()) check(it.hasNext());
109 equal(it.next().getValue(), i);
110 }
111 if (rnd.nextBoolean()) {
112 try {
113 it.next();
114 throw new AssertionError("should throw");
115 } catch (NoSuchElementException success) {}
116 }
117
118 if (rnd.nextBoolean())
119 check(! it.hasNext());
120 }
121
122 <K,V> V firstValue(Map<K,V> map) {
123 return map.values().iterator().next();
124 }
125
126 void test(String[] args) throws Throwable {
127 final int n = 10;
128 // Create array of strong refs
129 final Foo[] foos = new Foo[2*n];
130 final Map<Foo,Integer> map = new WeakHashMap<>(foos.length);
131 check(map.isEmpty());
132 equal(map.size(), 0);
133
134 for (int i = 0; i < foos.length; i++) {
135 Foo foo = new Foo();
136 foos[i] = foo;
137 put(map, foo, i);
138 }
139 equal(map.size(), foos.length);
140
141 {
142 int first = firstValue(map);
143 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator();
144 foos[first] = null;
145 gcAwait(() -> map.size() == first);
146 checkIterator(it, first-1);
147 equal(map.size(), first);
148 equal(firstValue(map), first-1);
149 }
150
151 {
152 int first = firstValue(map);
153 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator();
154 it.next(); // protects first entry
155 System.out.println(map.values());
156 int oldSize = map.size();
157 foos[first] = null;
158 forceFullGc();
159 equal(map.size(), oldSize);
160 System.out.println(map.values());
161 checkIterator(it, first-1);
162 // first entry no longer protected
163 gcAwait(() -> map.size() == first);
164 equal(firstValue(map), first-1);
165 }
166
167 {
168 int first = firstValue(map);
169 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator();
170 it.next(); // protects first entry
171 System.out.println(map.values());
172 foos[first] = foos[first-1] = null;
173 gcAwait(() -> map.size() == first);
174 equal(firstValue(map), first);
175 System.out.println(map.values());
176 checkIterator(it, first-2);
177 // first entry no longer protected
178 gcAwait(() -> map.size() == first-1);
179 equal(firstValue(map), first-2);
180 }
181
182 {
183 int first = firstValue(map);
184 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator();
185 it.next(); // protects first entry
186 it.hasNext(); // protects second entry
187 System.out.println(map.values());
188 int oldSize = map.size();
189 foos[first] = foos[first-1] = null;
190 forceFullGc();
191 equal(map.size(), oldSize);
192 equal(firstValue(map), first);
193 System.out.println(map.values());
194 checkIterator(it, first-1);
195 // first entry no longer protected
196 gcAwait(() -> map.size() == first-1);
197 equal(firstValue(map), first-2);
198 }
199
200 {
201 int first = firstValue(map);
202 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator();
203 it.next(); // protects first entry
204 System.out.println(map.values());
205 equal(map.size(), first+1);
206 foos[first] = foos[first-1] = null;
207 gcAwait(() -> map.size() == first);
208 it.remove();
209 equal(firstValue(map), first-2);
210 equal(map.size(), first-1);
211 System.out.println(map.values());
212 checkIterator(it, first-2);
213 // first entry no longer protected
214 gcAwait(() -> map.size() == first-1);
215 equal(firstValue(map), first-2);
216 }
217
218 {
219 int first = firstValue(map);
220 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator();
221 it.next(); // protects first entry
222 it.remove();
223 it.hasNext(); // protects second entry
224 System.out.println(map.values());
225 equal(map.size(), first);
226 foos[first] = foos[first-1] = null;
227 forceFullGc();
228 equal(firstValue(map), first-1);
229 equal(map.size(), first);
230 System.out.println(map.values());
231 checkIterator(it, first-1);
232 gcAwait(() -> map.size() == first-1);
233 equal(firstValue(map), first-2);
234 }
235
236 {
237 int first = firstValue(map);
238 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator();
239 it.hasNext(); // protects first entry
240 Arrays.fill(foos, null);
241 gcAwait(() -> map.size() == 1);
242 System.out.println(map.values());
243 equal(it.next().getValue(), first);
244 check(! it.hasNext());
245 gcAwait(() -> map.size() == 0);
246 check(map.isEmpty());
247 }
248 }
249
250 //--------------------- Infrastructure ---------------------------
251 volatile int passed = 0, failed = 0;
252 void pass() {passed++;}
253 void fail() {failed++; Thread.dumpStack();}
254 void fail(String msg) {System.err.println(msg); fail();}
255 void unexpected(Throwable t) {failed++; t.printStackTrace();}
256 void check(boolean cond) {if (cond) pass(); else fail();}
257 void equal(Object x, Object y) {
258 if (x == null ? y == null : x.equals(y)) pass();
259 else fail(x + " not equal to " + y);}
260 public static void main(String[] args) throws Throwable {
261 new GCDuringIteration().instanceMain(args);}
262 void instanceMain(String[] args) throws Throwable {
263 try {test(args);} catch (Throwable t) {unexpected(t);}
264 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
265 if (failed > 0) throw new AssertionError("Some tests failed");}
266 }