|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * This code is free software; you can redistribute it and/or modify it
|
|
35 | 35 | #include "oops/oop.inline.hpp"
|
36 | 36 | #include "oops/weakHandle.inline.hpp"
|
37 | 37 | #include "runtime/atomic.hpp"
|
| 38 | +#include "runtime/mutexLocker.hpp" |
38 | 39 | #include "utilities/growableArray.hpp"
|
39 |
| -#include "utilities/hashtable.inline.hpp" |
| 40 | +#include "utilities/resourceHash.hpp" |
40 | 41 |
|
41 |
| -unsigned int ProtectionDomainCacheTable::compute_hash(Handle protection_domain) { |
42 |
| - // Identity hash can safepoint, so keep protection domain in a Handle. |
43 |
| - return (unsigned int)(protection_domain->identity_hash()); |
| 42 | +unsigned int ProtectionDomainCacheTable::compute_hash(const WeakHandle& protection_domain) { |
| 43 | + // The protection domain in the hash computation is passed from a Handle so cannot resolve to null. |
| 44 | + assert(protection_domain.peek() != nullptr, "Must be live"); |
| 45 | + return (unsigned int)(protection_domain.resolve()->identity_hash()); |
44 | 46 | }
|
45 | 47 |
|
46 |
| -int ProtectionDomainCacheTable::index_for(Handle protection_domain) { |
47 |
| - return hash_to_index(compute_hash(protection_domain)); |
| 48 | +bool ProtectionDomainCacheTable::equals(const WeakHandle& protection_domain1, const WeakHandle& protection_domain2) { |
| 49 | + return protection_domain1.peek() == protection_domain2.peek(); |
48 | 50 | }
|
49 | 51 |
|
50 |
| -ProtectionDomainCacheTable::ProtectionDomainCacheTable(int table_size) |
51 |
| - : Hashtable<WeakHandle, mtClass>(table_size, sizeof(ProtectionDomainCacheEntry)) |
52 |
| -{ _dead_entries = false; |
53 |
| - _total_oops_removed = 0; |
54 |
| -} |
| 52 | +// WeakHandle is both the key and the value. We need it as the key to compare the oops that each point to |
| 53 | +// for equality. We need it as the value to return the one that already exists to link in the DictionaryEntry. |
| 54 | +ResourceHashtable<WeakHandle, WeakHandle, 1009, ResourceObj::C_HEAP, mtClass, |
| 55 | + ProtectionDomainCacheTable::compute_hash, |
| 56 | + ProtectionDomainCacheTable::equals> _pd_cache_table; |
| 57 | + |
| 58 | +bool ProtectionDomainCacheTable::_dead_entries = false; |
| 59 | +int ProtectionDomainCacheTable::_total_oops_removed = 0; |
55 | 60 |
|
56 | 61 | void ProtectionDomainCacheTable::trigger_cleanup() {
|
57 | 62 | MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
|
@@ -129,107 +134,91 @@ void ProtectionDomainCacheTable::unlink() {
|
129 | 134 | // Purge any deleted entries outside of the SystemDictionary_lock.
|
130 | 135 | purge_deleted_entries();
|
131 | 136 |
|
| 137 | + // Reacquire the lock to remove entries from the hashtable. |
132 | 138 | MutexLocker ml(SystemDictionary_lock);
|
133 |
| - int oops_removed = 0; |
134 |
| - for (int i = 0; i < table_size(); ++i) { |
135 |
| - ProtectionDomainCacheEntry** p = bucket_addr(i); |
136 |
| - ProtectionDomainCacheEntry* entry = bucket(i); |
137 |
| - while (entry != NULL) { |
138 |
| - oop pd = entry->object_no_keepalive(); |
139 |
| - if (pd != NULL) { |
140 |
| - p = entry->next_addr(); |
141 |
| - } else { |
142 |
| - oops_removed++; |
| 139 | + |
| 140 | + struct Deleter { |
| 141 | + int _oops_removed; |
| 142 | + Deleter() : _oops_removed(0) {} |
| 143 | + |
| 144 | + bool do_entry(WeakHandle& key, WeakHandle& value) { |
| 145 | + oop pd = value.peek(); |
| 146 | + if (value.peek() == nullptr) { |
| 147 | + _oops_removed++; |
143 | 148 | LogTarget(Debug, protectiondomain, table) lt;
|
144 | 149 | if (lt.is_enabled()) {
|
145 | 150 | LogStream ls(lt);
|
146 |
| - ls.print_cr("protection domain unlinked at %d", i); |
| 151 | + ls.print_cr("protection domain unlinked %d", _oops_removed); |
147 | 152 | }
|
148 |
| - entry->literal().release(Universe::vm_weak()); |
149 |
| - *p = entry->next(); |
150 |
| - free_entry(entry); |
| 153 | + value.release(Universe::vm_weak()); |
| 154 | + return true; |
| 155 | + } else { |
| 156 | + return false; |
151 | 157 | }
|
152 |
| - entry = *p; |
153 | 158 | }
|
154 |
| - } |
155 |
| - _total_oops_removed += oops_removed; |
| 159 | + }; |
| 160 | + |
| 161 | + Deleter deleter; |
| 162 | + _pd_cache_table.unlink(&deleter); |
| 163 | + |
| 164 | + _total_oops_removed += deleter._oops_removed; |
156 | 165 | _dead_entries = false;
|
157 | 166 | }
|
158 | 167 |
|
159 |
| -void ProtectionDomainCacheTable::print_on(outputStream* st) const { |
| 168 | +void ProtectionDomainCacheTable::print_on(outputStream* st) { |
160 | 169 | assert_locked_or_safepoint(SystemDictionary_lock);
|
161 |
| - st->print_cr("Protection domain cache table (table_size=%d, classes=%d)", |
162 |
| - table_size(), number_of_entries()); |
163 |
| - for (int index = 0; index < table_size(); index++) { |
164 |
| - for (ProtectionDomainCacheEntry* probe = bucket(index); |
165 |
| - probe != NULL; |
166 |
| - probe = probe->next()) { |
167 |
| - st->print_cr("%4d: protection_domain: " PTR_FORMAT, index, p2i(probe->object_no_keepalive())); |
168 |
| - } |
169 |
| - } |
| 170 | + auto printer = [&] (WeakHandle& key, WeakHandle& value) { |
| 171 | + st->print_cr(" protection_domain: " PTR_FORMAT, p2i(value.peek())); |
| 172 | + }; |
| 173 | + st->print_cr("Protection domain cache table (table_size=%d, protection domains=%d)", |
| 174 | + _pd_cache_table.table_size(), _pd_cache_table.number_of_entries()); |
| 175 | + _pd_cache_table.iterate_all(printer); |
170 | 176 | }
|
171 | 177 |
|
172 | 178 | void ProtectionDomainCacheTable::verify() {
|
173 |
| - verify_table<ProtectionDomainCacheEntry>("Protection Domain Table"); |
174 |
| -} |
175 |
| - |
176 |
| -oop ProtectionDomainCacheEntry::object() { |
177 |
| - return literal().resolve(); |
| 179 | + auto verifier = [&] (WeakHandle& key, WeakHandle& value) { |
| 180 | + guarantee(value.peek() == nullptr || oopDesc::is_oop(value.peek()), "must be an oop"); |
| 181 | + }; |
| 182 | + _pd_cache_table.iterate_all(verifier); |
178 | 183 | }
|
179 | 184 |
|
180 | 185 | // The object_no_keepalive() call peeks at the phantomly reachable oop without
|
181 |
| -// keeping it alive. This is okay to do in the VM thread state if it is not |
182 |
| -// leaked out to become strongly reachable. |
183 |
| -oop ProtectionDomainCacheEntry::object_no_keepalive() { |
184 |
| - return literal().peek(); |
185 |
| -} |
186 |
| - |
| 186 | +// keeping it alive. This is used for traversing DictionaryEntry pd_set. |
187 | 187 | oop ProtectionDomainEntry::object_no_keepalive() {
|
188 |
| - return _pd_cache->object_no_keepalive(); |
189 |
| -} |
190 |
| - |
191 |
| -void ProtectionDomainCacheEntry::verify() { |
192 |
| - guarantee(object_no_keepalive() == NULL || oopDesc::is_oop(object_no_keepalive()), "must be an oop"); |
| 188 | + return _object.peek(); |
193 | 189 | }
|
194 | 190 |
|
195 |
| -ProtectionDomainCacheEntry* ProtectionDomainCacheTable::get(Handle protection_domain) { |
196 |
| - unsigned int hash = compute_hash(protection_domain); |
197 |
| - int index = hash_to_index(hash); |
198 |
| - |
199 |
| - ProtectionDomainCacheEntry* entry = find_entry(index, protection_domain); |
200 |
| - if (entry == NULL) { |
201 |
| - entry = add_entry(index, hash, protection_domain); |
202 |
| - } |
203 |
| - // keep entry alive |
204 |
| - (void)entry->object(); |
205 |
| - return entry; |
206 |
| -} |
207 |
| - |
208 |
| -ProtectionDomainCacheEntry* ProtectionDomainCacheTable::find_entry(int index, Handle protection_domain) { |
| 191 | +WeakHandle ProtectionDomainCacheTable::add_if_absent(Handle protection_domain) { |
209 | 192 | assert_locked_or_safepoint(SystemDictionary_lock);
|
210 |
| - for (ProtectionDomainCacheEntry* e = bucket(index); e != NULL; e = e->next()) { |
211 |
| - if (e->object_no_keepalive() == protection_domain()) { |
212 |
| - return e; |
| 193 | + WeakHandle w(Universe::vm_weak(), protection_domain); |
| 194 | + bool created; |
| 195 | + WeakHandle* wk = _pd_cache_table.put_if_absent(w, w, &created); |
| 196 | + if (!created) { |
| 197 | + // delete the one created since we already had it in the table |
| 198 | + w.release(Universe::vm_weak()); |
| 199 | + } else { |
| 200 | + LogTarget(Debug, protectiondomain, table) lt; |
| 201 | + if (lt.is_enabled()) { |
| 202 | + LogStream ls(lt); |
| 203 | + ls.print("protection domain added "); |
| 204 | + protection_domain->print_value_on(&ls); |
| 205 | + ls.cr(); |
213 | 206 | }
|
214 | 207 | }
|
| 208 | + // Keep entry alive |
| 209 | + (void)wk->resolve(); |
| 210 | + return *wk; |
| 211 | +} |
215 | 212 |
|
216 |
| - return NULL; |
| 213 | +void ProtectionDomainCacheTable::print_table_statistics(outputStream* st) { |
| 214 | + auto size = [&] (WeakHandle& key, WeakHandle& value) { |
| 215 | + // The only storage is in OopStorage for an oop |
| 216 | + return sizeof(oop); |
| 217 | + }; |
| 218 | + TableStatistics ts = _pd_cache_table.statistics_calculate(size); |
| 219 | + ts.print(st, "ProtectionDomainCacheTable"); |
217 | 220 | }
|
218 | 221 |
|
219 |
| -ProtectionDomainCacheEntry* ProtectionDomainCacheTable::add_entry(int index, unsigned int hash, Handle protection_domain) { |
220 |
| - assert_locked_or_safepoint(SystemDictionary_lock); |
221 |
| - assert(index == index_for(protection_domain), "incorrect index?"); |
222 |
| - assert(find_entry(index, protection_domain) == NULL, "no double entry"); |
223 |
| - |
224 |
| - LogTarget(Debug, protectiondomain, table) lt; |
225 |
| - if (lt.is_enabled()) { |
226 |
| - LogStream ls(lt); |
227 |
| - ls.print("protection domain added "); |
228 |
| - protection_domain->print_value_on(&ls); |
229 |
| - ls.cr(); |
230 |
| - } |
231 |
| - WeakHandle w(Universe::vm_weak(), protection_domain); |
232 |
| - ProtectionDomainCacheEntry* p = new_entry(hash, w); |
233 |
| - Hashtable<WeakHandle, mtClass>::add_entry(index, p); |
234 |
| - return p; |
| 222 | +int ProtectionDomainCacheTable::number_of_entries() { |
| 223 | + return _pd_cache_table.number_of_entries(); |
235 | 224 | }
|
0 commit comments