1 /* 2 * Copyright (c) 2023, 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 8285932 8310913 27 * @summary Test features provided by the ReferencedKeyMap/ReferencedKeySet classes. 28 * @modules java.base/jdk.internal.util 29 * @compile --patch-module java.base=${test.src} ReferencedKeyTest.java 30 * @run main/othervm --patch-module java.base=${test.class.path} jdk.internal.util.ReferencedKeyTest 31 */ 32 33 package jdk.internal.util; 34 35 import java.lang.ref.PhantomReference; 36 import java.lang.ref.Reference; 37 import java.lang.ref.ReferenceQueue; 38 import java.util.HashMap; 39 import java.util.Map; 40 import java.util.Set; 41 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.function.BooleanSupplier; 43 import java.util.function.Supplier; 44 45 public class ReferencedKeyTest { 46 static long BASE_KEY = 10_000_000L; 47 48 public static void main(String[] args) { 49 mapTest(false, HashMap::new); 50 mapTest(true, HashMap::new); 51 mapTest(false, ConcurrentHashMap::new); 52 mapTest(true, ConcurrentHashMap::new); 53 54 setTest(false, HashMap::new); 55 setTest(true, HashMap::new); 56 setTest(false, ConcurrentHashMap::new); 57 setTest(true, ConcurrentHashMap::new); 58 } 59 60 static void assertTrue(boolean test, String message) { 61 if (!test) { 62 throw new RuntimeException(message); 63 } 64 } 65 66 static void mapTest(boolean isSoft, Supplier<Map<ReferenceKey<Long>, String>> supplier) { 67 Map<Long, String> map = ReferencedKeyMap.create(isSoft, supplier); 68 populate(map); 69 if (!isSoft) { 70 if (!collect(() -> map.isEmpty())) { 71 throw new RuntimeException("WeakReference map not collecting!"); 72 } 73 } 74 populate(map); 75 methods(map); 76 } 77 78 static void setTest(boolean isSoft, Supplier<Map<ReferenceKey<Long>, ReferenceKey<Long>>> supplier) { 79 ReferencedKeySet<Long> set = ReferencedKeySet.create(isSoft, supplier); 80 populate(set); 81 if (!isSoft) { 82 if (!collect(() -> set.isEmpty())) { 83 throw new RuntimeException("WeakReference set not collecting!"); 84 } 85 } 86 populate(set); 87 methods(set); 88 } 89 90 static void methods(Map<Long, String> map) { 91 assertTrue(map.size() == 26, "missing key"); 92 assertTrue(map.containsKey(BASE_KEY + 'a' -'a'), "missing key"); 93 assertTrue(map.get(BASE_KEY + 'b' -'a').equals("b"), "wrong key"); 94 assertTrue(map.containsValue("c"), "missing value"); 95 map.remove(BASE_KEY + 'd' -'a'); 96 assertTrue(map.get(BASE_KEY + 'd' -'a') == null, "not removed"); 97 map.putAll(Map.of(1L, "A", 2L, "B")); 98 assertTrue(map.get(2L).equals("B"), "collection not added"); 99 assertTrue(map.containsKey(1L), "key missing"); 100 assertTrue(map.containsValue("A"), "key missing"); 101 assertTrue(map.entrySet().contains(Map.entry(1L, "A")), "key missing"); 102 map.putIfAbsent(3L, "C"); 103 assertTrue(map.get(3L).equals("C"), "key missing"); 104 map.putIfAbsent(2L, "D"); 105 assertTrue(map.get(2L).equals("B"), "key replaced"); 106 map.remove(3L); 107 assertTrue(map.get(3L) == null, "key not removed"); 108 map.replace(2L, "D"); 109 assertTrue(map.get(2L).equals("D"), "key not replaced"); 110 map.replace(2L, "B", "E"); 111 assertTrue(map.get(2L).equals("D"), "key replaced"); 112 } 113 114 static void methods(ReferencedKeySet<Long> set) { 115 assertTrue(set.size() == 26, "missing key"); 116 assertTrue(set.contains(BASE_KEY + 3), "missing key"); 117 set.remove(BASE_KEY + 3); 118 assertTrue(!set.contains(BASE_KEY + 3), "not removed"); 119 Long element1 = set.get(BASE_KEY + 2); 120 Long element2 = set.get(BASE_KEY + 3); 121 Long element3 = set.get(BASE_KEY + 4); 122 Long intern1 = set.intern(BASE_KEY + 2); 123 Long intern2 = set.intern(BASE_KEY + 3); 124 Long intern3 = set.intern(BASE_KEY + 4, e -> e); 125 assertTrue(element1 != null, "missing key"); 126 assertTrue(element2 == null, "not removed"); 127 assertTrue(element1 == intern1, "intern failed"); // must be same object 128 assertTrue(intern2 != null, "intern failed"); 129 assertTrue(element3 == intern3, "intern failed"); 130 131 Long value1 = Long.valueOf(BASE_KEY + 999); 132 Long value2 = Long.valueOf(BASE_KEY + 999); 133 assertTrue(set.add(value1), "key not added"); 134 assertTrue(!set.add(value1), "key added after second attempt"); 135 assertTrue(!set.add(value2), "key should not have been added"); 136 } 137 138 // Borrowed from jdk.test.lib.util.ForceGC but couldn't use from java.base/jdk.internal.util 139 static boolean collect(BooleanSupplier booleanSupplier) { 140 ReferenceQueue<Object> queue = new ReferenceQueue<>(); 141 Object obj = new Object(); 142 PhantomReference<Object> ref = new PhantomReference<>(obj, queue); 143 obj = null; 144 Reference.reachabilityFence(obj); 145 Reference.reachabilityFence(ref); 146 long timeout = 1000L; 147 long quanta = 200L; 148 long retries = timeout / quanta; 149 150 for (; retries >= 0; retries--) { 151 if (booleanSupplier.getAsBoolean()) { 152 return true; 153 } 154 155 System.gc(); 156 157 try { 158 queue.remove(quanta); 159 } catch (InterruptedException ie) { 160 // ignore, the loop will try again 161 } 162 } 163 164 return booleanSupplier.getAsBoolean(); 165 } 166 167 static void populate(Map<Long, String> map) { 168 for (int i = 0; i < 26; i++) { 169 Long key = BASE_KEY + i; 170 String value = String.valueOf((char) ('a' + i)); 171 map.put(key, value); 172 } 173 } 174 175 static void populate(Set<Long> set) { 176 for (int i = 0; i < 26; i++) { 177 Long value = BASE_KEY + i; 178 set.add(value); 179 } 180 } 181 }