8169517: WhiteBox should provide concurrent GC phase control
authorkbarrett
Thu, 13 Apr 2017 16:38:39 -0400
changeset 46384 dacebddcdea0
parent 46383 24999171edf9
child 46385 6b890c9a717f
8169517: WhiteBox should provide concurrent GC phase control Summary: Added WhiteBox API and G1 implementation. Reviewed-by: shade, dfazunen
hotspot/src/share/vm/gc/g1/concurrentMarkThread.cpp
hotspot/src/share/vm/gc/g1/concurrentMarkThread.hpp
hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp
hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp
hotspot/src/share/vm/gc/shared/collectedHeap.cpp
hotspot/src/share/vm/gc/shared/collectedHeap.hpp
hotspot/src/share/vm/gc/shared/concurrentGCPhaseManager.cpp
hotspot/src/share/vm/gc/shared/concurrentGCPhaseManager.hpp
hotspot/src/share/vm/prims/whitebox.cpp
hotspot/src/share/vm/runtime/mutexLocker.cpp
hotspot/src/share/vm/runtime/mutexLocker.hpp
hotspot/test/gc/concurrent_phase_control/CheckControl.java
hotspot/test/gc/concurrent_phase_control/CheckSupported.java
hotspot/test/gc/concurrent_phase_control/CheckUnsupported.java
hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlCMS.java
hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlG1.java
hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlG1Basics.java
hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlParallel.java
hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlSerial.java
--- a/hotspot/src/share/vm/gc/g1/concurrentMarkThread.cpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/gc/g1/concurrentMarkThread.cpp	Thu Apr 13 16:38:39 2017 -0400
@@ -1,5 +1,5 @@
- /*
- * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+/*
+ * Copyright (c) 2001, 2017, 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
@@ -32,21 +32,53 @@
 #include "gc/g1/g1Policy.hpp"
 #include "gc/g1/suspendibleThreadSet.hpp"
 #include "gc/g1/vm_operations_g1.hpp"
+#include "gc/shared/concurrentGCPhaseManager.hpp"
 #include "gc/shared/gcId.hpp"
 #include "gc/shared/gcTrace.hpp"
 #include "gc/shared/gcTraceTime.inline.hpp"
 #include "logging/log.hpp"
 #include "memory/resourceArea.hpp"
 #include "runtime/vmThread.hpp"
+#include "utilities/debug.hpp"
 
 // ======= Concurrent Mark Thread ========
 
+// Check order in EXPAND_CURRENT_PHASES
+STATIC_ASSERT(ConcurrentGCPhaseManager::UNCONSTRAINED_PHASE <
+              ConcurrentGCPhaseManager::IDLE_PHASE);
+
+#define EXPAND_CONCURRENT_PHASES(expander)                              \
+  expander(ANY, = ConcurrentGCPhaseManager::UNCONSTRAINED_PHASE, NULL)  \
+  expander(IDLE, = ConcurrentGCPhaseManager::IDLE_PHASE, NULL)          \
+  expander(CONCURRENT_CYCLE,, "Concurrent Cycle")                       \
+  expander(CLEAR_CLAIMED_MARKS,, "Concurrent Clear Claimed Marks")      \
+  expander(SCAN_ROOT_REGIONS,, "Concurrent Scan Root Regions")          \
+  expander(CONCURRENT_MARK,, "Concurrent Mark")                         \
+  expander(MARK_FROM_ROOTS,, "Concurrent Mark From Roots")              \
+  expander(BEFORE_REMARK,, NULL)                                        \
+  expander(REMARK,, NULL)                                               \
+  expander(CREATE_LIVE_DATA,, "Concurrent Create Live Data")            \
+  expander(COMPLETE_CLEANUP,, "Concurrent Complete Cleanup")            \
+  expander(CLEANUP_FOR_NEXT_MARK,, "Concurrent Cleanup for Next Mark")  \
+  /* */
+
+class G1ConcurrentPhase : public AllStatic {
+public:
+  enum {
+#define CONCURRENT_PHASE_ENUM(tag, value, ignore_title) tag value,
+    EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_ENUM)
+#undef CONCURRENT_PHASE_ENUM
+    PHASE_ID_LIMIT
+  };
+};
+
 // The CM thread is created when the G1 garbage collector is used
 
 ConcurrentMarkThread::ConcurrentMarkThread(G1ConcurrentMark* cm) :
   ConcurrentGCThread(),
   _cm(cm),
   _state(Idle),
+  _phase_manager_stack(),
   _vtime_accum(0.0),
   _vtime_mark_accum(0.0) {
 
@@ -97,8 +129,9 @@
 
  public:
   G1ConcPhaseTimer(G1ConcurrentMark* cm, const char* title) :
-     GCTraceConcTimeImpl<LogLevel::Info,  LogTag::_gc, LogTag::_marking>(title),
-     _cm(cm) {
+    GCTraceConcTimeImpl<LogLevel::Info,  LogTag::_gc, LogTag::_marking>(title),
+    _cm(cm)
+  {
     _cm->gc_timer_cm()->register_gc_concurrent_start(title);
   }
 
@@ -107,12 +140,106 @@
   }
 };
 
+static const char* const concurrent_phase_names[] = {
+#define CONCURRENT_PHASE_NAME(tag, ignore_value, ignore_title) XSTR(tag),
+  EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_NAME)
+#undef CONCURRENT_PHASE_NAME
+  NULL                          // terminator
+};
+// Verify dense enum assumption.  +1 for terminator.
+STATIC_ASSERT(G1ConcurrentPhase::PHASE_ID_LIMIT + 1 ==
+              ARRAY_SIZE(concurrent_phase_names));
+
+// Returns the phase number for name, or a negative value if unknown.
+static int lookup_concurrent_phase(const char* name) {
+  const char* const* names = concurrent_phase_names;
+  for (uint i = 0; names[i] != NULL; ++i) {
+    if (strcmp(name, names[i]) == 0) {
+      return static_cast<int>(i);
+    }
+  }
+  return -1;
+}
+
+// The phase must be valid and must have a title.
+static const char* lookup_concurrent_phase_title(int phase) {
+  static const char* const titles[] = {
+#define CONCURRENT_PHASE_TITLE(ignore_tag, ignore_value, title) title,
+    EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_TITLE)
+#undef CONCURRENT_PHASE_TITLE
+  };
+  // Verify dense enum assumption.
+  STATIC_ASSERT(G1ConcurrentPhase::PHASE_ID_LIMIT == ARRAY_SIZE(titles));
+
+  assert(0 <= phase, "precondition");
+  assert((uint)phase < ARRAY_SIZE(titles), "precondition");
+  const char* title = titles[phase];
+  assert(title != NULL, "precondition");
+  return title;
+}
+
+class G1ConcPhaseManager : public StackObj {
+  G1ConcurrentMark* _cm;
+  ConcurrentGCPhaseManager _manager;
+
+public:
+  G1ConcPhaseManager(int phase, ConcurrentMarkThread* thread) :
+    _cm(thread->cm()),
+    _manager(phase, thread->phase_manager_stack())
+  { }
+
+  ~G1ConcPhaseManager() {
+    // Deactivate the manager if marking aborted, to avoid blocking on
+    // phase exit when the phase has been requested.
+    if (_cm->has_aborted()) {
+      _manager.deactivate();
+    }
+  }
+
+  void set_phase(int phase, bool force) {
+    _manager.set_phase(phase, force);
+  }
+};
+
+// Combine phase management and timing into one convenient utility.
+class G1ConcPhase : public StackObj {
+  G1ConcPhaseTimer _timer;
+  G1ConcPhaseManager _manager;
+
+public:
+  G1ConcPhase(int phase, ConcurrentMarkThread* thread) :
+    _timer(thread->cm(), lookup_concurrent_phase_title(phase)),
+    _manager(phase, thread)
+  { }
+};
+
+const char* const* ConcurrentMarkThread::concurrent_phases() const {
+  return concurrent_phase_names;
+}
+
+bool ConcurrentMarkThread::request_concurrent_phase(const char* phase_name) {
+  int phase = lookup_concurrent_phase(phase_name);
+  if (phase < 0) return false;
+
+  while (!ConcurrentGCPhaseManager::wait_for_phase(phase,
+                                                   phase_manager_stack())) {
+    assert(phase != G1ConcurrentPhase::ANY, "Wait for ANY phase must succeed");
+    if ((phase != G1ConcurrentPhase::IDLE) && !during_cycle()) {
+      // If idle and the goal is !idle, start a collection.
+      G1CollectedHeap::heap()->collect(GCCause::_wb_conc_mark);
+    }
+  }
+  return true;
+}
+
 void ConcurrentMarkThread::run_service() {
   _vtime_start = os::elapsedVTime();
 
   G1CollectedHeap* g1h = G1CollectedHeap::heap();
   G1Policy* g1_policy = g1h->g1_policy();
 
+  G1ConcPhaseManager cpmanager(G1ConcurrentPhase::IDLE, this);
+
   while (!should_terminate()) {
     // wait until started is set.
     sleepBeforeNextCycle();
@@ -120,6 +247,8 @@
       break;
     }
 
+    cpmanager.set_phase(G1ConcurrentPhase::CONCURRENT_CYCLE, false /* force */);
+
     GCIdMark gc_id_mark;
 
     cm()->concurrent_cycle_start();
@@ -133,7 +262,7 @@
       double cycle_start = os::elapsedVTime();
 
       {
-        G1ConcPhaseTimer t(_cm, "Concurrent Clear Claimed Marks");
+        G1ConcPhase p(G1ConcurrentPhase::CLEAR_CLAIMED_MARKS, this);
         ClassLoaderDataGraph::clear_claimed_marks();
       }
 
@@ -146,47 +275,68 @@
       // correctness issue.
 
       {
-        G1ConcPhaseTimer t(_cm, "Concurrent Scan Root Regions");
+        G1ConcPhase p(G1ConcurrentPhase::SCAN_ROOT_REGIONS, this);
         _cm->scan_root_regions();
       }
 
-      // It would be nice to use the GCTraceConcTime class here but
+      // It would be nice to use the G1ConcPhase class here but
       // the "end" logging is inside the loop and not at the end of
-      // a scope. Mimicking the same log output as GCTraceConcTime instead.
-      jlong mark_start = os::elapsed_counter();
-      log_info(gc, marking)("Concurrent Mark (%.3fs)", TimeHelper::counter_to_seconds(mark_start));
+      // a scope. Also, the timer doesn't support nesting.
+      // Mimicking the same log output instead.
+      {
+        G1ConcPhaseManager mark_manager(G1ConcurrentPhase::CONCURRENT_MARK, this);
+        jlong mark_start = os::elapsed_counter();
+        const char* cm_title =
+          lookup_concurrent_phase_title(G1ConcurrentPhase::CONCURRENT_MARK);
+        log_info(gc, marking)("%s (%.3fs)",
+                              cm_title,
+                              TimeHelper::counter_to_seconds(mark_start));
+        for (uint iter = 1; !cm()->has_aborted(); ++iter) {
+          // Concurrent marking.
+          {
+            G1ConcPhase p(G1ConcurrentPhase::MARK_FROM_ROOTS, this);
+            _cm->mark_from_roots();
+          }
+          if (cm()->has_aborted()) break;
 
-      for (uint iter = 1; true; ++iter) {
-        if (!cm()->has_aborted()) {
-          G1ConcPhaseTimer t(_cm, "Concurrent Mark From Roots");
-          _cm->mark_from_roots();
-        }
+          // Provide a control point after mark_from_roots.
+          {
+            G1ConcPhaseManager p(G1ConcurrentPhase::BEFORE_REMARK, this);
+          }
+          if (cm()->has_aborted()) break;
 
-        double mark_end_time = os::elapsedVTime();
-        jlong mark_end = os::elapsed_counter();
-        _vtime_mark_accum += (mark_end_time - cycle_start);
-        if (!cm()->has_aborted()) {
+          // Delay remark pause for MMU.
+          double mark_end_time = os::elapsedVTime();
+          jlong mark_end = os::elapsed_counter();
+          _vtime_mark_accum += (mark_end_time - cycle_start);
           delay_to_keep_mmu(g1_policy, true /* remark */);
-          log_info(gc, marking)("Concurrent Mark (%.3fs, %.3fs) %.3fms",
+          if (cm()->has_aborted()) break;
+
+          // Pause Remark.
+          log_info(gc, marking)("%s (%.3fs, %.3fs) %.3fms",
+                                cm_title,
                                 TimeHelper::counter_to_seconds(mark_start),
                                 TimeHelper::counter_to_seconds(mark_end),
                                 TimeHelper::counter_to_millis(mark_end - mark_start));
-
+          mark_manager.set_phase(G1ConcurrentPhase::REMARK, false);
           CMCheckpointRootsFinalClosure final_cl(_cm);
           VM_CGC_Operation op(&final_cl, "Pause Remark");
           VMThread::execute(&op);
+          if (cm()->has_aborted()) {
+            break;
+          } else if (!cm()->restart_for_overflow()) {
+            break;              // Exit loop if no restart requested.
+          } else {
+            // Loop to restart for overflow.
+            mark_manager.set_phase(G1ConcurrentPhase::CONCURRENT_MARK, false);
+            log_info(gc, marking)("%s Restart for Mark Stack Overflow (iteration #%u)",
+                                  cm_title, iter);
+          }
         }
-
-        if (!cm()->restart_for_overflow() || cm()->has_aborted()) {
-          break;
-        }
-
-        log_info(gc, marking)("Concurrent Mark Restart due to overflow"
-                              " (iteration #%u", iter);
       }
 
       if (!cm()->has_aborted()) {
-        G1ConcPhaseTimer t(_cm, "Concurrent Create Live Data");
+        G1ConcPhase p(G1ConcurrentPhase::CREATE_LIVE_DATA, this);
         cm()->create_live_data();
       }
 
@@ -220,8 +370,8 @@
         // place, it would wait for us to process the regions
         // reclaimed by cleanup.
 
-        G1ConcPhaseTimer t(_cm, "Concurrent Complete Cleanup");
         // Now do the concurrent cleanup operation.
+        G1ConcPhase p(G1ConcurrentPhase::COMPLETE_CLEANUP, this);
         _cm->complete_cleanup();
 
         // Notify anyone who's waiting that there are no more free
@@ -276,7 +426,7 @@
       // We may have aborted just before the remark. Do not bother clearing the
       // bitmap then, as it has been done during mark abort.
       if (!cm()->has_aborted()) {
-        G1ConcPhaseTimer t(_cm, "Concurrent Cleanup for Next Mark");
+        G1ConcPhase p(G1ConcurrentPhase::CLEANUP_FOR_NEXT_MARK, this);
         _cm->cleanup_for_next_mark();
       } else {
         assert(!G1VerifyBitmaps || _cm->nextMarkBitmapIsClear(), "Next mark bitmap must be clear");
@@ -293,6 +443,8 @@
 
       cm()->concurrent_cycle_end();
     }
+
+    cpmanager.set_phase(G1ConcurrentPhase::IDLE, cm()->has_aborted() /* force */);
   }
   _cm->root_regions()->cancel_scan();
 }
--- a/hotspot/src/share/vm/gc/g1/concurrentMarkThread.hpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/gc/g1/concurrentMarkThread.hpp	Thu Apr 13 16:38:39 2017 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2017, 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 @@
 #ifndef SHARE_VM_GC_G1_CONCURRENTMARKTHREAD_HPP
 #define SHARE_VM_GC_G1_CONCURRENTMARKTHREAD_HPP
 
+#include "gc/shared/concurrentGCPhaseManager.hpp"
 #include "gc/shared/concurrentGCThread.hpp"
 
 // The Concurrent Mark GC Thread triggers the parallel G1CMConcurrentMarkingTasks
@@ -50,6 +51,9 @@
 
   volatile State _state;
 
+  // WhiteBox testing support.
+  ConcurrentGCPhaseManager::Stack _phase_manager_stack;
+
   void sleepBeforeNextCycle();
   void delay_to_keep_mmu(G1Policy* g1_policy, bool remark);
 
@@ -83,6 +87,14 @@
   // as the CM thread might take some time to wake up before noticing
   // that started() is set and set in_progress().
   bool during_cycle()      { return !idle(); }
+
+  // WhiteBox testing support.
+  const char* const* concurrent_phases() const;
+  bool request_concurrent_phase(const char* phase);
+
+  ConcurrentGCPhaseManager::Stack* phase_manager_stack() {
+    return &_phase_manager_stack;
+  }
 };
 
 #endif // SHARE_VM_GC_G1_CONCURRENTMARKTHREAD_HPP
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp	Thu Apr 13 16:38:39 2017 -0400
@@ -2487,6 +2487,18 @@
   _verifier->verify(vo);
 }
 
+bool G1CollectedHeap::supports_concurrent_phase_control() const {
+  return true;
+}
+
+const char* const* G1CollectedHeap::concurrent_phases() const {
+  return _cmThread->concurrent_phases();
+}
+
+bool G1CollectedHeap::request_concurrent_phase(const char* phase) {
+  return _cmThread->request_concurrent_phase(phase);
+}
+
 class PrintRegionClosure: public HeapRegionClosure {
   outputStream* _st;
 public:
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp	Thu Apr 13 16:38:39 2017 -0400
@@ -1438,6 +1438,11 @@
   // full GC.
   void verify(VerifyOption vo);
 
+  // WhiteBox testing support.
+  virtual bool supports_concurrent_phase_control() const;
+  virtual const char* const* concurrent_phases() const;
+  virtual bool request_concurrent_phase(const char* phase);
+
   // The methods below are here for convenience and dispatch the
   // appropriate method depending on value of the given VerifyOption
   // parameter. The values for that parameter, and their meanings,
--- a/hotspot/src/share/vm/gc/shared/collectedHeap.cpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/gc/shared/collectedHeap.cpp	Thu Apr 13 16:38:39 2017 -0400
@@ -158,6 +158,22 @@
   trace_heap(GCWhen::AfterGC, gc_tracer);
 }
 
+// WhiteBox API support for concurrent collectors.  These are the
+// default implementations, for collectors which don't support this
+// feature.
+bool CollectedHeap::supports_concurrent_phase_control() const {
+  return false;
+}
+
+const char* const* CollectedHeap::concurrent_phases() const {
+  static const char* const result[] = { NULL };
+  return result;
+}
+
+bool CollectedHeap::request_concurrent_phase(const char* phase) {
+  return false;
+}
+
 // Memory state functions.
 
 
--- a/hotspot/src/share/vm/gc/shared/collectedHeap.hpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/gc/shared/collectedHeap.hpp	Thu Apr 13 16:38:39 2017 -0400
@@ -579,6 +579,31 @@
   // Heap verification
   virtual void verify(VerifyOption option) = 0;
 
