8195099: Concurrent safe-memory-reclamation mechanism jdk-11+10
authorrehn
Wed, 18 Apr 2018 09:25:51 +0200
changeset 49800 69d7398038c5
parent 49799 33dcb9c42f55
child 49801 466d4df248a6
8195099: Concurrent safe-memory-reclamation mechanism Summary: This implement a globalcounter with RCU semantics. Reviewed-by: acorn, coleenp, dcubed, eosterlund, gziemski, mlarsson, kbarrett, dholmes
src/hotspot/share/runtime/thread.cpp
src/hotspot/share/runtime/thread.hpp
src/hotspot/share/utilities/globalCounter.cpp
src/hotspot/share/utilities/globalCounter.hpp
src/hotspot/share/utilities/globalCounter.inline.hpp
test/hotspot/gtest/utilities/test_globalCounter.cpp
test/hotspot/gtest/utilities/utilitiesHelper.inline.hpp
--- a/src/hotspot/share/runtime/thread.cpp	Tue Apr 17 23:27:41 2018 -0700
+++ b/src/hotspot/share/runtime/thread.cpp	Wed Apr 18 09:25:51 2018 +0200
@@ -246,6 +246,7 @@
   _threads_hazard_ptr = NULL;
   _nested_threads_hazard_ptr = NULL;
   _nested_threads_hazard_ptr_cnt = 0;
+  _rcu_counter = 0;
 
   // the handle mark links itself to last_handle_mark
   new HandleMark(this);
--- a/src/hotspot/share/runtime/thread.hpp	Tue Apr 17 23:27:41 2018 -0700
+++ b/src/hotspot/share/runtime/thread.hpp	Wed Apr 18 09:25:51 2018 +0200
@@ -305,6 +305,14 @@
   // claimed as a task.
   int _oops_do_parity;
 
+  // Support for GlobalCounter
+ private:
+  volatile uintx _rcu_counter;
+ public:
+  volatile uintx* get_rcu_counter() {
+    return &_rcu_counter;
+  }
+
  public:
   void set_last_handle_mark(HandleMark* mark)   { _last_handle_mark = mark; }
   HandleMark* last_handle_mark() const          { return _last_handle_mark; }
@@ -378,7 +386,7 @@
   void initialize_thread_current();
   void clear_thread_current(); // TLS cleanup needed before threads terminate
 
