--- old/src/share/vm/gc/g1/concurrentMarkThread.cpp 2017-03-09 23:56:49.284626771 -0500 +++ new/src/share/vm/gc/g1/concurrentMarkThread.cpp 2017-03-09 23:56:49.168621107 -0500 @@ -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(title), - _cm(cm) { + GCTraceConcTimeImpl(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(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)); - - for (uint iter = 1; true; ++iter) { - if (!cm()->has_aborted()) { - G1ConcPhaseTimer t(_cm, "Concurrent Mark From Roots"); - _cm->mark_from_roots(); - } - - double mark_end_time = os::elapsedVTime(); - jlong mark_end = os::elapsed_counter(); - _vtime_mark_accum += (mark_end_time - cycle_start); - if (!cm()->has_aborted()) { + // 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; + + // Provide a control point after mark_from_roots. + { + G1ConcPhaseManager p(G1ConcurrentPhase::BEFORE_REMARK, this); + } + if (cm()->has_aborted()) break; + + // 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(); } --- old/src/share/vm/gc/g1/concurrentMarkThread.hpp 2017-03-09 23:56:49.828653335 -0500 +++ new/src/share/vm/gc/g1/concurrentMarkThread.hpp 2017-03-09 23:56:49.712647666 -0500 @@ -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 --- old/src/share/vm/gc/g1/g1CollectedHeap.cpp 2017-03-09 23:56:50.352678913 -0500 +++ new/src/share/vm/gc/g1/g1CollectedHeap.cpp 2017-03-09 23:56:50.232673051 -0500 @@ -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 @@ -2495,6 +2495,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: --- old/src/share/vm/gc/g1/g1CollectedHeap.hpp 2017-03-09 23:56:50.940707623 -0500 +++ new/src/share/vm/gc/g1/g1CollectedHeap.hpp 2017-03-09 23:56:50.824701960 -0500 @@ -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 @@ -1434,6 +1434,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, --- old/src/share/vm/gc/shared/collectedHeap.cpp 2017-03-09 23:56:51.488734372 -0500 +++ new/src/share/vm/gc/shared/collectedHeap.cpp 2017-03-09 23:56:51.376728904 -0500 @@ -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 @@ -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. --- old/src/share/vm/gc/shared/collectedHeap.hpp 2017-03-09 23:56:52.028760741 -0500 +++ new/src/share/vm/gc/shared/collectedHeap.hpp 2017-03-09 23:56:51.912755073 -0500 @@ -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 @@ -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 --- old/src/share/vm/prims/whitebox.cpp 2017-03-09 23:56:52.560786714 -0500 +++ new/src/share/vm/prims/whitebox.cpp 2017-03-09 23:56:52.444781046 -0500 @@ -355,6 +355,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)) G1CollectedHeap* g1 = G1CollectedHeap::heap(); @@ -1905,6 +1942,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 --- old/src/share/vm/runtime/mutexLocker.cpp 2017-03-09 23:56:53.112813660 -0500 +++ new/src/share/vm/runtime/mutexLocker.cpp 2017-03-09 23:56:53.000808192 -0500 @@ -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. --- old/src/share/vm/runtime/mutexLocker.hpp 2017-03-09 23:56:53.640839438 -0500 +++ new/src/share/vm/runtime/mutexLocker.hpp 2017-03-09 23:56:53.528833970 -0500 @@ -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 2017-03-08 17:39:57.499858527 -0500 +++ new/src/share/vm/gc/shared/concurrentGCPhaseManager.cpp 2017-03-09 23:56:54.060859948 -0500 @@ -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 2017-03-08 17:39:57.499858527 -0500 +++ new/src/share/vm/gc/shared/concurrentGCPhaseManager.hpp 2017-03-09 23:56:54.548883773 -0500 @@ -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 --- /dev/null 2017-03-08 17:39:57.499858527 -0500 +++ new/test/gc/whitebox/concurrent_phase_control/TestConcurrentPhaseControlBasics.java 2017-03-09 23:56:55.028907208 -0500 @@ -0,0 +1,95 @@ +/* + * 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 TestConcurrentPhaseControlBasics + * @bug 8169517 + * @summary Basic tests of WhiteBox concurrent GC phase control. + * @key gc + * @modules java.base + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * TestConcurrentPhaseControlBasics + */ + +import sun.hotspot.WhiteBox; + +public class TestConcurrentPhaseControlBasics { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static boolean checkHasPhases(boolean supported) { + System.out.println("Expecting " + (supported ? "non-" : "") + "empty phase list"); + String[] phases = WB.getConcurrentGCPhases(); + return phases.length != 0; + } + + private static boolean request() throws Exception { + WB.requestConcurrentGCPhase("UNKNOWN PHASE"); + return false; + } + + private static boolean checkUnknownPhase() throws Exception { + System.out.println("Expecting IllegalArgumentException"); + try { + return request(); + } catch (IllegalArgumentException e) { + return true; + } + } + + private static boolean checkSupported() throws Exception { + return checkHasPhases(true) && checkUnknownPhase(); + } + + private static boolean checkUnsupportedRequest() throws Exception { + System.out.println("Expecting IllegalStateException"); + try { + return request(); + } catch (IllegalStateException e) { + return true; + } + } + + private static boolean checkUnsupported() throws Exception { + return !checkHasPhases(false) && checkUnsupportedRequest(); + } + + private static boolean check() throws Exception { + if (WB.supportsConcurrentGCPhaseControl()) { + return checkSupported(); + } else { + return checkUnsupported(); + } + } + + public static void main(String[] args) throws Exception { + if (!check()) { + throw new RuntimeException("Test failed"); + } + } +} --- /dev/null 2017-03-08 17:39:57.499858527 -0500 +++ new/test/gc/whitebox/concurrent_phase_control/TestConcurrentPhaseControlG1.java 2017-03-09 23:56:55.520931224 -0500 @@ -0,0 +1,107 @@ +/* + * 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. + * @key gc + * @modules java.base + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run driver TestConcurrentPhaseControlG1 + */ + +import jdk.test.lib.gc.ConcurrentPhaseControlUtil; + +public class TestConcurrentPhaseControlG1 { + // All requestable phases. + private static final String[] g1AllPhases = { + "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", + }; + + // Pairs of phase name and regex to match log stringm for stepping through, + private static final String[][] g1StepPhases = { + // 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 { + ConcurrentPhaseControlUtil test = + new ConcurrentPhaseControlUtil(PhaseStepper.class.getName(), + g1StepPhases, + g1Options, + g1Name); + test.run(); + } + + public static class PhaseStepper { + public static void main(String[] args) throws Exception { + ConcurrentPhaseControlUtil.Executor executor = + new ConcurrentPhaseControlUtil.Executor(g1AllPhases, + g1StepPhases); + executor.run(); + } + } +}