Thu, 27 Feb 2020 06:41:35 +0000
8055283: Expand ResourceHashtable with C_HEAP allocation, removal and some unit tests
Reviewed-by: phh
1.1 --- a/src/share/vm/prims/jni.cpp Thu Feb 27 06:05:11 2020 +0000 1.2 +++ b/src/share/vm/prims/jni.cpp Thu Feb 27 06:41:35 2020 +0000 1.3 @@ -5101,6 +5101,7 @@ 1.4 void TestNewSize_test(); 1.5 void TestKlass_test(); 1.6 void Test_linked_list(); 1.7 +void TestResourcehash_test(); 1.8 void TestChunkedList_test(); 1.9 #if INCLUDE_ALL_GCS 1.10 void TestOldFreeSpaceCalculation_test(); 1.11 @@ -5133,6 +5134,7 @@ 1.12 run_unit_test(test_snprintf()); 1.13 run_unit_test(TestNewSize_test()); 1.14 run_unit_test(TestKlass_test()); 1.15 + run_unit_test(TestResourcehash_test()); 1.16 run_unit_test(Test_linked_list()); 1.17 run_unit_test(TestChunkedList_test()); 1.18 run_unit_test(ObjectMonitor::sanity_checks());
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/share/vm/utilities/resourceHash.cpp Thu Feb 27 06:41:35 2020 +0000 2.3 @@ -0,0 +1,182 @@ 2.4 +/* 2.5 + * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. 2.11 + * 2.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.15 + * version 2 for more details (a copy is included in the LICENSE file that 2.16 + * accompanied this code). 2.17 + * 2.18 + * You should have received a copy of the GNU General Public License version 2.19 + * 2 along with this work; if not, write to the Free Software Foundation, 2.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.21 + * 2.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.23 + * or visit www.oracle.com if you need additional information or have any 2.24 + * questions. 2.25 + * 2.26 + */ 2.27 + 2.28 +#include "precompiled.hpp" 2.29 +#include "memory/allocation.hpp" 2.30 +#include "memory/resourceArea.hpp" 2.31 +#include "utilities/debug.hpp" 2.32 +#include "utilities/resourceHash.hpp" 2.33 + 2.34 +#ifndef PRODUCT 2.35 + 2.36 +/////////////// Unit tests /////////////// 2.37 + 2.38 +class TestResourceHashtable : public AllStatic { 2.39 + typedef void* K; 2.40 + typedef int V; 2.41 + 2.42 + static unsigned identity_hash(const K& k) { 2.43 + return (unsigned)(uintptr_t)k; 2.44 + } 2.45 + 2.46 + static unsigned bad_hash(const K& k) { 2.47 + return 1; 2.48 + } 2.49 + 2.50 + class EqualityTestIter { 2.51 + public: 2.52 + bool do_entry(K const& k, V const& v) { 2.53 + assert((uintptr_t)k == (uintptr_t)v, ""); 2.54 + return true; // continue iteration 2.55 + } 2.56 + }; 2.57 + 2.58 + template< 2.59 + unsigned (*HASH) (K const&) = primitive_hash<K>, 2.60 + bool (*EQUALS)(K const&, K const&) = primitive_equals<K>, 2.61 + unsigned SIZE = 256, 2.62 + ResourceObj::allocation_type ALLOC_TYPE = ResourceObj::RESOURCE_AREA, 2.63 + MEMFLAGS MEM_TYPE = mtInternal 2.64 + > 2.65 + class Runner : public AllStatic { 2.66 + static void* as_K(uintptr_t val) { return (void*)val; } 2.67 + 2.68 + public: 2.69 + static void test_small() { 2.70 + EqualityTestIter et; 2.71 + ResourceHashtable<K, V, HASH, EQUALS, SIZE, ALLOC_TYPE, MEM_TYPE> rh; 2.72 + 2.73 + assert(!rh.contains(as_K(0x1)), ""); 2.74 + 2.75 + assert(rh.put(as_K(0x1), 0x1), ""); 2.76 + assert(rh.contains(as_K(0x1)), ""); 2.77 + 2.78 + assert(!rh.put(as_K(0x1), 0x1), ""); 2.79 + 2.80 + assert(rh.put(as_K(0x2), 0x2), ""); 2.81 + assert(rh.put(as_K(0x3), 0x3), ""); 2.82 + assert(rh.put(as_K(0x4), 0x4), ""); 2.83 + assert(rh.put(as_K(0x5), 0x5), ""); 2.84 + 2.85 + assert(!rh.remove(as_K(0x0)), ""); 2.86 + rh.iterate(&et); 2.87 + 2.88 + assert(rh.remove(as_K(0x1)), ""); 2.89 + rh.iterate(&et); 2.90 + 2.91 + } 2.92 + 2.93 + // We use keys with the low bits cleared since the default hash will do some shifting 2.94 + static void test_small_shifted() { 2.95 + EqualityTestIter et; 2.96 + ResourceHashtable<K, V, HASH, EQUALS, SIZE, ALLOC_TYPE, MEM_TYPE> rh; 2.97 + 2.98 + assert(!rh.contains(as_K(0x10)), ""); 2.99 + 2.100 + assert(rh.put(as_K(0x10), 0x10), ""); 2.101 + assert(rh.contains(as_K(0x10)), ""); 2.102 + 2.103 + assert(!rh.put(as_K(0x10), 0x10), ""); 2.104 + 2.105 + assert(rh.put(as_K(0x20), 0x20), ""); 2.106 + assert(rh.put(as_K(0x30), 0x30), ""); 2.107 + assert(rh.put(as_K(0x40), 0x40), ""); 2.108 + assert(rh.put(as_K(0x50), 0x50), ""); 2.109 + 2.110 + assert(!rh.remove(as_K(0x00)), ""); 2.111 + 2.112 + assert(rh.remove(as_K(0x10)), ""); 2.113 + 2.114 + rh.iterate(&et); 2.115 + } 2.116 + 2.117 + static void test(unsigned num_elements = SIZE) { 2.118 + EqualityTestIter et; 2.119 + ResourceHashtable<K, V, HASH, EQUALS, SIZE, ALLOC_TYPE, MEM_TYPE> rh; 2.120 + 2.121 + for (uintptr_t i = 0; i < num_elements; ++i) { 2.122 + assert(rh.put(as_K(i), i), ""); 2.123 + } 2.124 + 2.125 + rh.iterate(&et); 2.126 + 2.127 + for (uintptr_t i = num_elements; i > 0; --i) { 2.128 + uintptr_t index = i - 1; 2.129 + assert(rh.remove(as_K(index)), ""); 2.130 + } 2.131 + rh.iterate(&et); 2.132 + for (uintptr_t i = num_elements; i > 0; --i) { 2.133 + uintptr_t index = i - 1; 2.134 + assert(!rh.remove(as_K(index)), ""); 2.135 + } 2.136 + rh.iterate(&et); 2.137 + } 2.138 + }; 2.139 + 2.140 + public: 2.141 + static void run_tests() { 2.142 + { 2.143 + ResourceMark rm; 2.144 + Runner<>::test_small(); 2.145 + Runner<>::test_small_shifted(); 2.146 + Runner<>::test(); 2.147 + } 2.148 + 2.149 + { 2.150 + ResourceMark rm; 2.151 + Runner<identity_hash>::test_small(); 2.152 + Runner<identity_hash>::test_small_shifted(); 2.153 + Runner<identity_hash>::test(); 2.154 + } 2.155 + 2.156 + { 2.157 + ResourceMark rm; 2.158 + Runner<bad_hash>::test_small(); 2.159 + Runner<bad_hash>::test_small_shifted(); 2.160 + Runner<bad_hash>::test(); 2.161 + } 2.162 + 2.163 + 2.164 + assert(Thread::current()->resource_area()->nesting() == 0, "this code depends on not having an active ResourceMark"); 2.165 + // The following test calls will cause an assert if resource allocations occur since we don't have an active mark 2.166 + Runner<primitive_hash<K>, primitive_equals<K>, 512, ResourceObj::C_HEAP>::test_small(); 2.167 + Runner<primitive_hash<K>, primitive_equals<K>, 512, ResourceObj::C_HEAP>::test_small_shifted(); 2.168 + Runner<primitive_hash<K>, primitive_equals<K>, 512, ResourceObj::C_HEAP>::test(); 2.169 + 2.170 + Runner<bad_hash, primitive_equals<K>, 512, ResourceObj::C_HEAP>::test_small(); 2.171 + Runner<bad_hash, primitive_equals<K>, 512, ResourceObj::C_HEAP>::test_small_shifted(); 2.172 + Runner<bad_hash, primitive_equals<K>, 512, ResourceObj::C_HEAP>::test(); 2.173 + 2.174 + Runner<identity_hash, primitive_equals<K>, 1, ResourceObj::C_HEAP>::test_small(); 2.175 + Runner<identity_hash, primitive_equals<K>, 1, ResourceObj::C_HEAP>::test_small_shifted(); 2.176 + Runner<identity_hash, primitive_equals<K>, 1, ResourceObj::C_HEAP>::test(512); 2.177 + } 2.178 +}; 2.179 + 2.180 +void TestResourcehash_test() { 2.181 + TestResourceHashtable::run_tests(); 2.182 +} 2.183 + 2.184 +#endif // not PRODUCT 2.185 +
3.1 --- a/src/share/vm/utilities/resourceHash.hpp Thu Feb 27 06:05:11 2020 +0000 3.2 +++ b/src/share/vm/utilities/resourceHash.hpp Thu Feb 27 06:41:35 2020 +0000 3.3 @@ -1,5 +1,5 @@ 3.4 /* 3.5 - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 3.6 + * Copyright (c) 2012, 2015 Oracle and/or its affiliates. All rights reserved. 3.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.8 * 3.9 * This code is free software; you can redistribute it and/or modify it 3.10 @@ -35,7 +35,7 @@ 3.11 3.12 template<typename K> unsigned primitive_hash(const K& k) { 3.13 unsigned hash = (unsigned)((uintptr_t)k); 3.14 - return hash ^ (hash > 3); // just in case we're dealing with aligned ptrs 3.15 + return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs 3.16 } 3.17 3.18 template<typename K> bool primitive_equals(const K& k0, const K& k1) { 3.19 @@ -50,7 +50,9 @@ 3.20 //typename ResourceHashtableFns<K>::equals_fn EQUALS = primitive_equals<K>, 3.21 unsigned (*HASH) (K const&) = primitive_hash<K>, 3.22 bool (*EQUALS)(K const&, K const&) = primitive_equals<K>, 3.23 - unsigned SIZE = 256 3.24 + unsigned SIZE = 256, 3.25 + ResourceObj::allocation_type ALLOC_TYPE = ResourceObj::RESOURCE_AREA, 3.26 + MEMFLAGS MEM_TYPE = mtInternal 3.27 > 3.28 class ResourceHashtable : public ResourceObj { 3.29 private: 3.30 @@ -91,6 +93,21 @@ 3.31 public: 3.32 ResourceHashtable() { memset(_table, 0, SIZE * sizeof(Node*)); } 3.33 3.34 + ~ResourceHashtable() { 3.35 + if (ALLOC_TYPE == C_HEAP) { 3.36 + Node* const* bucket = _table; 3.37 + while (bucket < &_table[SIZE]) { 3.38 + Node* node = *bucket; 3.39 + while (node != NULL) { 3.40 + Node* cur = node; 3.41 + node = node->_next; 3.42 + delete cur; 3.43 + } 3.44 + ++bucket; 3.45 + } 3.46 + } 3.47 + } 3.48 + 3.49 bool contains(K const& key) const { 3.50 return get(key) != NULL; 3.51 } 3.52 @@ -105,17 +122,38 @@ 3.53 } 3.54 } 3.55 3.56 - // Inserts or replaces a value in the table 3.57 - void put(K const& key, V const& value) { 3.58 + /** 3.59 + * Inserts or replaces a value in the table. 3.60 + * @return: true: if a new item is added 3.61 + * false: if the item already existed and the value is updated 3.62 + */ 3.63 + bool put(K const& key, V const& value) { 3.64 unsigned hv = HASH(key); 3.65 Node** ptr = lookup_node(hv, key); 3.66 if (*ptr != NULL) { 3.67 (*ptr)->_value = value; 3.68 + return false; 3.69 } else { 3.70 - *ptr = new Node(hv, key, value); 3.71 + *ptr = new (ALLOC_TYPE, MEM_TYPE) Node(hv, key, value); 3.72 + return true; 3.73 } 3.74 } 3.75 3.76 + bool remove(K const& key) { 3.77 + unsigned hv = HASH(key); 3.78 + Node** ptr = lookup_node(hv, key); 3.79 + 3.80 + Node* node = *ptr; 3.81 + if (node != NULL) { 3.82 + *ptr = node->_next; 3.83 + if (ALLOC_TYPE == C_HEAP) { 3.84 + delete node; 3.85 + } 3.86 + return true; 3.87 + } 3.88 + return false; 3.89 + } 3.90 + 3.91 // ITER contains bool do_entry(K const&, V const&), which will be 3.92 // called for each entry in the table. If do_entry() returns false, 3.93 // the iteration is cancelled. 3.94 @@ -132,6 +170,10 @@ 3.95 ++bucket; 3.96 } 3.97 } 3.98 + 3.99 + static size_t node_size() { 3.100 + return sizeof(Node); 3.101 + } 3.102 }; 3.103 3.104