+  // Return true if concurrent phase control (via
+  // request_concurrent_phase_control) is supported by this collector.
+  // The default implementation returns false.
+  virtual bool supports_concurrent_phase_control() const;
+
+  // Return a NULL terminated array of concurrent phase names provided
+  // by this collector.  Supports Whitebox testing.  These are the
+  // names recognized by request_concurrent_phase(). The default
+  // implementation returns an array of one NULL element.
+  virtual const char* const* concurrent_phases() const;
+
+  // Request the collector enter the indicated concurrent phase, and
+  // wait until it does so.  Supports WhiteBox testing.  Only one
+  // request may be active at a time.  Phases are designated by name;
+  // the set of names and their meaning is GC-specific.  Once the
+  // requested phase has been reached, the collector will attempt to
+  // avoid transitioning to a new phase until a new request is made.
+  // [Note: A collector might not be able to remain in a given phase.
+  // For example, a full collection might cancel an in-progress
+  // concurrent collection.]
+  //
+  // Returns true when the phase is reached.  Returns false for an
+  // unknown phase.  The default implementation returns false.
+  virtual bool request_concurrent_phase(const char* phase);
+
   // Non product verification and debugging.
 #ifndef PRODUCT
   // Support for PromotionFailureALot.  Return true if it's time to cause a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/shared/concurrentGCPhaseManager.cpp	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017, 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 "gc/shared/concurrentGCPhaseManager.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/thread.hpp"
