8170299: Debugger does not stop inside the low memory notifications code
authordtitov
Tue, 08 Oct 2019 09:13:08 -0700
changeset 58503 726a3945e934
parent 58502 7cb1218ef4d0
child 58504 94dd00d2da29
8170299: Debugger does not stop inside the low memory notifications code Reviewed-by: sspitsyn, dholmes
src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp
src/hotspot/share/runtime/globals.hpp
src/hotspot/share/runtime/mutexLocker.cpp
src/hotspot/share/runtime/mutexLocker.hpp
src/hotspot/share/runtime/notificationThread.cpp
src/hotspot/share/runtime/notificationThread.hpp
src/hotspot/share/runtime/serviceThread.cpp
src/hotspot/share/runtime/serviceThread.hpp
src/hotspot/share/runtime/vmStructs.cpp
src/hotspot/share/services/diagnosticFramework.cpp
src/hotspot/share/services/gcNotifier.cpp
src/hotspot/share/services/lowMemoryDetector.cpp
src/hotspot/share/services/lowMemoryDetector.hpp
src/hotspot/share/services/management.cpp
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/NotificationThread.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java
test/jdk/com/sun/jdi/JdbStopInNotificationThreadTest.java
--- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -393,6 +393,10 @@
     Service_lock->unlock();
   }
 
+  if (UseNotificationThread && Notification_lock->owned_by_self()) {
+    Notification_lock->unlock();
+  }
+
   if (CodeCache_lock->owned_by_self()) {
     CodeCache_lock->unlock();
   }
--- a/src/hotspot/share/runtime/globals.hpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/globals.hpp	Tue Oct 08 09:13:08 2019 -0700
@@ -1047,6 +1047,9 @@
   diagnostic(bool, EnableThreadSMRStatistics, trueInDebug,                  \
              "Enable Thread SMR Statistics")                                \
                                                                             \
+  product(bool, UseNotificationThread, true,                                \
+          "Use Notification Thread")                                        \
+                                                                            \
   product(bool, Inline, true,                                               \
           "Enable inlining")                                                \
                                                                             \
--- a/src/hotspot/share/runtime/mutexLocker.cpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/mutexLocker.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -115,6 +115,7 @@
 
 Mutex*   Management_lock              = NULL;
 Monitor* Service_lock                 = NULL;
+Monitor* Notification_lock            = NULL;
 Monitor* PeriodicTask_lock            = NULL;
 Monitor* RedefineClasses_lock         = NULL;
 Mutex*   Verify_lock                  = NULL;
@@ -236,6 +237,13 @@
   def(Patching_lock                , PaddedMutex  , special,     true,  _safepoint_check_never);      // used for safepointing and code patching.
   def(CompiledMethod_lock          , PaddedMutex  , special-1,   true,  _safepoint_check_never);
   def(Service_lock                 , PaddedMonitor, special,     true,  _safepoint_check_never);      // used for service thread operations
+
+  if (UseNotificationThread) {
+    def(Notification_lock            , PaddedMonitor, special,     true,  _safepoint_check_never);  // used for notification thread operations
+  } else {
+    Notification_lock = Service_lock;
+  }
+
   def(JmethodIdCreation_lock       , PaddedMutex  , leaf,        true,  _safepoint_check_always); // used for creating jmethodIDs.
 
   def(SystemDictionary_lock        , PaddedMonitor, leaf,        true,  _safepoint_check_always);
--- a/src/hotspot/share/runtime/mutexLocker.hpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/mutexLocker.hpp	Tue Oct 08 09:13:08 2019 -0700
@@ -111,6 +111,7 @@
 
 extern Mutex*   Management_lock;                 // a lock used to serialize JVM management
 extern Monitor* Service_lock;                    // a lock used for service thread operation
+extern Monitor* Notification_lock;               // a lock used for notification thread operation
 extern Monitor* PeriodicTask_lock;               // protects the periodic task structure
 extern Monitor* RedefineClasses_lock;            // locks classes from parallel redefinition
 extern Mutex*   Verify_lock;                     // synchronize initialization of verify library
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/notificationThread.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019, 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 "memory/universe.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/notificationThread.hpp"
+#include "services/diagnosticArgument.hpp"
+#include "services/diagnosticFramework.hpp"
+#include "services/gcNotifier.hpp"
+#include "services/lowMemoryDetector.hpp"
+
+NotificationThread* NotificationThread::_instance = NULL;
+
+void NotificationThread::initialize() {
+  EXCEPTION_MARK;
+
+  const char* name = "Notification Thread";
+  Handle string = java_lang_String::create_from_str(name, CHECK);
+
+  // Initialize thread_oop to put it into the system threadGroup
+  Handle thread_group (THREAD, Universe::system_thread_group());
+  Handle thread_oop = JavaCalls::construct_new_instance(
+                          SystemDictionary::Thread_klass(),
+                          vmSymbols::threadgroup_string_void_signature(),
+                          thread_group,
+                          string,
+                          CHECK);
+
+  Klass* group = SystemDictionary::ThreadGroup_klass();
+  JavaValue result(T_VOID);
+  JavaCalls::call_special(&result,
+                          thread_group,
+                          group,
+                          vmSymbols::add_method_name(),
+                          vmSymbols::thread_void_signature(),
+                          thread_oop,
+                          THREAD);
+  {
+    MutexLocker mu(Threads_lock);
+    NotificationThread* thread =  new NotificationThread(&notification_thread_entry);
+
+    // At this point it may be possible that no osthread was created for the
+    // JavaThread due to lack of memory. We would have to throw an exception
+    // in that case. However, since this must work and we do not allow
+    // exceptions anyway, check and abort if this fails.
+    if (thread == NULL || thread->osthread() == NULL) {
+      vm_exit_during_initialization("java.lang.OutOfMemoryError",
+                                    os::native_thread_creation_failed_msg());
+    }
+
+    java_lang_Thread::set_thread(thread_oop(), thread);
+    java_lang_Thread::set_priority(thread_oop(), NearMaxPriority);
+    java_lang_Thread::set_daemon(thread_oop());
+    thread->set_threadObj(thread_oop());
+    _instance = thread;
+
+    Threads::add(thread);
+    Thread::start(thread);
+  }
+}
+
+
+
+void NotificationThread::notification_thread_entry(JavaThread* jt, TRAPS) {
+  while (true) {
+    bool sensors_changed = false;
+    bool has_dcmd_notification_event = false;
+    bool has_gc_notification_event = false;
+    {
+      // Need state transition ThreadBlockInVM so that this thread
+      // will be handled by safepoint correctly when this thread is
+      // notified at a safepoint.
+
+      ThreadBlockInVM tbivm(jt);
+
+      MonitorLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
+      // Process all available work on each (outer) iteration, rather than
+      // only the first recognized bit of work, to avoid frequently true early
+      // tests from potentially starving later work.  Hence the use of
+      // arithmetic-or to combine results; we don't want short-circuiting.
+      while (((sensors_changed = LowMemoryDetector::has_pending_requests()) |
+              (has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) |
+              (has_gc_notification_event = GCNotifier::has_event()))
+             == 0) {
+        // Wait as a suspend equalent until notified that there is some work to do.
+        ml.wait(0, true);
+      }
+
+    }
+
+    if (sensors_changed) {
+      LowMemoryDetector::process_sensor_changes(jt);
+    }
+
+    if(has_gc_notification_event) {
+      GCNotifier::sendNotification(CHECK);
+    }
+
+    if(has_dcmd_notification_event) {
+      DCmdFactory::send_notification(CHECK);
+    }
+
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/notificationThread.hpp	Tue Oct 08 09:13:08 2019 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019, 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_RUNTIME_NOTIFICATIONTHREAD_HPP
+#define SHARE_RUNTIME_NOTIFICATIONTHREAD_HPP
+
+#include "runtime/thread.hpp"
+
+// A JavaThread for low memory detection support, GC and
+// diagnostic framework notifications. This thread is not hidden
+// from the external view to allow the debugger to stop at the
+// breakpoints inside registred MXBean notification listeners.
+
+class NotificationThread : public JavaThread {
+  friend class VMStructs;
+ private:
+
+  static NotificationThread* _instance;
+
+  static void notification_thread_entry(JavaThread* thread, TRAPS);
+  NotificationThread(ThreadFunction entry_point) : JavaThread(entry_point) {};
+
+ public:
+  static void initialize();
+
+};
+
+#endif // SHARE_RUNTIME_NOTIFICATIONTHREAD_HPP
--- a/src/hotspot/share/runtime/serviceThread.cpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/serviceThread.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -120,10 +120,10 @@
       // only the first recognized bit of work, to avoid frequently true early
       // tests from potentially starving later work.  Hence the use of
       // arithmetic-or to combine results; we don't want short-circuiting.
-      while (((sensors_changed = LowMemoryDetector::has_pending_requests()) |
+      while (((sensors_changed = (!UseNotificationThread && LowMemoryDetector::has_pending_requests())) |
               (has_jvmti_events = JvmtiDeferredEventQueue::has_events()) |
-              (has_gc_notification_event = GCNotifier::has_event()) |
-              (has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) |
+              (has_gc_notification_event = (!UseNotificationThread && GCNotifier::has_event())) |
+              (has_dcmd_notification_event = (!UseNotificationThread && DCmdFactory::has_pending_jmx_notification())) |
               (stringtable_work = StringTable::has_work()) |
               (symboltable_work = SymbolTable::has_work()) |
               (resolved_method_table_work = ResolvedMethodTable::has_work()) |
@@ -151,16 +151,18 @@
       jvmti_event.post();
     }
 
-    if (sensors_changed) {
-      LowMemoryDetector::process_sensor_changes(jt);
-    }
+    if (!UseNotificationThread) {
+      if (sensors_changed) {
+        LowMemoryDetector::process_sensor_changes(jt);
+      }
 
-    if(has_gc_notification_event) {
-      GCNotifier::sendNotification(CHECK);
-    }
+      if(has_gc_notification_event) {
+        GCNotifier::sendNotification(CHECK);
+      }
 
-    if(has_dcmd_notification_event) {
-      DCmdFactory::send_notification(CHECK);
+      if(has_dcmd_notification_event) {
+        DCmdFactory::send_notification(CHECK);
+      }
     }
 
     if (resolved_method_table_work) {
--- a/src/hotspot/share/runtime/serviceThread.hpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/serviceThread.hpp	Tue Oct 08 09:13:08 2019 -0700
@@ -27,8 +27,10 @@
 
 #include "runtime/thread.hpp"
 
-// A JavaThread for low memory detection support and JVMTI
-// compiled-method-load events.
+// A hidden from external view JavaThread for JVMTI compiled-method-load
+// events, oop storage cleanup, and the maintainance of string, symbol,
+// protection domain, and resolved method tables.
+
 class ServiceThread : public JavaThread {
   friend class VMStructs;
  private:
--- a/src/hotspot/share/runtime/vmStructs.cpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/runtime/vmStructs.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -88,6 +88,7 @@
 #include "runtime/globals.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
+#include "runtime/notificationThread.hpp"
 #include "runtime/os.hpp"
 #include "runtime/perfMemory.hpp"
 #include "runtime/serviceThread.hpp"
@@ -1366,6 +1367,7 @@
       declare_type(JavaThread, Thread)                                    \
         declare_type(JvmtiAgentThread, JavaThread)                        \
         declare_type(ServiceThread, JavaThread)                           \
+        declare_type(NotificationThread, JavaThread)                      \
         declare_type(CompilerThread, JavaThread)                          \
         declare_type(CodeCacheSweeperThread, JavaThread)                  \
   declare_toplevel_type(OSThread)                                         \
--- a/src/hotspot/share/services/diagnosticFramework.cpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/diagnosticFramework.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -437,9 +437,9 @@
 }
 
 void DCmdFactory::push_jmx_notification_request() {
-  MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+  MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
   _has_pending_jmx_notification = true;
-  Service_lock->notify_all();
+  Notification_lock->notify_all();
 }
 
 void DCmdFactory::send_notification(TRAPS) {
@@ -455,7 +455,7 @@
   HandleMark hm(THREAD);
   bool notif = false;
   {
-    MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+    MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
     notif = _has_pending_jmx_notification;
     _has_pending_jmx_notification = false;
   }
--- a/src/hotspot/share/services/gcNotifier.cpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/gcNotifier.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -54,18 +54,18 @@
  }
 
 void GCNotifier::addRequest(GCNotificationRequest *request) {
-  MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+  MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
   if(first_request == NULL) {
     first_request = request;
   } else {
     last_request->next = request;
   }
   last_request = request;
-  Service_lock->notify_all();
+  Notification_lock->notify_all();
 }
 
 GCNotificationRequest *GCNotifier::getRequest() {
-  MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+  MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
   GCNotificationRequest *request = first_request;
   if(first_request != NULL) {
     first_request = first_request->next;
--- a/src/hotspot/share/services/lowMemoryDetector.cpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/lowMemoryDetector.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -40,7 +40,7 @@
 volatile jint LowMemoryDetector::_disabled_count = 0;
 
 bool LowMemoryDetector::has_pending_requests() {
-  assert(Service_lock->owned_by_self(), "Must own Service_lock");
+  assert(Notification_lock->owned_by_self(), "Must own Notification_lock");
   bool has_requests = false;
   int num_memory_pools = MemoryService::num_memory_pools();
   for (int i = 0; i < num_memory_pools; i++) {
@@ -62,7 +62,7 @@
   ResourceMark rm(THREAD);
   HandleMark hm(THREAD);
 
-  // No need to hold Service_lock to call out to Java
+  // No need to hold Notification_lock to call out to Java
   int num_memory_pools = MemoryService::num_memory_pools();
   for (int i = 0; i < num_memory_pools; i++) {
     MemoryPool* pool = MemoryService::get_memory_pool(i);
@@ -80,7 +80,7 @@
 // This method could be called from any Java threads
 // and also VMThread.
 void LowMemoryDetector::detect_low_memory() {
-  MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+  MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
 
   bool has_pending_requests = false;
   int num_memory_pools = MemoryService::num_memory_pools();
@@ -98,7 +98,7 @@
   }
 
   if (has_pending_requests) {
-    Service_lock->notify_all();
+    Notification_lock->notify_all();
   }
 }
 
@@ -113,14 +113,14 @@
   }
 
   {
-    MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+    MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
 
     MemoryUsage usage = pool->get_memory_usage();
     sensor->set_gauge_sensor_level(usage,
                                    pool->usage_threshold());
     if (sensor->has_pending_requests()) {
       // notify sensor state update
-      Service_lock->notify_all();
+      Notification_lock->notify_all();
     }
   }
 }
@@ -135,14 +135,14 @@
   }
 
   {
-    MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+    MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
 
     MemoryUsage usage = pool->get_last_collection_usage();
     sensor->set_counter_sensor_level(usage, pool->gc_usage_threshold());
 
     if (sensor->has_pending_requests()) {
       // notify sensor state update
-      Service_lock->notify_all();
+      Notification_lock->notify_all();
     }
   }
 }
@@ -205,7 +205,7 @@
 // If the current level is between high and low threshold, no change.
 //
 void SensorInfo::set_gauge_sensor_level(MemoryUsage usage, ThresholdSupport* high_low_threshold) {
-  assert(Service_lock->owned_by_self(), "Must own Service_lock");
+  assert(Notification_lock->owned_by_self(), "Must own Notification_lock");
   assert(high_low_threshold->is_high_threshold_supported(), "just checking");
 
   bool is_over_high = high_low_threshold->is_high_threshold_crossed(usage);
@@ -260,7 +260,7 @@
 //      the sensor will be on (i.e. sensor is currently off
 //      and has pending trigger requests).
 void SensorInfo::set_counter_sensor_level(MemoryUsage usage, ThresholdSupport* counter_threshold) {
-  assert(Service_lock->owned_by_self(), "Must own Service_lock");
+  assert(Notification_lock->owned_by_self(), "Must own Notification_lock");
   assert(counter_threshold->is_high_threshold_supported(), "just checking");
 
   bool is_over_high = counter_threshold->is_high_threshold_crossed(usage);
@@ -334,8 +334,8 @@
   }
 
   {
-    // Holds Service_lock and update the sensor state
-    MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+    // Holds Notification_lock and update the sensor state
+    MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
     assert(_pending_trigger_count > 0, "Must have pending trigger");
     _sensor_on = true;
     _sensor_count += count;
@@ -345,8 +345,8 @@
 
 void SensorInfo::clear(int count, TRAPS) {
   {
-    // Holds Service_lock and update the sensor state
-    MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
+    // Holds Notification_lock and update the sensor state
+    MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);
     if (_pending_clear_count == 0) {
       // Bail out if we lost a race to set_*_sensor_level() which may have
       // reactivated the sensor in the meantime because it was triggered again.
--- a/src/hotspot/share/services/lowMemoryDetector.hpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/lowMemoryDetector.hpp	Tue Oct 08 09:13:08 2019 -0700
@@ -59,7 +59,8 @@
 //
 // May need to deal with hysteresis effect.
 //
-// Memory detection code runs in the Service thread (serviceThread.hpp).
+// Memory detection code runs in the Notification thread or
+// ServiceThread depending on UseNotificationThread flag.
 
 class OopClosure;
 class MemoryPool;
@@ -214,6 +215,7 @@
 class LowMemoryDetector : public AllStatic {
   friend class LowMemoryDetectorDisabler;
   friend class ServiceThread;
+  friend class NotificationThread;
 private:
   // true if any collected heap has low memory detection enabled
   static volatile bool _enabled_for_collected_pools;
--- a/src/hotspot/share/services/management.cpp	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/hotspot/share/services/management.cpp	Tue Oct 08 09:13:08 2019 -0700
@@ -44,6 +44,7 @@
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/jniHandles.inline.hpp"
+#include "runtime/notificationThread.hpp"
 #include "runtime/os.hpp"
 #include "runtime/serviceThread.hpp"
 #include "runtime/thread.inline.hpp"
@@ -148,7 +149,9 @@
 void Management::initialize(TRAPS) {
   // Start the service thread
   ServiceThread::initialize();
-
+  if (UseNotificationThread) {
+    NotificationThread::initialize();
+  }
   if (ManagementServer) {
     ResourceMark rm(THREAD);
     HandleMark hm(THREAD);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/NotificationThread.java	Tue Oct 08 09:13:08 2019 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+package sun.jvm.hotspot.runtime;
+
+
+import sun.jvm.hotspot.debugger.Address;
+
+public class NotificationThread extends JavaThread {
+    public NotificationThread(Address addr) {
+        super(addr);
+    }
+
+    public boolean isJavaThread() { return false; }
+
+}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java	Tue Oct 08 15:03:20 2019 +0100
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java	Tue Oct 08 09:13:08 2019 -0700
@@ -158,6 +158,7 @@
         }
         virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class);
         virtualConstructor.addMapping("ServiceThread", ServiceThread.class);
+        virtualConstructor.addMapping("NotificationThread", NotificationThread.class);
     }
 
     public Threads() {
@@ -165,14 +166,14 @@
     }
 
     /** NOTE: this returns objects of type JavaThread, CompilerThread,
-      JvmtiAgentThread, and ServiceThread.
+      JvmtiAgentThread, NotificationThread, and ServiceThread.
       The latter four are subclasses of the former. Most operations
       (fetching the top frame, etc.) are only allowed to be performed on
       a "pure" JavaThread. For this reason, {@link
       sun.jvm.hotspot.runtime.JavaThread#isJavaThread} has been
       changed from the definition in the VM (which returns true for
       all of these thread types) to return true for JavaThreads and
-      false for the three subclasses. FIXME: should reconsider the
+      false for the four subclasses. FIXME: should reconsider the
       inheritance hierarchy; see {@link
       sun.jvm.hotspot.runtime.JavaThread#isJavaThread}. */
     public JavaThread getJavaThreadAt(int i) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/JdbStopInNotificationThreadTest.java	Tue Oct 08 09:13:08 2019 -0700
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @summary Tests that the breakpoint in the notification listener is hit when the
+ * notification thread is enabled and is not hit when the notification thread is disabled
+ * (the service thread delivers the notifications in this case).
+ *
+ * @library /test/lib
+ * @run compile -g JdbStopInNotificationThreadTest.java
+ * @run main/othervm JdbStopInNotificationThreadTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import lib.jdb.JdbCommand;
+import lib.jdb.JdbTest;
+
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import java.lang.management.*;
+import java.util.Collection;
+import java.util.LinkedList;
+
+class JdbStopInNotificationThreadTestTarg {
+
+    private static volatile boolean done = false;
+
+    private static final MemoryPoolMXBean tenuredGenPool =
+            findTenuredGenPool();
+
+    public static void main(String[] args) throws Exception {
+        test(); // @1 breakpoint
+    }
+
+    private static void test() throws Exception {
+        setPercentageUsageThreshold(0.1);
+        MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
+        NotificationEmitter emitter = (NotificationEmitter) mbean;
+        emitter.addNotificationListener(new NotificationListener() {
+            public void handleNotification(Notification n, Object hb) {
+                System.out.println("Notification received:" + n.getType());
+                if (n.getType().equals(
+                        MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
+                    done = true;
+                    System.out.println("Notification MEMORY_THRESHOLD_EXCEEDED received:");
+                    long maxMemory = tenuredGenPool.getUsage().getMax();
+                    long usedMemory = tenuredGenPool.getUsage().getUsed();
+                    System.out.println("Memory usage low!!!"); // @2 breakpoint
+                    double percentageUsed = ((double) usedMemory) / maxMemory;
+                    System.out.println("percentageUsed = " + percentageUsed);
+                }
+            }
+        }, null, null);
+
+        Collection<Object[]> numbers = new LinkedList();
+        long counter = 0;
+        while (!done) {
+            numbers.add(new Object[1000]);
+            counter++;
+            if (counter % 1000 == 0) {
+                Thread.sleep(100);
+            }
+        }
+        System.out.println("Done");
+    }
+
+    private static MemoryPoolMXBean findTenuredGenPool() {
+        for (MemoryPoolMXBean pool :
+                ManagementFactory.getMemoryPoolMXBeans()) {
+            if (pool.getType() == MemoryType.HEAP &&
+                    pool.isUsageThresholdSupported()) {
+                return pool;
+            }
+        }
+        throw new RuntimeException("Could not find tenured space");
+    }
+
+    public static void setPercentageUsageThreshold(double percentage) {
+        if (percentage <= 0.0 || percentage > 1.0) {
+            throw new IllegalArgumentException("Percentage not in range");
+        }
+        System.out.println("Setting threashold for pool " + tenuredGenPool.getName() + " percentage:" + percentage);
+        long maxMemory = tenuredGenPool.getUsage().getMax();
+        long warningThreshold = (long) (maxMemory * percentage);
+        tenuredGenPool.setUsageThreshold(warningThreshold);
+    }
+}
+
+public class JdbStopInNotificationThreadTest extends JdbTest {
+
+    private static final String DEBUGGEE_CLASS = JdbStopInNotificationThreadTestTarg.class.getName();
+    private static final String PATTERN1_TEMPLATE = "^Breakpoint hit: \"thread=Notification Thread\", " +
+            "JdbStopInNotificationThreadTestTarg\\$1\\.handleNotification\\(\\), line=%LINE_NUMBER.*\\R%LINE_NUMBER\\s+System\\.out\\.println\\(\"Memory usage low!!!\"\\);.*";
+
+    private JdbStopInNotificationThreadTest() {
+        super(DEBUGGEE_CLASS);
+    }
+
+    public static void main(String argv[]) {
+        new JdbStopInNotificationThreadTest().run();
+    }
+
+    @Override
+    protected void runCases() {
+        if (isNotificationThreadDisabled()) {
+            System.out.println("Notification Thread is disabled. Skipping the test");
+            return;
+        }
+        int bpLine2 = parseBreakpoints(getTestSourcePath("JdbStopInNotificationThreadTest.java"), 2).get(0);
+        jdb.command(JdbCommand.stopAt(DEBUGGEE_CLASS + "$1", bpLine2));
+        String pattern = PATTERN1_TEMPLATE.replaceAll("%LINE_NUMBER", String.valueOf(bpLine2));
+        jdb.command(JdbCommand.cont());
+        new OutputAnalyzer(jdb.getJdbOutput()).shouldMatch(pattern);
+    }
+
+    private boolean isNotificationThreadDisabled() {
+        int bpLine1 = parseBreakpoints(getTestSourcePath("JdbStopInNotificationThreadTest.java"), 1).get(0);
+        jdb.command(JdbCommand.stopAt(DEBUGGEE_CLASS, bpLine1));
+        jdb.command(JdbCommand.run());
+        jdb.command(JdbCommand.threads());
+        if (new OutputAnalyzer(jdb.getJdbOutput()).getOutput().contains("Notification Thread")) {
+            return false;
+        }
+        return true;
+    }
+}