1 /*
  2  * Copyright (c) 2016, 2019, 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 #ifndef SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP
 26 #define SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP
 27 
 28 #include "memory/allocation.inline.hpp"
 29 #include "runtime/orderAccess.hpp"
 30 #include "utilities/debug.hpp"
 31 #include "utilities/macros.hpp"
 32 
 33 template <typename T>
 34 class JfrBasicHashtableEntry {
 35  private:
 36   typedef JfrBasicHashtableEntry<T> Entry;
 37   Entry* _next;
 38   T _literal;          // ref to item in table.
 39   uintptr_t _hash;
 40 
 41  public:
 42   uintptr_t hash() const { return _hash; }
 43   void set_hash(uintptr_t hash) { _hash = hash; }
 44   T literal() const { return _literal; }
 45   T* literal_addr() { return &_literal; }
 46   void set_literal(T s) { _literal = s; }
 47   void set_next(Entry* next) { _next = next; }
 48   Entry* next() const { return _next; }
 49   Entry** next_addr() { return &_next; }
 50 };
 51 
 52 template <typename T>
 53 class JfrHashtableBucket : public CHeapObj<mtTracing> {
 54   template <typename>
 55   friend class JfrBasicHashtable;
 56  private:
 57   typedef JfrBasicHashtableEntry<T> TableEntry;
 58   TableEntry* _entry;
 59 
 60   TableEntry* get_entry() const {
 61     return (TableEntry*)OrderAccess::load_acquire(&_entry);
 62   }
 63   void set_entry(TableEntry* entry) { OrderAccess::release_store(&_entry, entry);}
 64   TableEntry** entry_addr() { return &_entry; }
 65 };
 66 
 67 template <typename T>
 68 class JfrBasicHashtable : public CHeapObj<mtTracing> {
 69  private:
 70   typedef JfrHashtableBucket<T> Bucket;
 71   typedef JfrBasicHashtableEntry<T> TableEntry;
 72   Bucket* _buckets;
 73   uintptr_t _table_size;
 74   const size_t _entry_size;
 75   size_t _number_of_entries;
 76 
 77  protected:
 78   JfrBasicHashtable(uintptr_t table_size, size_t entry_size) :
 79     _buckets(NULL), _table_size(table_size), _entry_size(entry_size), _number_of_entries(0) {
 80     _buckets = NEW_C_HEAP_ARRAY2(Bucket, table_size, mtTracing, CURRENT_PC);
 81     memset((void*)_buckets, 0, table_size * sizeof(Bucket));
 82   }
 83 
 84   size_t hash_to_index(uintptr_t full_hash) const {
 85     const uintptr_t h = full_hash % _table_size;
 86     assert(h >= 0 && h < _table_size, "Illegal hash value");
 87     return (size_t)h;
 88   }
 89   size_t entry_size() const { return _entry_size; }
 90   void unlink_entry(TableEntry* entry) {
 91     entry->set_next(NULL);
 92     --_number_of_entries;
 93   }
 94   void free_buckets() {
 95     FREE_C_HEAP_ARRAY(Bucket, _buckets);
 96     _buckets = NULL;
 97   }
 98   TableEntry* bucket(size_t i) { return _buckets[i].get_entry();}
 99   TableEntry** bucket_addr(size_t i) { return _buckets[i].entry_addr(); }
100   uintptr_t table_size() const { return _table_size; }
101   size_t number_of_entries() const { return _number_of_entries; }
102   void add_entry(size_t index, TableEntry* entry) {
103     assert(entry != NULL, "invariant");
104     entry->set_next(bucket(index));
105     _buckets[index].set_entry(entry);
106     ++_number_of_entries;
107   }
108 };
109 
110 template <typename IdType, typename Entry, typename T>
111 class AscendingId : public CHeapObj<mtTracing>  {
112  private:
113   IdType _id;
114  public:
115   AscendingId() : _id(0) {}
116   // callbacks
117   void assign_id(Entry* entry) {
118     assert(entry != NULL, "invariant");
119     assert(entry->id() == 0, "invariant");
120     entry->set_id(++_id);
121   }
122   bool equals(const T& data, uintptr_t hash, const Entry* entry) {
123     assert(entry->hash() == hash, "invariant");
124     return true;
125   }
126 };
127 
128 // IdType must be scalar
129 template <typename T, typename IdType>
130 class Entry : public JfrBasicHashtableEntry<T> {
131  public:
132   typedef IdType ID;
133   void init() { _id = 0; }
134   ID id() const { return _id; }
135   void set_id(ID id) { _id = id; }
136   void set_value(const T& value) { this->set_literal(value); }
137   T& value() const { return *const_cast<Entry*>(this)->literal_addr();}
138   const T* value_addr() const { return const_cast<Entry*>(this)->literal_addr(); }
139 
140  private:
141   ID _id;
142 };
143 
144 template <typename T, typename IdType, template <typename, typename> class Entry,
145           typename Callback = AscendingId<IdType, Entry<T, IdType>, T> ,
146           size_t TABLE_SIZE = 1009>
147 class HashTableHost : public JfrBasicHashtable<T> {
148  public:
149   typedef Entry<T, IdType> HashEntry;
150   HashTableHost() : _callback(new Callback()) {}
151   HashTableHost(Callback* cb) : JfrBasicHashtable<T>(TABLE_SIZE, sizeof(HashEntry)), _callback(cb) {}
152   ~HashTableHost() {
153     this->clear_entries();
154     this->free_buckets();
155   }
156 
157   // direct insert assumes non-existing entry
158   HashEntry& put(const T& data, uintptr_t hash);
159 
160   // lookup entry, will put if not found
161   HashEntry& lookup_put(const T& data, uintptr_t hash) {
162     HashEntry* entry = lookup_only(data, hash);
163     return entry == NULL ? put(data, hash) : *entry;
164   }
165 
166   // read-only lookup
167   HashEntry* lookup_only(const T& query, uintptr_t hash);
168 
169   // id retrieval
170   IdType id(const T& data, uintptr_t hash) {
171     assert(data != NULL, "invariant");
172     const HashEntry& entry = lookup_put(data, hash);
173     assert(entry.id() > 0, "invariant");
174     return entry.id();
175   }
176 
177   template <typename Functor>
178   void iterate_value(Functor& f);
179 
180   template <typename Functor>
181   void iterate_entry(Functor& f);
182 
183   size_t cardinality() const { return this->number_of_entries(); }
184   bool has_entries() const { return this->cardinality() > 0; }
185   void clear_entries();
186 
187   // removal and deallocation
188   void free_entry(HashEntry* entry) {
189     assert(entry != NULL, "invariant");
190     JfrBasicHashtable<T>::unlink_entry(entry);
191     FREE_C_HEAP_ARRAY(char, entry);
192   }
193 
194  private:
195   Callback* _callback;
196   size_t index_for(uintptr_t hash) { return this->hash_to_index(hash); }
197   HashEntry* new_entry(const T& data, uintptr_t hash);
198   void add_entry(size_t index, HashEntry* new_entry) {
199     assert(new_entry != NULL, "invariant");
200     _callback->assign_id(new_entry);
201     assert(new_entry->id() > 0, "invariant");
202     JfrBasicHashtable<T>::add_entry(index, new_entry);
203   }
204 };
205 
206 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
207 Entry<T, IdType>& HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::put(const T& data, uintptr_t hash) {
208   assert(lookup_only(data, hash) == NULL, "use lookup_put()");
209   HashEntry* const entry = new_entry(data, hash);
210   add_entry(index_for(hash), entry);
211   return *entry;
212 }
213 
214 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
215 Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::lookup_only(const T& query, uintptr_t hash) {
216   HashEntry* entry = (HashEntry*)this->bucket(index_for(hash));
217   while (entry != NULL) {
218     if (entry->hash() == hash && _callback->equals(query, hash, entry)) {
219       return entry;
220     }
221     entry = (HashEntry*)entry->next();
222   }
223   return NULL;
224 }
225 
226 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
227 template <typename Functor>
228 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::iterate_value(Functor& f) {
229   for (size_t i = 0; i < this->table_size(); ++i) {
230     const HashEntry* entry = (const HashEntry*)this->bucket(i);
231     while (entry != NULL) {
232       if (!f(entry->value())) {
233         break;
234       }
235       entry = (HashEntry*)entry->next();
236     }
237   }
238 }
239 
240 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
241 template <typename Functor>
242 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::iterate_entry(Functor& f) {
243   for (size_t i = 0; i < this->table_size(); ++i) {
244     const HashEntry* entry = (const HashEntry*)this->bucket(i);
245     while (entry != NULL) {
246       if (!f(entry)) {
247         break;
248       }
249       entry = (const HashEntry*)entry->next();
250     }
251   }
252 }
253 
254 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
255 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::clear_entries() {
256   for (size_t i = 0; i < this->table_size(); ++i) {
257     HashEntry** bucket = (HashEntry**)this->bucket_addr(i);
258     HashEntry* entry = *bucket;
259     while (entry != NULL) {
260       HashEntry* entry_to_remove = entry;
261       entry = (HashEntry*)entry->next();
262       this->free_entry(entry_to_remove);
263     }
264     *bucket = NULL;
265   }
266   assert(this->number_of_entries() == 0, "should have removed all entries");
267 }
268 
269 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
270 Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::new_entry(const T& data, uintptr_t hash) {
271   assert(sizeof(HashEntry) == this->entry_size(), "invariant");
272   HashEntry* const entry = (HashEntry*) NEW_C_HEAP_ARRAY2(char, this->entry_size(), mtTracing, CURRENT_PC);
273   entry->init();
274   entry->set_hash(hash);
275   entry->set_value(data);
276   entry->set_next(NULL);
277   assert(0 == entry->id(), "invariant");
278   return entry;
279 }
280 
281 #endif // SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP