1 /* 2 * Copyright (c) 2007, 2011, 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 * @ignore until 6842353 is resolved 28 * @summary Check that iterators work properly in the presence of 29 * concurrent finalization and removal of elements. 30 * @key randomness 31 */ 32 33 import java.util.*; 34 import java.util.concurrent.CountDownLatch; 35 36 public class GCDuringIteration { 37 private static void waitForFinalizersToRun() { 38 for (int i = 0; i < 2; i++) 39 tryWaitForFinalizersToRun(); 40 } 41 42 private static void tryWaitForFinalizersToRun() { 43 System.gc(); 44 final CountDownLatch fin = new CountDownLatch(1); 45 new Object() { protected void finalize() { fin.countDown(); }}; 46 System.gc(); 47 try { fin.await(); } 48 catch (InterruptedException ie) { throw new Error(ie); } 49 } 50 51 // A class with the traditional pessimal hashCode implementation, 52 // to ensure that all instances end up in the same bucket. 53 static class Foo { public int hashCode() { return 42; }} 54 55 <K,V> void put(Map<K,V> map, K k, V v) { 56 check(! map.containsKey(k)); 57 equal(map.get(k), null); 58 equal(map.put(k, v), null); 59 equal(map.get(k), v); 60 check(map.containsKey(k)); 61 equal(map.put(k, v), v); 62 equal(map.get(k), v); 63 check(map.containsKey(k)); 64 check(! map.isEmpty()); 65 equal(map.keySet().iterator().next(), k); 66 equal(map.values().iterator().next(), v); 67 } 68 69 void checkIterator(final Iterator<Map.Entry<Foo, Integer>> it, int first) { 70 final Random rnd = new Random(); 71 for (int i = first; i >= 0; --i) { 72 if (rnd.nextBoolean()) check(it.hasNext()); 73 equal(it.next().getValue(), i); 74 } 75 if (rnd.nextBoolean()) 76 THROWS(NoSuchElementException.class, 77 new F(){void f(){it.next();}}); 78 if (rnd.nextBoolean()) 79 check(! it.hasNext()); 80 } 81 82 <K,V> V firstValue(Map<K,V> map) { 83 return map.values().iterator().next(); 84 } 85 86 void test(String[] args) throws Throwable { 87 final int n = 10; 88 // Create array of strong refs 89 final Foo[] foos = new Foo[2*n]; 90 final Map<Foo,Integer> map = new WeakHashMap<Foo,Integer>(foos.length); 91 check(map.isEmpty()); 92 equal(map.size(), 0); 93 94 for (int i = 0; i < foos.length; i++) { 95 Foo foo = new Foo(); 96 foos[i] = foo; 97 put(map, foo, i); 98 } 99 equal(map.size(), foos.length); 100 101 { 102 int first = firstValue(map); 103 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); 104 foos[first] = null; 105 for (int i = 0; i < 10 && map.size() != first; i++) 106 tryWaitForFinalizersToRun(); 107 equal(map.size(), first); 108 checkIterator(it, first-1); 109 equal(map.size(), first); 110 equal(firstValue(map), first-1); 111 } 112 113 { 114 int first = firstValue(map); 115 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); 116 it.next(); // protects first entry 117 System.out.println(map.values()); 118 foos[first] = null; 119 tryWaitForFinalizersToRun() 120 equal(map.size(), first+1); 121 System.out.println(map.values()); 122 checkIterator(it, first-1); 123 // first entry no longer protected 124 for (int i = 0; i < 10 && map.size() != first; i++) 125 tryWaitForFinalizersToRun(); 126 equal(map.size(), first); 127 equal(firstValue(map), first-1); 128 } 129 130 { 131 int first = firstValue(map); 132 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); 133 it.next(); // protects first entry 134 System.out.println(map.values()); 135 foos[first] = foos[first-1] = null; 136 tryWaitForFinalizersToRun(); 137 equal(map.size(), first); 138 equal(firstValue(map), first); 139 System.out.println(map.values()); 140 checkIterator(it, first-2); 141 // first entry no longer protected 142 for (int i = 0; i < 10 && map.size() != first-1; i++) 143 tryWaitForFinalizersToRun(); 144 equal(map.size(), first-1); 145 equal(firstValue(map), first-2); 146 } 147 148 { 149 int first = firstValue(map); 150 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); 151 it.next(); // protects first entry 152 it.hasNext(); // protects second entry 153 System.out.println(map.values()); 154 foos[first] = foos[first-1] = null; 155 tryWaitForFinalizersToRun(); 156 equal(firstValue(map), first); 157 equal(map.size(), first+1); 158 System.out.println(map.values()); 159 checkIterator(it, first-1); 160 // first entry no longer protected 161 for (int i = 0; i < 10 && map.size() != first-1; i++) 162 tryWaitForFinalizersToRun(); 163 equal(map.size(), first-1); 164 equal(firstValue(map), first-2); 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 tryWaitForFinalizersToRun(); 174 it.remove(); 175 equal(firstValue(map), first-2); 176 equal(map.size(), first-1); 177 System.out.println(map.values()); 178 checkIterator(it, first-2); 179 // first entry no longer protected 180 for (int i = 0; i < 10 && map.size() != first-1; i++) 181 tryWaitForFinalizersToRun(); 182 equal(map.size(), first-1); 183 equal(firstValue(map), first-2); 184 } 185 186 { 187 int first = firstValue(map); 188 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); 189 it.next(); // protects first entry 190 it.remove(); 191 it.hasNext(); // protects second entry 192 System.out.println(map.values()); 193 foos[first] = foos[first-1] = null; 194 tryWaitForFinalizersToRun(); 195 equal(firstValue(map), first-1); 196 equal(map.size(), first); 197 System.out.println(map.values()); 198 checkIterator(it, first-1); 199 for (int i = 0; i < 10 && map.size() != first-1; i++) 200 tryWaitForFinalizersToRun(); 201 equal(map.size(), first-1); 202 equal(firstValue(map), first-2); 203 } 204 205 { 206 int first = firstValue(map); 207 final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); 208 it.hasNext(); // protects first entry 209 Arrays.fill(foos, null); 210 tryWaitForFinalizersToRun(); 211 equal(map.size(), 1); 212 System.out.println(map.values()); 213 equal(it.next().getValue(), first); 214 check(! it.hasNext()); 215 for (int i = 0; i < 10 && map.size() != 0; i++) 216 tryWaitForFinalizersToRun(); 217 equal(map.size(), 0); 218 check(map.isEmpty()); 219 } 220 } 221 222 //--------------------- Infrastructure --------------------------- 223 volatile int passed = 0, failed = 0; 224 void pass() {passed++;} 225 void fail() {failed++; Thread.dumpStack();} 226 void fail(String msg) {System.err.println(msg); fail();} 227 void unexpected(Throwable t) {failed++; t.printStackTrace();} 228 void check(boolean cond) {if (cond) pass(); else fail();} 229 void equal(Object x, Object y) { 230 if (x == null ? y == null : x.equals(y)) pass(); 231 else fail(x + " not equal to " + y);} 232 public static void main(String[] args) throws Throwable { 233 new GCDuringIteration().instanceMain(args);} 234 void instanceMain(String[] args) throws Throwable { 235 try {test(args);} catch (Throwable t) {unexpected(t);} 236 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); 237 if (failed > 0) throw new AssertionError("Some tests failed");} 238 abstract class F {abstract void f() throws Throwable;} 239 void THROWS(Class<? extends Throwable> k, F... fs) { 240 for (F f : fs) 241 try {f.f(); fail("Expected " + k.getName() + " not thrown");} 242 catch (Throwable t) { 243 if (k.isAssignableFrom(t.getClass())) pass(); 244 else unexpected(t);}} 245 }