+
+#define assert_ConcurrentGC_thread() \
+  assert(Thread::current()->is_ConcurrentGC_thread(), "precondition")
+
+#define assert_not_enter_unconstrained(phase) \
+  assert((phase) != UNCONSTRAINED_PHASE, "Cannot enter \"unconstrained\" phase")
+
+#define assert_manager_is_tos(manager, stack, kind)  \
+  assert((manager) == (stack)->_top, kind " manager is not top of stack")
+
+ConcurrentGCPhaseManager::Stack::Stack() :
+  _requested_phase(UNCONSTRAINED_PHASE),
+  _top(NULL)
+{ }
+
+ConcurrentGCPhaseManager::ConcurrentGCPhaseManager(int phase, Stack* stack) :
+  _phase(phase),
+  _active(true),
+  _prev(NULL),
+  _stack(stack)
+{
+  assert_ConcurrentGC_thread();
+  assert_not_enter_unconstrained(phase);
+  assert(stack != NULL, "precondition");
+  MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
+  if (stack->_top != NULL) {
+    assert(stack->_top->_active, "precondition");
+    _prev = stack->_top;
+  }
+  stack->_top = this;
+  ml.notify_all();
+}
+
+ConcurrentGCPhaseManager::~ConcurrentGCPhaseManager() {
+  assert_ConcurrentGC_thread();
+  MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
+  assert_manager_is_tos(this, _stack, "This");
+  wait_when_requested_impl();
+  _stack->_top = _prev;
+  ml.notify_all();
+}
+
+bool ConcurrentGCPhaseManager::is_requested() const {
+  assert_ConcurrentGC_thread();
+  MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
+  assert_manager_is_tos(this, _stack, "This");
+  return _active && (_stack->_requested_phase == _phase);
+}
+
+bool ConcurrentGCPhaseManager::wait_when_requested_impl() const {
+  assert_ConcurrentGC_thread();
+  assert_lock_strong(CGCPhaseManager_lock);
+  bool waited = false;
+  while (_active && (_stack->_requested_phase == _phase)) {
+    waited = true;
+    CGCPhaseManager_lock->wait(Mutex::_no_safepoint_check_flag);
+  }
+  return waited;
+}
+
+bool ConcurrentGCPhaseManager::wait_when_requested() const {
+  assert_ConcurrentGC_thread();
+  MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
+  assert_manager_is_tos(this, _stack, "This");
+  return wait_when_requested_impl();
+}
+
+void ConcurrentGCPhaseManager::set_phase(int phase, bool force) {
+  assert_ConcurrentGC_thread();
+  assert_not_enter_unconstrained(phase);
+  MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
+  assert_manager_is_tos(this, _stack, "This");
+  if (!force) wait_when_requested_impl();
+  _phase = phase;
+  ml.notify_all();
+}
+
+void ConcurrentGCPhaseManager::deactivate() {
+  assert_ConcurrentGC_thread();
+  MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
+  assert_manager_is_tos(this, _stack, "This");
+  _active = false;
+  ml.notify_all();
+}
+
+bool ConcurrentGCPhaseManager::wait_for_phase(int phase, Stack* stack) {
+  assert(Thread::current()->is_Java_thread(), "precondition");
+  assert(stack != NULL, "precondition");
+  MonitorLockerEx ml(CGCPhaseManager_lock);
+  // Update request and notify service of change.
+  if (stack->_requested_phase != phase) {
+    stack->_requested_phase = phase;
+    ml.notify_all();
+  }
+
+  if (phase == UNCONSTRAINED_PHASE) {
+    return true;
+  }
+
+  // Wait until phase or IDLE is active.
+  while (true) {
+    bool idle = false;
+    for (ConcurrentGCPhaseManager* manager = stack->_top;
+         manager != NULL;
+         manager = manager->_prev) {
+      if (manager->_phase == phase) {
+        return true;            // phase is active.
+      } else if (manager->_phase == IDLE_PHASE) {
+        idle = true;            // Note idle active, continue search for phase.
+      }
+    }
+    if (idle) {
+      return false;             // idle is active and phase is not.
+    } else {
+      ml.wait();                // Wait for phase change.
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/shared/concurrentGCPhaseManager.hpp	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2017, 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_VM_GC_CONCURRENTGCPHASEMANAGER_HPP
+#define SHARE_VM_GC_CONCURRENTGCPHASEMANAGER_HPP
+
+#include "memory/allocation.hpp"
+
+// Manage concurrent phase information, to support WhiteBox testing.
+// Managers are stack allocated.  Managers may be nested, to support
+// nested subphases.
+class ConcurrentGCPhaseManager : public StackObj {
+public:
+
+  // Special phase ids used by all GC's that use this facility.
+  static const int UNCONSTRAINED_PHASE = 0; // Unconstrained or no request.
+  static const int IDLE_PHASE = 1;          // Concurrent processing is idle.
+
+  // Stack of phase managers.
+  class Stack VALUE_OBJ_CLASS_SPEC {
+    friend class ConcurrentGCPhaseManager;
+
+  public:
+    // Create an empty stack of phase managers.
+    Stack();
+
+  private:
+    int _requested_phase;
+    ConcurrentGCPhaseManager* _top;
+
+    // Non-copyable - never defined.
+    Stack(const Stack&);
+    Stack& operator=(const Stack&);
+  };
+
+  // Construct and push a new manager on the stack, activating phase.
+  // Notifies callers in wait_for_phase of the phase change.
+  //
+  // Preconditions:
+  // - Calling thread must be a ConcurrentGC thread
+  // - phase != UNCONSTRAINED_PHASE
+  // - stack != NULL
+  // - other managers on stack must all be active.
+  ConcurrentGCPhaseManager(int phase, Stack* stack);
+
+  // Pop this manager off the stack, deactivating phase.  Before
+  // changing phases, if is_requested() is true, wait until the
+  // request is changed.  After changing phases, notifies callers of
+  // wait_for_phase of the phase change.
+  //
+  // Preconditions:
+  // - Calling thread must be a ConcurrentGC thread
+  // - this must be the current top of the manager stack
+  ~ConcurrentGCPhaseManager();
+
+  // Returns true if this phase is active and is currently requested.
+  //
+  // Preconditions:
+  // - Calling thread must be a ConcurrentGC thread
+  // - this must be the current top of manager stack
+  bool is_requested() const;
+
+  // Wait until is_requested() is false.  Returns true if waited.
+  //
+  // Preconditions:
+  // - Calling thread must be a ConcurrentGC thread
+  // - this must be the current top of manager stack
+  bool wait_when_requested() const;
+
+  // Directly step from one phase to another, without needing to pop a
+  // manager from the stack and allocate a new one.  Before changing
+  // phases, if is_requested() is true and force is false, wait until
+  // the request is changed.  After changing phases, notifies callers
+  // of wait_for_phase of the phase change.
+  //
+  // Preconditions:
+  // - Calling thread must be a ConcurrentGC thread
+  // - phase != UNCONSTRAINED_PHASE
+  // - this must be the current top of manager stack
+  void set_phase(int phase, bool force);
+
+  // Deactivate the manager.  An inactive manager no longer blocks
+  // transitions out of the associated phase when that phase has been
+  // requested.
+  //
+  // Preconditions:
+  // - Calling thread must be a ConcurrentGC thread
+  // - this must be the current top of manager stack
+  void deactivate();
+
+  // Used to implement CollectorPolicy::request_concurrent_phase().
+  // Updates request to the new phase, and notifies threads blocked on
+  // the old request of the change.  Returns true if the phase is
+  // UNCONSTRAINED_PHASE.  Otherwise, waits until an active phase is
+  // the requested phase (returning true) or IDLE_PHASE (returning
+  // false if not also the requested phase).
+  //
+  // Preconditions:
+  // - Calling thread must be a Java thread
+  // - stack must be non-NULL
+  static bool wait_for_phase(int phase, Stack* stack);
+
+private:
+  int _phase;
+  bool _active;
+  ConcurrentGCPhaseManager* _prev;
+  Stack* _stack;
+
+  // Non-copyable - never defined.
+  ConcurrentGCPhaseManager(const ConcurrentGCPhaseManager&);
+  ConcurrentGCPhaseManager& operator=(const ConcurrentGCPhaseManager&);
+
+  bool wait_when_requested_impl() const;
+};
+
+#endif // include guard
--- a/hotspot/src/share/vm/prims/whitebox.cpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/prims/whitebox.cpp	Thu Apr 13 16:38:39 2017 -0400
@@ -357,6 +357,43 @@
   return (jlong)alignment;
 WB_END
 
+WB_ENTRY(jboolean, WB_SupportsConcurrentGCPhaseControl(JNIEnv* env, jobject o))
+  return Universe::heap()->supports_concurrent_phase_control();
+WB_END
+
+WB_ENTRY(jobjectArray, WB_GetConcurrentGCPhases(JNIEnv* env, jobject o))
+  const char* const* phases = Universe::heap()->concurrent_phases();
+  jint nphases = 0;
+  for ( ; phases[nphases] != NULL; ++nphases) ;
+
+  ResourceMark rm(thread);
+  ThreadToNativeFromVM ttn(thread);
+  jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string());
+  CHECK_JNI_EXCEPTION_(env, NULL);
+
+  jobjectArray result = env->NewObjectArray(nphases, clazz, NULL);
+  CHECK_JNI_EXCEPTION_(env, NULL);
+
+  // If push fails, return with pending exception.
+  if (env->PushLocalFrame(nphases) < 0) return NULL;
+  for (jint i = 0; i < nphases; ++i) {
+    jstring phase = env->NewStringUTF(phases[i]);
+    CHECK_JNI_EXCEPTION_(env, NULL);
+    env->SetObjectArrayElement(result, i, phase);
+    CHECK_JNI_EXCEPTION_(env, NULL);
+  }
+  env->PopLocalFrame(NULL);
+
+  return result;
+WB_END
+
+WB_ENTRY(jboolean, WB_RequestConcurrentGCPhase(JNIEnv* env, jobject o, jstring name))
+  Handle h_name(THREAD, JNIHandles::resolve(name));
+  ResourceMark rm;
+  const char* c_name = java_lang_String::as_utf8_string(h_name());
+  return Universe::heap()->request_concurrent_phase(c_name);
+WB_END
+
 #if INCLUDE_ALL_GCS
 WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj))
   if (UseG1GC) {
@@ -1969,6 +2006,11 @@
   {CC"currentGC",                 CC"()I",            (void*)&WB_CurrentGC},
   {CC"allSupportedGC",            CC"()I",            (void*)&WB_AllSupportedGC},
   {CC"gcSelectedByErgo",          CC"()Z",            (void*)&WB_GCSelectedByErgo},
+  {CC"supportsConcurrentGCPhaseControl", CC"()Z",     (void*)&WB_SupportsConcurrentGCPhaseControl},
+  {CC"getConcurrentGCPhases",     CC"()[Ljava/lang/String;",
+                                                      (void*)&WB_GetConcurrentGCPhases},
+  {CC"requestConcurrentGCPhase0", CC"(Ljava/lang/String;)Z",
+                                                      (void*)&WB_RequestConcurrentGCPhase},
 };
 
 #undef CC
--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp	Thu Apr 13 16:38:39 2017 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -81,6 +81,7 @@
 Mutex*   MarkStackChunkList_lock      = NULL;
 Mutex*   ParGCRareEvent_lock          = NULL;
 Mutex*   DerivedPointerTableGC_lock   = NULL;
+Monitor* CGCPhaseManager_lock         = NULL;
 Mutex*   Compile_lock                 = NULL;
 Monitor* MethodCompileQueue_lock      = NULL;
 Monitor* CompileThread_lock           = NULL;
