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 "jfr/utilities/jfrAllocation.hpp"
  29 #include "runtime/atomic.hpp"
  30 #include "utilities/debug.hpp"
  31 #include "utilities/macros.hpp"
  32 
  33 template <typename T>
  34 class JfrBasicHashtableEntry : public JfrCHeapObj {
  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   JfrBasicHashtableEntry(uintptr_t hash, const T& data) : _next(NULL), _literal(data), _hash(hash) {}
  43   uintptr_t hash() const { return _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*)Atomic::load_acquire(&_entry);
  62   }
  63   void set_entry(TableEntry* entry) { Atomic::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   }
  97   TableEntry* bucket(size_t i) { return _buckets[i].get_entry();}
  98   TableEntry** bucket_addr(size_t i) { return _buckets[i].entry_addr(); }
  99   uintptr_t table_size() const { return _table_size; }
 100   size_t number_of_entries() const { return _number_of_entries; }
 101   void add_entry(size_t index, TableEntry* entry) {
 102     assert(entry != NULL, "invariant");
 103     entry->set_next(bucket(index));
 104     _buckets[index].set_entry(entry);
 105     ++_number_of_entries;
 106   }
 107 };
 108 
 109 template <typename IdType, typename Entry, typename T>
 110 class AscendingId : public JfrCHeapObj  {
 111  private:
 112   IdType _id;
 113  public:
 114   AscendingId() : _id(0) {}
 115   // callbacks
 116   void on_link(Entry* entry) {
 117     assert(entry != NULL, "invariant");
 118     assert(entry->id() == 0, "invariant");
 119     entry->set_id(++_id);
 120   }
 121   bool on_equals(uintptr_t hash, const Entry* entry) {
 122     assert(entry->hash() == hash, "invariant");
 123     return true;
 124   }
 125 };
 126 
 127 // IdType must be scalar
 128 template <typename T, typename IdType>
 129 class JfrHashtableEntry : public JfrBasicHashtableEntry<T> {
 130  public:
 131   JfrHashtableEntry(uintptr_t hash, const T& data) : JfrBasicHashtableEntry<T>(hash, data), _id(0) {}
 132   typedef IdType ID;
 133   ID id() const { return _id; }
 134   void set_id(ID id) const { _id = id; }
 135   T& value() const { return *const_cast<JfrHashtableEntry*>(this)->literal_addr();}
 136   const T* value_addr() const { return const_cast<JfrHashtableEntry*>(this)->literal_addr(); }
 137  private:
 138   mutable ID _id;
 139 };
 140 
 141 template <typename T, typename IdType, template <typename, typename> class Entry,
 142           typename Callback = AscendingId<IdType, Entry<T, IdType>, T> ,
 143           size_t TABLE_SIZE = 1009>
 144 class HashTableHost : public JfrBasicHashtable<T> {
 145  public:
 146   typedef Entry<T, IdType> HashEntry;
 147   HashTableHost(size_t size = 0) : JfrBasicHashtable<T>(size == 0 ? TABLE_SIZE : size, sizeof(HashEntry)), _callback(new Callback()) {}
 148   HashTableHost(Callback* cb, size_t size = 0) : JfrBasicHashtable<T>(size == 0 ? TABLE_SIZE : size, sizeof(HashEntry)), _callback(cb) {}
 149   ~HashTableHost() {
 150     this->clear_entries();
 151     this->free_buckets();
 152   }
 153 
 154   // direct insert assumes non-existing entry
 155   HashEntry& put(uintptr_t hash, const T& data);
 156 
 157   // lookup entry, will put if not found
 158   HashEntry& lookup_put(uintptr_t hash, const T& data) {
 159     HashEntry* entry = lookup_only(hash);
 160     return entry == NULL ? put(hash, data) : *entry;
 161   }
 162 
 163   HashEntry* lookup_only(uintptr_t hash);
 164 
 165   // id retrieval
 166   IdType id(uintptr_t hash, const T& data) {
 167     assert(data != NULL, "invariant");
 168     const HashEntry& entry = lookup_put(hash, data);
 169     assert(entry.id() > 0, "invariant");
 170     return entry.id();
 171   }
 172 
 173   template <typename Functor>
 174   void iterate_value(Functor& f);
 175 
 176   template <typename Functor>
 177   void iterate_entry(Functor& f);
 178 
 179   size_t cardinality() const { return this->number_of_entries(); }
 180   bool has_entries() const { return this->cardinality() > 0; }
 181   void clear_entries();
 182 
 183   // removal and deallocation
 184   void free_entry(HashEntry* entry) {
 185     assert(entry != NULL, "invariant");
 186     JfrBasicHashtable<T>::unlink_entry(entry);
 187     _callback->on_unlink(entry);
 188     delete entry;
 189   }
 190 
 191  private:
 192   Callback* _callback;
 193   size_t index_for(uintptr_t hash) { return this->hash_to_index(hash); }
 194   HashEntry* new_entry(uintptr_t hash, const T& data);
 195   void add_entry(size_t index, HashEntry* new_entry) {
 196     assert(new_entry != NULL, "invariant");
 197     _callback->on_link(new_entry);
 198     assert(new_entry->id() > 0, "invariant");
 199     JfrBasicHashtable<T>::add_entry(index, new_entry);
 200   }
 201 };
 202 
 203 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
 204 Entry<T, IdType>& HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::put(uintptr_t hash, const T& data) {
 205   assert(lookup_only(hash) == NULL, "use lookup_put()");
 206   HashEntry* const entry = new_entry(hash, data);
 207   add_entry(index_for(hash), entry);
 208   return *entry;
 209 }
 210 
 211 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
 212 Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::lookup_only(uintptr_t hash) {
 213   HashEntry* entry = (HashEntry*)this->bucket(index_for(hash));
 214   while (entry != NULL) {
 215     if (entry->hash() == hash && _callback->on_equals(hash, entry)) {
 216       return entry;
 217     }
 218     entry = (HashEntry*)entry->next();
 219   }
 220   return NULL;
 221 }
 222 
 223 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
 224 template <typename Functor>
 225 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::iterate_value(Functor& f) {
 226   for (size_t i = 0; i < this->table_size(); ++i) {
 227     const HashEntry* entry = (const HashEntry*)this->bucket(i);
 228     while (entry != NULL) {
 229       if (!f(entry->value())) {
 230         break;
 231       }
 232       entry = (HashEntry*)entry->next();
 233     }
 234   }
 235 }
 236 
 237 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
 238 template <typename Functor>
 239 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::iterate_entry(Functor& f) {
 240   for (size_t i = 0; i < this->table_size(); ++i) {
 241     const HashEntry* entry = (const HashEntry*)this->bucket(i);
 242     while (entry != NULL) {
 243       if (!f(entry)) {
 244         break;
 245       }
 246       entry = (const HashEntry*)entry->next();
 247     }
 248   }
 249 }
 250 
 251 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
 252 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::clear_entries() {
 253   for (size_t i = 0; i < this->table_size(); ++i) {
 254     HashEntry** bucket = (HashEntry**)this->bucket_addr(i);
 255     HashEntry* entry = *bucket;
 256     while (entry != NULL) {
 257       HashEntry* entry_to_remove = entry;
 258       entry = (HashEntry*)entry->next();
 259       this->free_entry(entry_to_remove);
 260     }
 261     *bucket = NULL;
 262   }
 263   assert(this->number_of_entries() == 0, "should have removed all entries");
 264 }
 265 
 266 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
 267 Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::new_entry(uintptr_t hash, const T& data) {
 268   assert(sizeof(HashEntry) == this->entry_size(), "invariant");
 269   HashEntry* const entry = new HashEntry(hash, data);
 270   assert(entry != NULL, "invariant");
 271   assert(0 == entry->id(), "invariant");
 272   return entry;
 273 }
 274 
 275 #endif // SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP