8152196: SuspendibleThreadSet::yield scales poorly
authorkbarrett
Tue, 22 Mar 2016 20:32:48 -0400
changeset 37188 390e3a2e7cee
parent 37185 7fd17c40a180
child 37189 3cda05a8c3f2
8152196: SuspendibleThreadSet::yield scales poorly Summary: Use semaphore to wake up VM thread on last suspendible leave/yield. Reviewed-by: mgerdin, drwhite
hotspot/src/share/vm/gc/g1/suspendibleThreadSet.cpp
hotspot/src/share/vm/gc/g1/suspendibleThreadSet.hpp
hotspot/src/share/vm/runtime/init.cpp
--- a/hotspot/src/share/vm/gc/g1/suspendibleThreadSet.cpp	Thu Mar 24 15:19:06 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/suspendibleThreadSet.cpp	Tue Mar 22 20:32:48 2016 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, 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
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "gc/g1/suspendibleThreadSet.hpp"
 #include "runtime/mutexLocker.hpp"
+#include "runtime/semaphore.hpp"
 #include "runtime/thread.inline.hpp"
 
 uint   SuspendibleThreadSet::_nthreads          = 0;
@@ -32,6 +33,19 @@
 bool   SuspendibleThreadSet::_suspend_all       = false;
 double SuspendibleThreadSet::_suspend_all_start = 0.0;
 
+static Semaphore* _synchronize_wakeup = NULL;
+
+void SuspendibleThreadSet_init() {
+  assert(_synchronize_wakeup == NULL, "STS already initialized");
+  _synchronize_wakeup = new Semaphore();
+}
+
+bool SuspendibleThreadSet::is_synchronized() {
+  assert_lock_strong(STS_lock);
+  assert(_nthreads_stopped <= _nthreads, "invariant");
+  return _nthreads_stopped == _nthreads;
+}
+
 void SuspendibleThreadSet::join() {
   assert(!Thread::current()->is_suspendible_thread(), "Thread already joined");
   MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag);
@@ -48,31 +62,30 @@
   assert(_nthreads > 0, "Invalid");
   DEBUG_ONLY(Thread::current()->clear_suspendible_thread();)
   _nthreads--;
-  if (_suspend_all) {
-    ml.notify_all();
+  if (_suspend_all && is_synchronized()) {
+    // This leave completes a request, so inform the requestor.
+    _synchronize_wakeup->signal();
   }
 }
 
 void SuspendibleThreadSet::yield() {
   assert(Thread::current()->is_suspendible_thread(), "Must have joined");
+  MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag);
   if (_suspend_all) {
-    MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag);
-    if (_suspend_all) {
-      _nthreads_stopped++;
-      if (_nthreads_stopped == _nthreads) {
-        if (ConcGCYieldTimeout > 0) {
-          double now = os::elapsedTime();
-          guarantee((now - _suspend_all_start) * 1000.0 < (double)ConcGCYieldTimeout, "Long delay");
-        }
+    _nthreads_stopped++;
+    if (is_synchronized()) {
+      if (ConcGCYieldTimeout > 0) {
+        double now = os::elapsedTime();
+        guarantee((now - _suspend_all_start) * 1000.0 < (double)ConcGCYieldTimeout, "Long delay");
       }
-      ml.notify_all();
-      while (_suspend_all) {
-        ml.wait(Mutex::_no_safepoint_check_flag);
-      }
-      assert(_nthreads_stopped > 0, "Invalid");
-      _nthreads_stopped--;
-      ml.notify_all();
+      // This yield completes the request, so inform the requestor.
+      _synchronize_wakeup->signal();
     }
+    while (_suspend_all) {
+      ml.wait(Mutex::_no_safepoint_check_flag);
+    }
+    assert(_nthreads_stopped > 0, "Invalid");
+    _nthreads_stopped--;
   }
 }
 
@@ -81,18 +94,41 @@
   if (ConcGCYieldTimeout > 0) {
     _suspend_all_start = os::elapsedTime();
   }
+  {
+    MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag);
+    assert(!_suspend_all, "Only one at a time");
+    _suspend_all = true;
+    if (is_synchronized()) {
+      return;
+    }
+  } // Release lock before semaphore wait.
+
+  // Semaphore initial count is zero.  To reach here, there must be at
+  // least one not yielded thread in the set, e.g. is_synchronized()
+  // was false before the lock was released.  A thread in the set will
+  // signal the semaphore iff it is the last to yield or leave while
+  // there is an active suspend request.  So there will be exactly one
+  // signal, which will increment the semaphore count to one, which
+  // will then be consumed by this wait, returning it to zero.  No
+  // thread can exit yield or enter the set until desynchronize is
+  // called, so there are no further opportunities for the semaphore
+  // being signaled until we get back here again for some later
+  // synchronize call.  Hence, there is no need to re-check for
+  // is_synchronized after the wait; it will always be true there.
+  _synchronize_wakeup->wait();
+
+#ifdef ASSERT
   MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag);
-  assert(!_suspend_all, "Only one at a time");
-  _suspend_all = true;
-  while (_nthreads_stopped < _nthreads) {
-    ml.wait(Mutex::_no_safepoint_check_flag);
-  }
+  assert(_suspend_all, "STS not synchronizing");
+  assert(is_synchronized(), "STS not synchronized");
+#endif
 }
 
 void SuspendibleThreadSet::desynchronize() {
   assert(Thread::current()->is_VM_thread(), "Must be the VM thread");
   MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag);
-  assert(_nthreads_stopped == _nthreads, "Invalid");
+  assert(_suspend_all, "STS not synchronizing");
+  assert(is_synchronized(), "STS not synchronized");
   _suspend_all = false;
   ml.notify_all();
 }
--- a/hotspot/src/share/vm/gc/g1/suspendibleThreadSet.hpp	Thu Mar 24 15:19:06 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/suspendibleThreadSet.hpp	Tue Mar 22 20:32:48 2016 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, 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
@@ -45,6 +45,8 @@
   static bool   _suspend_all;
   static double _suspend_all_start;
 
+  static bool is_synchronized();
+
   // Add the current thread to the set. May block if a suspension is in progress.
   static void join();
 
--- a/hotspot/src/share/vm/runtime/init.cpp	Thu Mar 24 15:19:06 2016 +0000
+++ b/hotspot/src/share/vm/runtime/init.cpp	Tue Mar 22 20:32:48 2016 -0400
@@ -47,6 +47,7 @@
 void mutex_init();
 void chunkpool_init();
 void perfMemory_init();
+void SuspendibleThreadSet_init() NOT_ALL_GCS_RETURN;
 
 // Initialization done by Java thread in init_globals()
 void management_init();
@@ -93,6 +94,7 @@
   mutex_init();
   chunkpool_init();
   perfMemory_init();
+  SuspendibleThreadSet_init();
 }