@@ -203,6 +204,9 @@
   }
   def(ParGCRareEvent_lock          , PaddedMutex  , leaf     ,   true,  Monitor::_safepoint_check_sometimes);
   def(DerivedPointerTableGC_lock   , PaddedMutex  , leaf,        true,  Monitor::_safepoint_check_never);
+#ifdef INCLUDE_ALL_GCS
+  def(CGCPhaseManager_lock         , PaddedMonitor, leaf,        false, Monitor::_safepoint_check_sometimes);
+#endif
   def(CodeCache_lock               , PaddedMutex  , special,     true,  Monitor::_safepoint_check_never);
   def(RawMonitor_lock              , PaddedMutex  , special,     true,  Monitor::_safepoint_check_never);
   def(OopMapCacheAlloc_lock        , PaddedMutex  , leaf,        true,  Monitor::_safepoint_check_always);     // used for oop_map_cache allocation.
--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp	Thu Apr 13 11:23:37 2017 -0700
+++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp	Thu Apr 13 16:38:39 2017 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -57,6 +57,7 @@
 extern Mutex*   TouchedMethodLog_lock;           // a lock on allocation of LogExecutedMethods info
 extern Mutex*   RetData_lock;                    // a lock on installation of RetData inside method data
 extern Mutex*   DerivedPointerTableGC_lock;      // a lock to protect the derived pointer table
+extern Monitor* CGCPhaseManager_lock;            // a lock to protect a concurrent GC's phase management
 extern Monitor* VMOperationQueue_lock;           // a lock on queue of vm_operations waiting to execute
 extern Monitor* VMOperationRequest_lock;         // a lock on Threads waiting for a vm_operation to terminate
 extern Monitor* Safepoint_lock;                  // a lock used by the safepoint abstraction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrent_phase_control/CheckControl.java	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2017, 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 gc.concurrent_phase_control;
