8233378: CHT: Fast reset

Reviewed-by: tschatzl, rehn
diff --git a/src/hotspot/share/utilities/concurrentHashTable.hpp b/src/hotspot/share/utilities/concurrentHashTable.hpp
index 154672e..fb85402 100644
--- a/src/hotspot/share/utilities/concurrentHashTable.hpp
+++ b/src/hotspot/share/utilities/concurrentHashTable.hpp
@@ -292,6 +292,7 @@
   void internal_shrink_epilog(Thread* thread);
   void internal_shrink_range(Thread* thread, size_t start, size_t stop);
   bool internal_shrink(Thread* thread, size_t size_limit_log2);
+  void internal_reset(size_t log2_size);
 
   // Methods for growing.
   bool unzip_bucket(Thread* thread, InternalTable* old_table,
@@ -388,6 +389,10 @@
   // Re-size operations.
   bool shrink(Thread* thread, size_t size_limit_log2 = 0);
   bool grow(Thread* thread, size_t size_limit_log2 = 0);
+  // Unsafe reset and resize the table. This method assumes that we
+  // want to clear and maybe resize the internal table without the
+  // overhead of clearing individual items in the table.
+  void unsafe_reset(size_t size_log2 = 0);
 
   // All callbacks for get are under critical sections. Other callbacks may be
   // under critical section or may have locked parts of table. Calling any
diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp
index 599fe9b..698c0c6 100644
--- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp
+++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp
@@ -782,6 +782,20 @@
 }
 
 template <typename CONFIG, MEMFLAGS F>
+inline void ConcurrentHashTable<CONFIG, F>::
+  internal_reset(size_t log2_size)
+{
+  assert(_table != NULL, "table failed");
+  assert(_log2_size_limit >= log2_size, "bad ergo");
+
+  delete _table;
+  // Create and publish a new table
+  InternalTable* table = new InternalTable(log2_size);
+  _size_limit_reached = (log2_size == _log2_size_limit);
+  Atomic::release_store(&_table, table);
+}
+
+template <typename CONFIG, MEMFLAGS F>
 inline bool ConcurrentHashTable<CONFIG, F>::
   internal_grow_prolog(Thread* thread, size_t log2_size)
 {
@@ -1033,6 +1047,14 @@
 }
 
 template <typename CONFIG, MEMFLAGS F>
+inline void ConcurrentHashTable<CONFIG, F>::
+  unsafe_reset(size_t size_log2)
+{
+  size_t tmp = size_log2 == 0 ? _log2_start_size : size_log2;
+  internal_reset(tmp);
+}
+
+template <typename CONFIG, MEMFLAGS F>
 inline bool ConcurrentHashTable<CONFIG, F>::
   grow(Thread* thread, size_t size_limit_log2)
 {
diff --git a/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp b/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp
index 04d716f..6a855d3 100644
--- a/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp
+++ b/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp
@@ -49,8 +49,46 @@
   }
 };
 
+struct Config : public AllStatic {
+  typedef uintptr_t Value;
+  struct TableElement{
+    TableElement * volatile _next;
+    Value _value;
+  };
+
+  static const uint nelements = 5;
+  static TableElement* elements;
+  static uint cur_index;
+
+  static uintx get_hash(const Value& value, bool* dead_hash) {
+    return (uintx)value;
+  }
+  static void initialize() {
+    elements = (TableElement*)::malloc(nelements * sizeof(TableElement));
+  }
+  static void* allocate_node(size_t size, const Value& value) {
+    return (void*)&elements[cur_index++];
+  }
+
+  static void free_node(void* memory, const Value& value) {
+    return;
+  }
+
+  static void reset() {
+    cur_index = 0;
+  }
+
+  static void bulk_free() {
+    ::free(elements);
+  }
+};
+
+Config::TableElement* Config::elements = nullptr;
+uint Config::cur_index = 0;
+
 typedef ConcurrentHashTable<Pointer, mtInternal> SimpleTestTable;
 typedef ConcurrentHashTable<Pointer, mtInternal>::MultiGetHandle SimpleTestGetHandle;
+typedef ConcurrentHashTable<Config, mtInternal> CustomTestTable;
 
 struct SimpleTestLookup {
   uintptr_t _val;
@@ -75,20 +113,23 @@
   }
 };
 
-static uintptr_t cht_get_copy(SimpleTestTable* cht, Thread* thr, SimpleTestLookup stl) {
+template <typename T=SimpleTestTable>
+static uintptr_t cht_get_copy(T* cht, Thread* thr, SimpleTestLookup stl) {
   ValueGet vg;
   cht->get(thr, stl, vg);
   return vg.get_value();
 }
 
-static void cht_find(Thread* thr, SimpleTestTable* cht, uintptr_t val) {
+template <typename T=SimpleTestTable>
+static void cht_find(Thread* thr, T* cht, uintptr_t val) {
   SimpleTestLookup stl(val);
   ValueGet vg;
   EXPECT_EQ(cht->get(thr, stl, vg), true) << "Getting an old value failed.";
   EXPECT_EQ(val, vg.get_value()) << "Getting an old value failed.";
 }
 
-static void cht_insert_and_find(Thread* thr, SimpleTestTable* cht, uintptr_t val) {
+template <typename T=SimpleTestTable>
+static void cht_insert_and_find(Thread* thr, T* cht, uintptr_t val) {
   SimpleTestLookup stl(val);
   EXPECT_EQ(cht->insert(thr, stl, val), true) << "Inserting an unique value failed.";
   cht_find(thr, cht, val);
@@ -220,6 +261,32 @@
   delete cht;
 }
 
+static void cht_reset_shrink(Thread* thr) {
+  uintptr_t val1 = 1;
+  uintptr_t val2 = 2;
+  uintptr_t val3 = 3;
+  SimpleTestLookup stl1(val1), stl2(val2), stl3(val3);
+
+  Config::initialize();
+  CustomTestTable* cht = new CustomTestTable();
+
+  cht_insert_and_find(thr, cht, val1);
+  cht_insert_and_find(thr, cht, val2);
+  cht_insert_and_find(thr, cht, val3);
+
+  cht->unsafe_reset();
+  Config::reset();
+
+  EXPECT_EQ(cht_get_copy(cht, thr, stl1), (uintptr_t)0) << "Table should have been reset";
+  // Re-inserted values should not be considered duplicates; table was reset.
+  cht_insert_and_find(thr, cht, val1);
+  cht_insert_and_find(thr, cht, val2);
+  cht_insert_and_find(thr, cht, val3);
+
+  delete cht;
+  Config::bulk_free();
+}
+
 static void cht_scope(Thread* thr) {
   uintptr_t val = 0x2;
   SimpleTestLookup stl(val);
@@ -394,6 +461,10 @@
   nomt_test_doer(cht_getinsert_bulkdelete_task);
 }
 
+TEST_VM(ConcurrentHashTable, basic_reset_shrink) {
+  nomt_test_doer(cht_reset_shrink);
+}
+
 TEST_VM(ConcurrentHashTable, basic_scan) {
   nomt_test_doer(cht_scan);
 }