1 /* 2 * Copyright (c) 2022, 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 * @run testng/othervm WeakValuePolicyTest 27 * @summary Test WeakHashMap.ValuePolicy modes 28 */ 29 import org.testng.annotations.DataProvider; 30 import org.testng.annotations.Test; 31 import org.testng.Assert; 32 import static org.testng.Assert.*; 33 34 import java.util.ArrayList; 35 import java.util.Objects; 36 import java.util.WeakHashMap; 37 import java.lang.ref.Reference; 38 import java.lang.ref.ReferenceQueue; 39 import java.lang.ref.SoftReference; 40 41 @Test 42 public class WeakValuePolicyTest { 43 44 record KeyValue(Object key, String value){}; 45 46 @DataProvider(name="Keys") 47 KeyValue[] keys() { 48 return new KeyValue[] { 49 new KeyValue(new IntValue(1), "IntValue(1)"), 50 new KeyValue(new StringValue("xyz"), "StringValue(\"xyz\")"), 51 new KeyValue(Integer.valueOf(2), "Integer.valueOf(2)"), 52 }; 53 } 54 55 @DataProvider(name="WeakHashMaps") 56 Object[][] weakValuePolicy() { 57 return new Object[][] { 58 {new WeakHashMap<Object, String>(0, 0.75f, WeakHashMap.ValuePolicy.SOFT)}, 59 {new WeakHashMap<Object, String>(0, 0.75f, WeakHashMap.ValuePolicy.STRONG)}, 60 }; 61 } 62 63 @Test(dataProvider="WeakHashMaps") 64 public void putValueSoftStrong(WeakHashMap map) { 65 WeakHashMap.ValuePolicy policy = map.valuePolicy(); 66 for (KeyValue kv : keys()) { 67 System.out.println("k: " + kv.key); 68 Assert.assertFalse(map.containsKey(kv.key), "map.contains on empty map: " + kv.key); 69 Assert.assertNull(map.get(kv.key), "map.get on empty map: " + kv.key); 70 var prev = map.put(kv.key, kv.value); 71 Assert.assertNull(prev, "map.put on empty map did not return null: " + kv.key); 72 Assert.assertEquals(map.get(kv.key), kv.value, "map.get after put: " + kv.key); 73 74 forceGC(); 75 76 if (kv.key.getClass().isValue() && 77 policy.equals(WeakHashMap.ValuePolicy.SOFT)) { 78 Assert.assertFalse(map.containsKey(kv.key), "map.containsKey after GC: " + kv.key); 79 String value = (String)map.get(kv.key); 80 Assert.assertNull(value, "map.get after GC should return null: " + kv.key); 81 } else { 82 prev = map.remove(kv.key); 83 Assert.assertEquals(prev, kv.value, "map.remove: " + kv.key); 84 Assert.assertNull(map.get(kv.key), "map.get after remove: " + kv.key); 85 } 86 Assert.assertTrue(map.isEmpty(), "m.isEmpty()"); 87 } 88 } 89 90 @Test 91 public void putValueDiscard() { 92 final WeakHashMap<Object, String> map = new WeakHashMap<>(0, 0.75f, WeakHashMap.ValuePolicy.DISCARD); 93 final IntValue intValue = new IntValue(1); 94 String old = map.put(intValue, "IntValue(1)"); 95 Assert.assertNull(old, "old"); 96 old = map.get(intValue); 97 Assert.assertNull(old, "get after put of discarded value"); 98 } 99 100 @Test 101 public void putValueThrows() { 102 final WeakHashMap<Object, String> map = new WeakHashMap<>(0, 0.75f, WeakHashMap.ValuePolicy.THROW); 103 Assert.assertThrows(IdentityException.class, () -> map.put(new IntValue(1), "IntValue(1)")); 104 } 105 106 private void forceGC() { 107 Object marker = new Object(); 108 ReferenceQueue<Object> queue = new ReferenceQueue<>(); 109 SoftReference expected = new SoftReference(marker, queue); 110 marker = null; 111 Reference<?> actual = waitForReference(queue); 112 assertEquals(actual, expected, "Unexpected Reference queued"); 113 } 114 115 /** 116 * Wait for any Reference to be enqueued to a ReferenceQueue. 117 * The garbage collector is invoked to find unreferenced objects. 118 * 119 * @param queue a ReferenceQueue 120 * @return true if the reference was enqueued, false if not enqueued within 121 */ 122 private static Reference<?> waitForReference(ReferenceQueue<Object> queue) { 123 Objects.requireNonNull(queue, "queue should not be null"); 124 ArrayList<Object> chunks = new ArrayList<>(10000); 125 try { 126 for (int i = 0; i < 10_000; i++) { 127 chunks.add(new byte[100_000]); 128 } 129 } catch (OutOfMemoryError oome) { 130 131 } finally { 132 chunks = null; 133 } 134 for (int retries = 100; retries > 0; retries--) { 135 try { 136 var r = queue.remove(10L); 137 if (r != null) { 138 return r; 139 } 140 } catch (InterruptedException ie) { 141 // ignore, the loop will try again 142 } 143 } 144 return null; 145 } 146 147 static value class IntValue { 148 int value; 149 150 IntValue(int value) { 151 this.value = value; 152 } 153 154 @java.lang.Override 155 public java.lang.String toString() { 156 return "IntValue{" + "value=" + value + '}'; 157 } 158 } 159 160 static value class StringValue { 161 String value; 162 163 StringValue(String value) { 164 this.value = value; 165 } 166 167 @java.lang.Override 168 public java.lang.String toString() { 169 return "StringValue{" + "value='" + value + '\'' + '}'; 170 } 171 } 172 }