+
+/*
+ * Utility class that uses the WhiteBox concurrent GC phase control to
+ * step through a provided sequence of phases, and verify that the
+ * phases were actually reached as expected.
+ *
+ * To use:
+ *
+ * (1) The main test class has a main function which calls this helper
+ * class's check() function with appropriate arguments for the
+ * collector being tested.
+ *
+ * (2) The test program must provide access to WhiteBox, as it is used
+ * by this support class.
+ *
+ * (4) The main test class should be invoked as a driver.  This
+ * helper class's check() function will run its Executor class in a
+ * subprocess, in order to capture its output for analysis.
+ */
+
+import sun.hotspot.WhiteBox;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public final class CheckControl {
+    // gcName: The name of the GC, logged as "Using <name>" near the
+    // beginning of the log output.
+    //
+    // gcOptions: Command line options for invoking the desired
+    // collector and logging options to produce output that can be
+    // matched against the regex patterns in the gcPhaseInfo pairs.
+    //
+    // gcPhaseInfo: An array of pairs of strings.  Each pair is a
+    // phase name and a regex pattern for recognizing the associated
+    // log message.  The regex pattern can be null if no log message
+    // is associated with the named phase.  The test will iterate
+    // through the array, requesting each phase in turn.
+    public static void check(String gcName,
+                             String[] gcOptions,
+                             String[][] gcPhaseInfo) throws Exception {
+        String[] stepPhases = new String[gcPhaseInfo.length];
+        for (int i = 0; i < gcPhaseInfo.length; ++i) {
+            stepPhases[i] = gcPhaseInfo[i][0];
+        }
+        String messages = executeTest(gcName, gcOptions, stepPhases);
+        checkPhaseControl(messages, gcPhaseInfo);
+    }
+
+    private static void fail(String message) throws Exception {
+        throw new RuntimeException(message);
+    }
+
+    private static final String requestPrefix = "Requesting concurrent phase: ";
+    private static final String reachedPrefix = "Reached concurrent phase: ";
+
+    private static String executeTest(String gcName,
+                                      String[] gcOptions,
+                                      String[] gcStepPhases) throws Exception {
+        System.out.println("\n---------- Testing ---------");
+
+        final String[] wb_arguments = {
+            "-Xbootclasspath/a:.",
+            "-XX:+UnlockDiagnosticVMOptions",
+            "-XX:+WhiteBoxAPI"
+        };
+
+        List<String> arglist = new ArrayList<String>();
+        Collections.addAll(arglist, wb_arguments);
+        Collections.addAll(arglist, gcOptions);
+        arglist.add(Executor.class.getName());
+        Collections.addAll(arglist, gcStepPhases);
+        String[] arguments = arglist.toArray(new String[arglist.size()]);
+
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(arguments);
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+        String messages = output.getStdout();
+        System.out.println(messages);
+
+        output.shouldHaveExitValue(0);
+        output.shouldContain("Using " + gcName);
+
+        return messages;
+    }
+
+    private static void checkPhaseControl(String messages,
+                                          String[][] gcPhaseInfo)
+        throws Exception
+    {
+        // Iterate through the phase sequence for the test, verifying
+        // output contains appropriate sequences of request message,
+        // log message for phase, and request reached message.  Note
+        // that a log message for a phase may occur later than the
+        // associated request reached message, or even the following
+        // request message.
+
+        Pattern nextReqP = Pattern.compile(requestPrefix);
+        Matcher nextReqM = nextReqP.matcher(messages);
+
+        Pattern nextReachP = Pattern.compile(reachedPrefix);
+        Matcher nextReachM = nextReachP.matcher(messages);
+
+        String pendingPhaseMessage = null;
+        int pendingPhaseMessagePosition = -1;
+
+        int position = 0;
+        for (String[] phase: gcPhaseInfo) {
+            String phaseName = phase[0];
+            String phaseMsg = phase[1];
+
+            System.out.println("Checking phase " + phaseName);
+
+            // Update the "next" matchers to refer to the next
+            // corresponding pair of request and reached messages.
+            if (!nextReqM.find()) {
+                fail("Didn't find next phase request");
+            } else if ((position != 0) && (nextReqM.start() < nextReachM.end())) {
+                fail("Next request before previous reached");
+            } else if (!nextReachM.find()) {
+                fail("Didn't find next phase reached");
+            } else if (nextReachM.start() <= nextReqM.end()) {
+                fail("Next request/reached misordered");
+            }
+
+            // Find the expected request message, and ensure it is the next.
+            Pattern reqP = Pattern.compile(requestPrefix + phaseName);
+            Matcher reqM = reqP.matcher(messages);
+            if (!reqM.find(position)) {
+                fail("Didn't find request for " + phaseName);
+            } else if (reqM.start() != nextReqM.start()) {
+                fail("Request mis-positioned for " + phaseName);
+            }
+
+            // Find the expected reached message, and ensure it is the next.
+            Pattern reachP = Pattern.compile(reachedPrefix + phaseName);
+            Matcher reachM = reachP.matcher(messages);
+            if (!reachM.find(position)) {
+                fail("Didn't find reached for " + phaseName);
+            } else if (reachM.start() != nextReachM.start()) {
+                fail("Reached mis-positioned for " + phaseName);
+            }
+
+            // If there is a pending log message (see below), ensure
+            // it was before the current reached message.
+            if (pendingPhaseMessage != null) {
+                if (pendingPhaseMessagePosition >= reachM.start()) {
+                    fail("Log message after next reached message: " +
+                         pendingPhaseMessage);
+                }
+            }
+
+            // If the phase has an associated logging message, verify
+            // such a logging message is present following the
+            // request, and otherwise positioned appropriately.  The
+            // complication here is that the logging message
+            // associated with a request might follow the reached
+            // message, and even the next request message, if there is
+            // a later request.  But it must preceed the next
+            // logging message and the next reached message.
+            boolean clearPendingPhaseMessage = true;
+            if (phaseMsg != null) {
+                Pattern logP = Pattern.compile("GC\\(\\d+\\)\\s+" + phaseMsg);
+                Matcher logM = logP.matcher(messages);
+                if (!logM.find(reqM.end())) {
+                    fail("Didn't find message " + phaseMsg);
+                }
+
+                if (pendingPhaseMessage != null) {
+                    if (pendingPhaseMessagePosition >= logM.start()) {
+                        fail("Log messages out of order: " +
+                             pendingPhaseMessage + " should preceed " +
+                             phaseMsg);
+                    }
+                }
+
+                if (reachM.end() <= logM.start()) {
+                    clearPendingPhaseMessage = false;
+                    pendingPhaseMessage = phaseMsg;
+                    pendingPhaseMessagePosition = logM.end();
+                }
+            }
+            if (clearPendingPhaseMessage) {
+                pendingPhaseMessage = null;
+                pendingPhaseMessagePosition = -1;
+            }
+
+            // Update position for start of next phase search.
+            position = reachM.end();
+        }
+        // It's okay for there to be a leftover pending phase message.
+        // We know it was found before the end of the log.
+    }
+
+    private static final class Executor {
+        private static final WhiteBox WB = WhiteBox.getWhiteBox();
+
+        private static void step(String phase) {
+            System.out.println(requestPrefix + phase);
+            WB.requestConcurrentGCPhase(phase);
+            System.out.println(reachedPrefix + phase);
+        }
+
+        public static void main(String[] phases) throws Exception {
+            // Iterate through set sequence of phases, reporting each.
+            for (String phase: phases) {
+                step(phase);
+            }
+            // Wait a little to allow a delayed logging message for
+            // the final request/reached to be printed before exiting
+            // the program.
+            Thread.sleep(250);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrent_phase_control/CheckSupported.java	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2017, 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 gc.concurrent_phase_control;
+
+/*
+ * Utility class that provides verification of expected behavior of
+ * the Concurrent GC Phase Control WB API when the current GC supports
+ * phase control.  The invoking test must provide WhiteBox access.
+ */
+
+import sun.hotspot.WhiteBox;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class CheckSupported {
+
+    private static final WhiteBox WB = WhiteBox.getWhiteBox();
+
+    private static Set<String> toSet(List<String> list, String which)
+        throws Exception
+    {
+        Set<String> result = new HashSet<String>(list);
+        if (result.size() < list.size()) {
+            throw new RuntimeException(which + " phases contains duplicates");
+        }
+        return result;
+    }
+
+    private static void checkPhases(String[] expectedPhases) throws Exception {
+        String[] actualPhases = WB.getConcurrentGCPhases();
+
+        List<String> expectedList = Arrays.asList(expectedPhases);
+        List<String> actualList = Arrays.asList(actualPhases);
+
+        Set<String> expected = toSet(expectedList, "Expected");
+        Set<String> actual = toSet(actualList, "Actual");
+
+        expected.removeAll(actualList);
+        actual.removeAll(expectedList);
+
+        boolean match = true;
+        if (!expected.isEmpty()) {
+            match = false;
+            System.out.println("Unexpected phases:");
+            for (String s: expected) {
+                System.out.println("  " + s);
+            }
+        }
+        if (!actual.isEmpty()) {
+            match = false;
+            System.out.println("Expected but missing phases:");
+            for (String s: actual) {
+                System.out.println("  " + s);
+            }
+        }
+        if (!match) {
+            throw new RuntimeException("Mismatch between expected and actual phases");
+        }
+    }
+
+    public static void check(String gcName, String[] phases) throws Exception {
+        // Verify supported.
+        if (!WB.supportsConcurrentGCPhaseControl()) {
+            throw new RuntimeException(
+                gcName + " unexpectedly missing phase control support");
+        }
+
+        checkPhases(phases);
+
+        // Verify IllegalArgumentException thrown by request attempt
+        // with unknown phase.
+        boolean illegalArgumentThrown = false;
+        try {
+            WB.requestConcurrentGCPhase("UNKNOWN PHASE");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+            illegalArgumentThrown = true;
+        } catch (Exception e) {
+            throw new RuntimeException(
+                gcName + ": Unexpected exception when requesting unknown phase: " + e.toString());
+        }
+        if (!illegalArgumentThrown) {
+            throw new RuntimeException(
+                gcName + ": No exception when requesting unknown phase");
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrent_phase_control/CheckUnsupported.java	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017, 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 gc.concurrent_phase_control;
+
+/*
+ * Utility class that provides verification of expected behavior of
+ * the Concurrent GC Phase Control WB API when the current GC does not
+ * support phase control.  The invoking test must provide WhiteBox access.
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class CheckUnsupported {
+
+    private static final WhiteBox WB = WhiteBox.getWhiteBox();
+
+    public static void check(String gcName) throws Exception {
+        // Verify unsupported.
+        if (WB.supportsConcurrentGCPhaseControl()) {
+            throw new RuntimeException(
+                gcName + " unexpectedly supports phase control");
+        }
+
+        // Verify phase sequence is empty.
+        String[] phases = WB.getConcurrentGCPhases();
+        if (phases.length > 0) {
+            throw new RuntimeException(
+                gcName + " unexpectedly has non-empty phases");
+        }
+
+        // Verify IllegalStateException thrown by request attempt.
+        boolean illegalStateThrown = false;
+        try {
+            WB.requestConcurrentGCPhase("UNKNOWN PHASE");
+        } catch (IllegalStateException e) {
+            // Expected.
+            illegalStateThrown = true;
+        } catch (Exception e) {
+            throw new RuntimeException(
+                gcName + ": Unexpected exception when requesting phase: "
+                + e.toString());
+        }
+        if (!illegalStateThrown) {
+            throw new RuntimeException(
+                gcName + ": No exception when requesting phase");
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlCMS.java	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017, 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 TestConcurrentPhaseControlCMS
+ * @bug 8169517
+ * @requires vm.gc.ConcMarkSweep
+ * @summary Verify CMS GC doesn't support WhiteBox concurrent phase control.
+ * @key gc
+ * @modules java.base
+ * @library /test/lib /
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *    sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UseConcMarkSweepGC
+ *   -Xbootclasspath/a:.
+ *   -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *   TestConcurrentPhaseControlCMS
+ */
+
+import gc.concurrent_phase_control.CheckUnsupported;
+
+public class TestConcurrentPhaseControlCMS {
+
+    public static void main(String[] args) throws Exception {
+        CheckUnsupported.check("CMS");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlG1.java	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, 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 TestConcurrentPhaseControlG1
+ * @bug 8169517
+ * @requires vm.gc.G1
+ * @summary Test of WhiteBox concurrent GC phase control for G1.
+ * @key gc
+ * @modules java.base
+ * @library /test/lib /
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *    sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run driver TestConcurrentPhaseControlG1
+ */
+
+import gc.concurrent_phase_control.CheckControl;
+
+public class TestConcurrentPhaseControlG1 {
+
+    // Pairs of phase name and regex to match log stringm for stepping through,
+    private static final String[][] g1PhaseInfo = {
+        // Step through the phases in order.
+        {"IDLE", null},
+        {"CONCURRENT_CYCLE", "Concurrent Cycle"},
+        {"IDLE", null},  // Resume IDLE before testing subphases
+        {"CLEAR_CLAIMED_MARKS", "Concurrent Clear Claimed Marks"},
+        {"SCAN_ROOT_REGIONS", "Concurrent Scan Root Regions"},
+        // ^F so not "From Roots", ^R so not "Restart"
+        {"CONCURRENT_MARK", "Concurrent Mark [^FR]"},
+        {"IDLE", null},  // Resume IDLE before testing subphases
+        {"MARK_FROM_ROOTS", "Concurrent Mark From Roots"},
+        {"BEFORE_REMARK", null},
+        {"REMARK", "Pause Remark"},
+        {"CREATE_LIVE_DATA", "Concurrent Create Live Data"},
+        // "COMPLETE_CLEANUP",  -- optional phase, not reached by this test
+        {"CLEANUP_FOR_NEXT_MARK", "Concurrent Cleanup for Next Mark"},
+        // Clear request
+        {"IDLE", null},
+        {"ANY", null},
+        // Request a phase.
+        {"MARK_FROM_ROOTS", "Concurrent Mark From Roots"},
+        // Request an earlier phase, to ensure loop rather than stuck at idle.
+        {"SCAN_ROOT_REGIONS", "Concurrent Scan Root Regions"},
+        // Clear request, to unblock service.
+        {"IDLE", null},
+        {"ANY", null},
+    };
+
+    private static final String[] g1Options =
+        new String[]{"-XX:+UseG1GC",  "-Xlog:gc,gc+marking"};
+
+    private static final String g1Name = "G1";
+
+    public static void main(String[] args) throws Exception {
+        CheckControl.check(g1Name, g1Options, g1PhaseInfo);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlG1Basics.java	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017, 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 TestConcurrentPhaseControlG1Basics
+ * @bug 8169517
+ * @requires vm.gc.G1
+ * @summary Verify G1 supports concurrent phase control and has the
+ * expected set of phases.
+ * @key gc
+ * @modules java.base
+ * @library /test/lib /
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *    sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UseG1GC
+ *   -Xbootclasspath/a:.
+ *   -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *   TestConcurrentPhaseControlG1Basics
+ */
+
+import gc.concurrent_phase_control.CheckSupported;
+
+public class TestConcurrentPhaseControlG1Basics {
+
+    private static final String[] phases = {
+        "ANY",
+        "IDLE",
+        "CONCURRENT_CYCLE",
+        "CLEAR_CLAIMED_MARKS",
+        "SCAN_ROOT_REGIONS",
+        "CONCURRENT_MARK",
+        "MARK_FROM_ROOTS",
+        "BEFORE_REMARK",
+        "REMARK",
+        "CREATE_LIVE_DATA",
+        "COMPLETE_CLEANUP",
+        "CLEANUP_FOR_NEXT_MARK",
+    };
+
+    public static void main(String[] args) throws Exception {
+        CheckSupported.check("G1", phases);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlParallel.java	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017, 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 TestConcurrentPhaseControlParallel
+ * @bug 8169517
+ * @requires vm.gc.Parallel
+ * @summary Verify Parallel GC doesn't support WhiteBox concurrent phase control.
+ * @key gc
+ * @modules java.base
+ * @library /test/lib /
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *    sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UseParallelGC
+ *   -Xbootclasspath/a:.
+ *   -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *   TestConcurrentPhaseControlParallel
+ */
+
+import gc.concurrent_phase_control.CheckUnsupported;
+
+public class TestConcurrentPhaseControlParallel {
+
+    public static void main(String[] args) throws Exception {
+        CheckUnsupported.check("Parallel");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlSerial.java	Thu Apr 13 16:38:39 2017 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017, 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 TestConcurrentPhaseControlSerial
+ * @bug 8169517
+ * @requires vm.gc.Serial
+ * @summary Verify Serial GC doesn't support WhiteBox concurrent phase control.
+ * @key gc
+ * @modules java.base
+ * @library /test/lib /
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *    sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UseSerialGC
+ *   -Xbootclasspath/a:.
+ *   -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *   TestConcurrentPhaseControlSerial
+ */
+
+import gc.concurrent_phase_control.CheckUnsupported;
+
+public class TestConcurrentPhaseControlSerial {
+
+    public static void main(String[] args) throws Exception {
+        CheckUnsupported.check("Serial");
+    }
+}