1 /* 2 * Copyright (c) 2005, 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 6312056 4155650 4294891 4904074 27 * @summary Reasonable things should happen if mutating while iterating. 28 */ 29 30 import java.util.ConcurrentModificationException; 31 import java.util.HashMap; 32 import java.util.Iterator; 33 import java.util.Map; 34 import java.util.concurrent.ConcurrentHashMap; 35 import java.util.concurrent.ConcurrentMap; 36 import java.util.concurrent.ConcurrentSkipListMap; 37 38 public class ConcurrentModification { 39 static volatile int passed = 0, failed = 0; 40 41 static void fail(String msg) { 42 failed++; 43 new AssertionError(msg).printStackTrace(); 44 } 45 46 static void unexpected(Throwable t) { 47 failed++; 48 t.printStackTrace(); 49 } 50 51 static void check(boolean condition, String msg) { 52 if (condition) 53 passed++; 54 else 55 fail(msg); 56 } 57 58 static void check(boolean condition) { 59 check(condition, "Assertion failed"); 60 } 61 62 private static void test(ConcurrentMap<Integer, Integer> m) 63 { 64 try { 65 m.clear(); 66 check(m.isEmpty()); 67 m.put(1,2); 68 Iterator<Map.Entry<Integer,Integer>> it = m.entrySet().iterator(); 69 if (it.hasNext()) { 70 m.remove(1); // sneaky 71 Map.Entry<Integer, Integer> e = it.next(); 72 check(m.isEmpty()); 73 check(e.getKey() == 1); 74 check(e.getValue() == 2); 75 } 76 } catch (Throwable t) {unexpected(t);} 77 78 try { 79 m.clear(); 80 check(m.isEmpty()); 81 m.put(1,2); 82 Iterator<Map.Entry<Integer,Integer>> it = m.entrySet().iterator(); 83 if (it.hasNext()) { 84 m.put(1,3); // sneaky 85 Map.Entry<Integer, Integer> e = it.next(); 86 check(e.getKey() == 1); 87 check(e.getValue() == 2 || e.getValue() == 3); 88 if (m instanceof ConcurrentHashMap) { 89 e.setValue(4); 90 check(m.get(1) == 4); 91 } 92 } 93 } catch (Throwable t) {unexpected(t);} 94 } 95 96 // expected java.util.ConcurrentModificationException 97 static void expected(Throwable t) { 98 if (t instanceof ConcurrentModificationException) 99 passed++; 100 else { 101 failed++; 102 t.printStackTrace(); 103 } 104 } 105 private static void testClear(HashMap<Integer, Integer> m) { 106 try { 107 // modifying an empty map expects no ConcurrentModificationException 108 m.clear(); 109 check(m.isEmpty()); 110 m.computeIfAbsent(null, key -> { 111 m.clear(); 112 return null; 113 }); 114 } catch (Throwable t) { 115 unexpected(t); 116 } 117 118 try { 119 // modifying an empty map expects no ConcurrentModificationException 120 m.clear(); 121 check(m.isEmpty()); 122 m.compute(null, (key, value) -> { 123 m.clear(); 124 return null; 125 }); 126 } catch (Throwable t) { 127 unexpected(t); 128 } 129 130 try { 131 // modifying an empty map expects no ConcurrentModificationException 132 m.clear(); 133 m.put(1, 2); 134 check(!m.isEmpty()); 135 m.computeIfAbsent(null, key -> { 136 m.clear(); 137 return null; 138 }); 139 } catch (Throwable t) { 140 expected(t); 141 } 142 143 try { 144 // modifying an empty map expects no ConcurrentModificationException 145 m.clear(); 146 m.put(1, 2); 147 check(!m.isEmpty()); 148 m.compute(null, (key, value) -> { 149 m.clear(); 150 return null; 151 }); 152 } catch (Throwable t) { 153 expected(t); 154 } 155 } 156 157 public static void main(String[] args) { 158 test(new ConcurrentHashMap<Integer,Integer>()); 159 test(new ConcurrentSkipListMap<Integer,Integer>()); 160 testClear(new HashMap<Integer, Integer>()); 161 162 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); 163 if (failed > 0) throw new Error("Some tests failed"); 164 } 165 }