-  public:
+ public:
   // thread entry point
   virtual void run();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/utilities/globalCounter.cpp	Wed Apr 18 09:25:51 2018 +0200
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "utilities/globalCounter.hpp"
+#include "runtime/orderAccess.inline.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/threadSMR.inline.hpp"
+#include "runtime/vmThread.hpp"
+#include "utilities/spinYield.hpp"
+
+GlobalCounter::PaddedCounter GlobalCounter::_global_counter;
+
+class GlobalCounter::CounterThreadCheck : public ThreadClosure {
+ private:
+  uintx _gbl_cnt;
+ public:
+  CounterThreadCheck(uintx gbl_cnt) : _gbl_cnt(gbl_cnt) {}
+  void do_thread(Thread* thread) {
+    SpinYield yield;
+    // Loops on this thread until it has exited the critical read section.
+    while(true) {
+      uintx cnt = OrderAccess::load_acquire(thread->get_rcu_counter());
+      // This checks if the thread's counter is active. And if so is the counter
+      // for a pre-existing reader (belongs to this grace period). A pre-existing
+      // reader will have a lower counter than the global counter version for this
+      // generation. If the counter is larger than the global counter version this
+      //  is a new reader and we can continue.
+      if (((cnt & COUNTER_ACTIVE) != 0) && (cnt - _gbl_cnt) > (max_uintx / 2)) {
+        yield.wait();
+      } else {
+        break;
+      }
+    }
+  }
+};
+
+void GlobalCounter::write_synchronize() {
+  assert((*Thread::current()->get_rcu_counter() & COUNTER_ACTIVE) == 0x0, "must be outside a critcal section");
+  // Atomic::add must provide fence since we have storeload dependency.
+  volatile uintx gbl_cnt = Atomic::add((uintx)COUNTER_INCREMENT, &_global_counter._counter);
+  // Do all RCU threads.
+  CounterThreadCheck ctc(gbl_cnt);
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
+    ctc.do_thread(thread);
+  }
+  ctc.do_thread(VMThread::vm_thread());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/utilities/globalCounter.hpp	Wed Apr 18 09:25:51 2018 +0200
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_UTILITIES_GLOBAL_COUNTER_HPP
+#define SHARE_UTILITIES_GLOBAL_COUNTER_HPP
+
+#include "memory/allocation.hpp"
+#include "memory/padded.hpp"
+
+class Thread;
+
+// The GlobalCounter provides a synchronization mechanism between threads for
+// safe memory reclamation and other ABA problems. All readers must call
+// critical_section_begin before reading the volatile data and
+// critical_section_end afterwards. The write side must call write_synchronize
+// before reclaming the memory. The read-path only does an uncontented store
+// to a thread-local-storage and fence to stop any loads from floating up, thus
+// light weight and wait-free. The write-side is more heavy since it must check
+// all readers and wait until they have left the generation. (a system memory
+// barrier can be used on write-side to remove fence in read-side,
+// not implemented).
+class GlobalCounter : public AllStatic {
+ private:
+  // Since do not know what we will end up next to in BSS, we make sure the
+  // counter is on a seperate cacheline.
+  struct PaddedCounter {
+    DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE/2, 0);
+    volatile uintx _counter;
+    DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE/2, sizeof(volatile uintx));
+  };
+
+  // The global counter
+  static PaddedCounter _global_counter;
+
+  // Bit 0 is active bit.
+  static const uintx COUNTER_ACTIVE = 1;
+  // Thus we increase counter by 2.
+  static const uintx COUNTER_INCREMENT = 2;
+
+  // The per thread scanning closure.
+  class CounterThreadCheck;
+
+ public:
+  // Must be called before accessing the data. Only threads accessible lock-free
+  // can used this. Those included now are all Threads on SMR ThreadsList and
+  // the VMThread. Nesting is not yet supported.
+  static void critical_section_begin(Thread *thread);
+
+  // Must be called after finished accessing the data.
+  // Do not provide fence, allows load/stores moving into the critical section.
+  static void critical_section_end(Thread *thread);
+
+  // Make the data inaccessible to readers before calling. When this call
+  // returns it's safe to reclaim the data.
+  static void write_synchronize();
+
+  // A scoped object for a reads-side critical-section.
+  class CriticalSection;
+};
+
+#endif // include guard
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/utilities/globalCounter.inline.hpp	Wed Apr 18 09:25:51 2018 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_UTILITIES_GLOBAL_COUNTER_INLINE_HPP
+#define SHARE_UTILITIES_GLOBAL_COUNTER_INLINE_HPP
+
+#include "runtime/orderAccess.inline.hpp"
+#include "runtime/thread.hpp"
+#include "utilities/globalCounter.hpp"
+
+inline void GlobalCounter::critical_section_begin(Thread *thread) {
+  assert(thread == Thread::current(), "must be current thread");
+  assert(thread->is_VM_thread() || thread->is_Java_thread(), "must be VMThread or JavaThread");
+  assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == 0x0, "nestled critical sections, not supported yet");
+  uintx gbl_cnt = OrderAccess::load_acquire(&_global_counter._counter);
+  OrderAccess::release_store_fence(thread->get_rcu_counter(), gbl_cnt | COUNTER_ACTIVE);
+}
+
+inline void GlobalCounter::critical_section_end(Thread *thread) {
+  assert(thread == Thread::current(), "must be current thread");
+  assert(thread->is_VM_thread() || thread->is_Java_thread(), "must be VMThread or JavaThread");
+  assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == COUNTER_ACTIVE, "must be in ctitical section");
+  // Mainly for debugging we set it to 'now'.
+  uintx gbl_cnt = OrderAccess::load_acquire(&_global_counter._counter);
+  OrderAccess::release_store(thread->get_rcu_counter(), gbl_cnt);
+}
+
+class GlobalCounter::CriticalSection {
+ private:
+  Thread* _thread;
+ public:
+  inline CriticalSection(Thread* thread) : _thread(thread) {
+    GlobalCounter::critical_section_begin(_thread);
+  }
+  inline  ~CriticalSection() {
+    GlobalCounter::critical_section_end(_thread);
+  }
+};
+
+#endif // include guard
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/utilities/test_globalCounter.cpp	Wed Apr 18 09:25:51 2018 +0200
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "precompiled.hpp"
+#include "runtime/atomic.hpp"
+#include "runtime/orderAccess.inline.hpp"
+#include "runtime/os.hpp"
+#include "utilities/globalCounter.hpp"
+#include "utilities/globalCounter.inline.hpp"
+#include "utilitiesHelper.inline.hpp"
+
+#define GOOD 1337
+#define BAD  4711
+
+struct TestData {
+  long test_value;
+};
+
+class RCUReaderThread : public JavaTestThread {
+public:
+  static volatile bool _exit;
+  volatile TestData** _test;
+  Semaphore* _wrt_start;
+  RCUReaderThread(Semaphore* post, volatile TestData** test, Semaphore* wrt_start)
+    : JavaTestThread(post), _test(test), _wrt_start(wrt_start) {};
+  virtual ~RCUReaderThread(){}
+  void main_run() {
+    _wrt_start->signal();
+    while (!_exit) {
+      GlobalCounter::critical_section_begin(this);
+      volatile TestData* test = OrderAccess::load_acquire(_test);
+      long value = OrderAccess::load_acquire(&test->test_value);
+      ASSERT_EQ(value, GOOD);
+      GlobalCounter::critical_section_end(this);
+      {
+        GlobalCounter::CriticalSection cs(this);
+        volatile TestData* test = OrderAccess::load_acquire(_test);
+        long value = OrderAccess::load_acquire(&test->test_value);
+        ASSERT_EQ(value, GOOD);
+      }
+    }
+  }
+};
+
+volatile bool RCUReaderThread::_exit = false;
+
+class RCUWriterThread : public JavaTestThread {
+public:
+  RCUWriterThread(Semaphore* post) : JavaTestThread(post) {
+  };
+  virtual ~RCUWriterThread(){}
+  void main_run() {
+    static const int NUMBER_OF_READERS = 4;
+    Semaphore post;
+    Semaphore wrt_start;
+    volatile TestData* test = NULL;
+
+    RCUReaderThread* reader1 = new RCUReaderThread(&post, &test, &wrt_start);
+    RCUReaderThread* reader2 = new RCUReaderThread(&post, &test, &wrt_start);
+    RCUReaderThread* reader3 = new RCUReaderThread(&post, &test, &wrt_start);
+    RCUReaderThread* reader4 = new RCUReaderThread(&post, &test, &wrt_start);
+
+    TestData* tmp = new TestData();
+    tmp->test_value = GOOD;
+    OrderAccess::release_store_fence(&test, tmp);
+
+    reader1->doit();
+    reader2->doit();
+    reader3->doit();
+    reader4->doit();
+
+    int nw = NUMBER_OF_READERS;
+    while (nw > 0) {
+      wrt_start.wait();
+      --nw;
+    }
+    jlong stop_ms = os::javaTimeMillis() + 1000; // 1 seconds max test time
+    for (int i = 0; i < 100000 && stop_ms > os::javaTimeMillis(); i++) {
+      volatile TestData* free_tmp = test;
+      tmp = new TestData();
+      tmp->test_value = GOOD;
+      OrderAccess::release_store(&test, tmp);
+      GlobalCounter::write_synchronize();
+      free_tmp->test_value = BAD;
+      delete free_tmp;
+    }
+    RCUReaderThread::_exit = true;
+    for (int i = 0; i < NUMBER_OF_READERS; i++) {
+      post.wait();
+    }
+  }
+};
+
+TEST_VM(GlobalCounter, critical_section) {
+  RCUReaderThread::_exit = false;
+  mt_test_doer<RCUWriterThread>();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/utilities/utilitiesHelper.inline.hpp	Wed Apr 18 09:25:51 2018 +0200
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef GTEST_UTILITIES_HELPER_INLINE_HPP
+#define GTEST_UTILITIES_HELPER_INLINE_HPP
+
+#include "runtime/mutex.hpp"
+#include "runtime/semaphore.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/vmThread.hpp"
+#include "runtime/vm_operations.hpp"
+#include "unittest.hpp"
+
+class VM_StopSafepoint : public VM_Operation {
+public:
+  Semaphore* _test_complete;
+  VM_StopSafepoint(Semaphore* wait_for) : _test_complete(wait_for) {}
+  VMOp_Type type() const          { return VMOp_Dummy; }
+  Mode evaluation_mode() const    { return _no_safepoint; }
+  bool is_cheap_allocated() const { return false; }
+  void doit()                     { _test_complete->wait(); }
+};
+
+// This class and thread keep the non-safepoint op running while we do our testing.
+class VMThreadBlocker : public JavaThread {
+public:
+  Semaphore* _unblock;
+  Semaphore* _done;
+  VMThreadBlocker(Semaphore* ub, Semaphore* done) : _unblock(ub), _done(done) {
+  }
+  virtual ~VMThreadBlocker() {}
+  void run() {
+    this->set_thread_state(_thread_in_vm);
+    {
+      MutexLocker ml(Threads_lock);
+      Threads::add(this);
+    }
+    VM_StopSafepoint ss(_unblock);
+    VMThread::execute(&ss);
+    _done->signal();
+    Threads::remove(this);
+    this->smr_delete();
+  }
+  void doit() {
+    if (os::create_thread(this, os::os_thread)) {
+      os::start_thread(this);
+    } else {
+      ASSERT_TRUE(false);
+    }
+  }
+};
+
+// For testing in a real JavaThread.
+class JavaTestThread : public JavaThread {
+public:
+  Semaphore* _post;
+  JavaTestThread(Semaphore* post)
+    : _post(post) {
+  }
+  virtual ~JavaTestThread() {}
+
+  void prerun() {
+    this->set_thread_state(_thread_in_vm);
+    {
+      MutexLocker ml(Threads_lock);
+      Threads::add(this);
+    }
+    {
+      MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag);
+    }
+  }
+
+  virtual void main_run() = 0;
+
+  void run() {
+    prerun();
+    main_run();
+    postrun();
+  }
+
+  void postrun() {
+    Threads::remove(this);
+    _post->signal();
+    this->smr_delete();
+  }
+
+  void doit() {
+    if (os::create_thread(this, os::os_thread)) {
+      os::start_thread(this);
+    } else {
+      ASSERT_TRUE(false);
+    }
+  }
+};
+
+template <typename FUNC>
+class SingleTestThread : public JavaTestThread {
+public:
+  FUNC& _f;
+  SingleTestThread(Semaphore* post, FUNC& f)
+    : JavaTestThread(post), _f(f) {
+  }
+
+  virtual ~SingleTestThread(){}
+
+  virtual void main_run() {
+    _f(this);
+  }
+};
+
+template <typename TESTFUNC>
+static void nomt_test_doer(TESTFUNC &f) {
+  Semaphore post, block_done, vmt_done;
+  VMThreadBlocker* blocker = new VMThreadBlocker(&block_done, &vmt_done);
+  blocker->doit();
+  SingleTestThread<TESTFUNC>* stt = new SingleTestThread<TESTFUNC>(&post, f);
+  stt->doit();
+  post.wait();
+  block_done.signal();
+  vmt_done.wait();
+}
+
+template <typename RUNNER>
+static void mt_test_doer() {
+  Semaphore post, block_done, vmt_done;
+  VMThreadBlocker* blocker = new VMThreadBlocker(&block_done, &vmt_done);
+  blocker->doit();
+  RUNNER* runner = new RUNNER(&post);
+  runner->doit();
+  post.wait();
+  block_done.signal();
+  vmt_done.wait();
+}
+
+#endif // include guard