# HG changeset patch # User pliden # Date 1397212164 -7200 # Node ID 5dbf1f44de1808b5efba11245c0918a6d732783a # Parent 095cc0a63ed9ff19910e4ab99417d061f791e159 8039147: Cleanup SuspendibleThreadSet Reviewed-by: brutisso, tschatzl, mgerdin diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -71,6 +71,7 @@ } void ConcurrentG1RefineThread::sample_young_list_rs_lengths() { + SuspendibleThreadSetJoiner sts; G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1CollectorPolicy* g1p = g1h->g1_policy(); if (g1p->adaptive_young_list_length()) { @@ -82,8 +83,8 @@ // we try to yield every time we visit 10 regions if (regions_visited == 10) { - if (_sts.should_yield()) { - _sts.yield("G1 refine"); + if (sts.should_yield()) { + sts.yield(); // we just abandon the iteration break; } @@ -99,9 +100,7 @@ DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); _vtime_start = os::elapsedVTime(); while(!_should_terminate) { - _sts.join(); sample_young_list_rs_lengths(); - _sts.leave(); if (os::supports_vtime()) { _vtime_accum = (os::elapsedVTime() - _vtime_start); @@ -182,37 +181,37 @@ break; } - _sts.join(); + { + SuspendibleThreadSetJoiner sts; + + do { + int curr_buffer_num = (int)dcqs.completed_buffers_num(); + // If the number of the buffers falls down into the yellow zone, + // that means that the transition period after the evacuation pause has ended. + if (dcqs.completed_queue_padding() > 0 && curr_buffer_num <= cg1r()->yellow_zone()) { + dcqs.set_completed_queue_padding(0); + } - do { - int curr_buffer_num = (int)dcqs.completed_buffers_num(); - // If the number of the buffers falls down into the yellow zone, - // that means that the transition period after the evacuation pause has ended. - if (dcqs.completed_queue_padding() > 0 && curr_buffer_num <= cg1r()->yellow_zone()) { - dcqs.set_completed_queue_padding(0); - } + if (_worker_id > 0 && curr_buffer_num <= _deactivation_threshold) { + // If the number of the buffer has fallen below our threshold + // we should deactivate. The predecessor will reactivate this + // thread should the number of the buffers cross the threshold again. + deactivate(); + break; + } - if (_worker_id > 0 && curr_buffer_num <= _deactivation_threshold) { - // If the number of the buffer has fallen below our threshold - // we should deactivate. The predecessor will reactivate this - // thread should the number of the buffers cross the threshold again. + // Check if we need to activate the next thread. + if (_next != NULL && !_next->is_active() && curr_buffer_num > _next->_threshold) { + _next->activate(); + } + } while (dcqs.apply_closure_to_completed_buffer(_worker_id + _worker_id_offset, cg1r()->green_zone())); + + // We can exit the loop above while being active if there was a yield request. + if (is_active()) { deactivate(); - break; } - - // Check if we need to activate the next thread. - if (_next != NULL && !_next->is_active() && curr_buffer_num > _next->_threshold) { - _next->activate(); - } - } while (dcqs.apply_closure_to_completed_buffer(_worker_id + _worker_id_offset, cg1r()->green_zone())); - - // We can exit the loop above while being active if there was a yield request. - if (is_active()) { - deactivate(); } - _sts.leave(); - if (os::supports_vtime()) { _vtime_accum = (os::elapsedVTime() - _vtime_start); } else { @@ -223,17 +222,6 @@ terminate(); } - -void ConcurrentG1RefineThread::yield() { - if (G1TraceConcRefinement) { - gclog_or_tty->print_cr("G1-Refine-yield"); - } - _sts.yield("G1 refine"); - if (G1TraceConcRefinement) { - gclog_or_tty->print_cr("G1-Refine-yield-end"); - } -} - void ConcurrentG1RefineThread::stop() { // it is ok to take late safepoints here, if needed { diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp Fri Apr 11 12:29:24 2014 +0200 @@ -64,9 +64,6 @@ void activate(); void deactivate(); - // For use by G1CollectedHeap, which is a friend. - static SuspendibleThreadSet* sts() { return &_sts; } - public: virtual void run(); // Constructor @@ -84,8 +81,6 @@ ConcurrentG1Refine* cg1r() { return _cg1r; } - // Yield for GC - void yield(); // shutdown void stop(); }; diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -976,11 +976,11 @@ } if (concurrent()) { - ConcurrentGCThread::stsLeave(); + SuspendibleThreadSet::leave(); } _first_overflow_barrier_sync.enter(); if (concurrent()) { - ConcurrentGCThread::stsJoin(); + SuspendibleThreadSet::join(); } // at this point everyone should have synced up and not be doing any // more work @@ -1024,11 +1024,11 @@ } if (concurrent()) { - ConcurrentGCThread::stsLeave(); + SuspendibleThreadSet::leave(); } _second_overflow_barrier_sync.enter(); if (concurrent()) { - ConcurrentGCThread::stsJoin(); + SuspendibleThreadSet::join(); } // at this point everything should be re-initialized and ready to go @@ -1076,7 +1076,7 @@ double start_vtime = os::elapsedVTime(); - ConcurrentGCThread::stsJoin(); + SuspendibleThreadSet::join(); assert(worker_id < _cm->active_tasks(), "invariant"); CMTask* the_task = _cm->task(worker_id); @@ -1103,9 +1103,9 @@ if (!_cm->has_aborted() && the_task->has_aborted()) { sleep_time_ms = (jlong) (elapsed_vtime_sec * _cm->sleep_factor() * 1000.0); - ConcurrentGCThread::stsLeave(); + SuspendibleThreadSet::leave(); os::sleep(Thread::current(), sleep_time_ms, false); - ConcurrentGCThread::stsJoin(); + SuspendibleThreadSet::join(); } double end_time2_sec = os::elapsedTime(); double elapsed_time2_sec = end_time2_sec - start_time_sec; @@ -1123,7 +1123,7 @@ the_task->record_end_time(); guarantee(!the_task->has_aborted() || _cm->has_aborted(), "invariant"); - ConcurrentGCThread::stsLeave(); + SuspendibleThreadSet::leave(); double end_vtime = os::elapsedVTime(); _cm->update_accum_task_vtime(worker_id, end_vtime - start_vtime); @@ -3302,21 +3302,17 @@ // We take a break if someone is trying to stop the world. bool ConcurrentMark::do_yield_check(uint worker_id) { - if (should_yield()) { + if (SuspendibleThreadSet::should_yield()) { if (worker_id == 0) { _g1h->g1_policy()->record_concurrent_pause(); } - cmThread()->yield(); + SuspendibleThreadSet::yield(); return true; } else { return false; } } -bool ConcurrentMark::should_yield() { - return cmThread()->should_yield(); -} - bool ConcurrentMark::containing_card_is_marked(void* p) { size_t offset = pointer_delta(p, _g1h->reserved_region().start(), 1); return _card_bm.at(offset >> CardTableModRefBS::card_shift); @@ -3605,7 +3601,7 @@ #endif // _MARKING_STATS_ // (4) We check whether we should yield. If we have to, then we abort. - if (_cm->should_yield()) { + if (SuspendibleThreadSet::should_yield()) { // We should yield. To do this we abort the task. The caller is // responsible for yielding. set_has_aborted(); diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp Fri Apr 11 12:29:24 2014 +0200 @@ -814,7 +814,6 @@ } inline bool do_yield_check(uint worker_i = 0); - inline bool should_yield(); // Called to abort the marking cycle after a Full GC takes place. void abort(); diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -194,9 +194,8 @@ } else { // We don't want to update the marking status if a GC pause // is already underway. - _sts.join(); + SuspendibleThreadSetJoiner sts; g1h->set_marking_complete(); - _sts.leave(); } // Check if cleanup set the free_regions_coming flag. If it @@ -266,11 +265,12 @@ // record_concurrent_mark_cleanup_completed() (and, in fact, it's // not needed any more as the concurrent mark state has been // already reset). - _sts.join(); - if (!cm()->has_aborted()) { - g1_policy->record_concurrent_mark_cleanup_completed(); + { + SuspendibleThreadSetJoiner sts; + if (!cm()->has_aborted()) { + g1_policy->record_concurrent_mark_cleanup_completed(); + } } - _sts.leave(); if (cm()->has_aborted()) { if (G1Log::fine()) { @@ -282,30 +282,27 @@ // We now want to allow clearing of the marking bitmap to be // suspended by a collection pause. - _sts.join(); - _cm->clearNextBitmap(); - _sts.leave(); + { + SuspendibleThreadSetJoiner sts; + _cm->clearNextBitmap(); + } } // Update the number of full collections that have been // completed. This will also notify the FullGCCount_lock in case a // Java thread is waiting for a full GC to happen (e.g., it // called System.gc() with +ExplicitGCInvokesConcurrent). - _sts.join(); - g1h->increment_old_marking_cycles_completed(true /* concurrent */); - g1h->register_concurrent_cycle_end(); - _sts.leave(); + { + SuspendibleThreadSetJoiner sts; + g1h->increment_old_marking_cycles_completed(true /* concurrent */); + g1h->register_concurrent_cycle_end(); + } } assert(_should_terminate, "just checking"); terminate(); } - -void ConcurrentMarkThread::yield() { - _sts.yield("Concurrent Mark"); -} - void ConcurrentMarkThread::stop() { { MutexLockerEx ml(Terminator_lock); diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp Fri Apr 11 12:29:24 2014 +0200 @@ -89,9 +89,6 @@ // that started() is set and set in_progress(). bool during_cycle() { return started() || in_progress(); } - // Yield for GC - void yield(); - // shutdown void stop(); }; diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -92,15 +92,13 @@ // Local to this file. class RefineCardTableEntryClosure: public CardTableEntryClosure { - SuspendibleThreadSet* _sts; G1RemSet* _g1rs; ConcurrentG1Refine* _cg1r; bool _concurrent; public: - RefineCardTableEntryClosure(SuspendibleThreadSet* sts, - G1RemSet* g1rs, + RefineCardTableEntryClosure(G1RemSet* g1rs, ConcurrentG1Refine* cg1r) : - _sts(sts), _g1rs(g1rs), _cg1r(cg1r), _concurrent(true) + _g1rs(g1rs), _cg1r(cg1r), _concurrent(true) {} bool do_card_ptr(jbyte* card_ptr, uint worker_i) { bool oops_into_cset = _g1rs->refine_card(card_ptr, worker_i, false); @@ -109,7 +107,7 @@ // that point into the collection set. assert(!oops_into_cset, "should be"); - if (_concurrent && _sts->should_yield()) { + if (_concurrent && SuspendibleThreadSet::should_yield()) { // Caller will actually yield. return false; } @@ -2117,8 +2115,7 @@ g1_policy()->init(); _refine_cte_cl = - new RefineCardTableEntryClosure(ConcurrentG1RefineThread::sts(), - g1_rem_set(), + new RefineCardTableEntryClosure(g1_rem_set(), concurrent_g1_refine()); JavaThread::dirty_card_queue_set().set_closure(_refine_cte_cl); @@ -4317,7 +4314,7 @@ // this point does not assume that we are the only GC thread // running. Note: of course, the actual marking work will // not start until the safepoint itself is released in - // ConcurrentGCThread::safepoint_desynchronize(). + // SuspendibleThreadSet::desynchronize(). doConcurrentMark(); } diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -77,38 +77,37 @@ break; } - // Include this thread in safepoints - stsJoin(); + { + // Include thread in safepoints + SuspendibleThreadSetJoiner sts; - stat.mark_exec(); + stat.mark_exec(); - // Process the queue - for (;;) { - oop java_string = G1StringDedupQueue::pop(); - if (java_string == NULL) { - break; + // Process the queue + for (;;) { + oop java_string = G1StringDedupQueue::pop(); + if (java_string == NULL) { + break; + } + + G1StringDedupTable::deduplicate(java_string, stat); + + // Safepoint this thread if needed + if (sts.should_yield()) { + stat.mark_block(); + sts.yield(); + stat.mark_unblock(); + } } - G1StringDedupTable::deduplicate(java_string, stat); + G1StringDedupTable::trim_entry_cache(); - // Safepoint this thread if needed - if (stsShouldYield()) { - stat.mark_block(); - stsYield(NULL); - stat.mark_unblock(); - } - } + stat.mark_done(); - G1StringDedupTable::trim_entry_cache(); - - stat.mark_done(); - - // Print statistics - total_stat.add(stat); - print(gclog_or_tty, stat, total_stat); - - // Exclude this thread from safepoints - stsLeave(); + // Print statistics + total_stat.add(stat); + print(gclog_or_tty, stat, total_stat); + } } terminate(); diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp --- a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -37,21 +37,10 @@ int ConcurrentGCThread::_CGC_flag = CGC_nil; -SuspendibleThreadSet ConcurrentGCThread::_sts; - ConcurrentGCThread::ConcurrentGCThread() : _should_terminate(false), _has_terminated(false) { - _sts.initialize(); }; -void ConcurrentGCThread::safepoint_synchronize() { - _sts.suspend_all(); -} - -void ConcurrentGCThread::safepoint_desynchronize() { - _sts.resume_all(); -} - void ConcurrentGCThread::create_and_start() { if (os::create_thread(this, os::cgc_thread)) { // XXX: need to set this to low priority @@ -92,78 +81,6 @@ ThreadLocalStorage::set_thread(NULL); } - -void SuspendibleThreadSet::initialize_work() { - MutexLocker x(STS_init_lock); - if (!_initialized) { - _m = new Monitor(Mutex::leaf, - "SuspendibleThreadSetLock", true); - _async = 0; - _async_stop = false; - _async_stopped = 0; - _initialized = true; - } -} - -void SuspendibleThreadSet::join() { - initialize(); - MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); - while (_async_stop) _m->wait(Mutex::_no_safepoint_check_flag); - _async++; - assert(_async > 0, "Huh."); -} - -void SuspendibleThreadSet::leave() { - assert(_initialized, "Must be initialized."); - MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); - _async--; - assert(_async >= 0, "Huh."); - if (_async_stop) _m->notify_all(); -} - -void SuspendibleThreadSet::yield(const char* id) { - assert(_initialized, "Must be initialized."); - if (_async_stop) { - MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); - if (_async_stop) { - _async_stopped++; - assert(_async_stopped > 0, "Huh."); - if (_async_stopped == _async) { - if (ConcGCYieldTimeout > 0) { - double now = os::elapsedTime(); - guarantee((now - _suspend_all_start) * 1000.0 < - (double)ConcGCYieldTimeout, - "Long delay; whodunit?"); - } - } - _m->notify_all(); - while (_async_stop) _m->wait(Mutex::_no_safepoint_check_flag); - _async_stopped--; - assert(_async >= 0, "Huh"); - _m->notify_all(); - } - } -} - -void SuspendibleThreadSet::suspend_all() { - initialize(); // If necessary. - if (ConcGCYieldTimeout > 0) { - _suspend_all_start = os::elapsedTime(); - } - MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); - assert(!_async_stop, "Only one at a time."); - _async_stop = true; - while (_async_stopped < _async) _m->wait(Mutex::_no_safepoint_check_flag); -} - -void SuspendibleThreadSet::resume_all() { - assert(_initialized, "Must be initialized."); - MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); - assert(_async_stopped == _async, "Huh."); - _async_stop = false; - _m->notify_all(); -} - static void _sltLoop(JavaThread* thread, TRAPS) { SurrogateLockerThread* slt = (SurrogateLockerThread*)thread; slt->loop(); @@ -283,30 +200,3 @@ } assert(!_monitor.owned_by_self(), "Should unlock before exit."); } - - -// ===== STS Access From Outside CGCT ===== - -void ConcurrentGCThread::stsYield(const char* id) { - assert( Thread::current()->is_ConcurrentGC_thread(), - "only a conc GC thread can call this" ); - _sts.yield(id); -} - -bool ConcurrentGCThread::stsShouldYield() { - assert( Thread::current()->is_ConcurrentGC_thread(), - "only a conc GC thread can call this" ); - return _sts.should_yield(); -} - -void ConcurrentGCThread::stsJoin() { - assert( Thread::current()->is_ConcurrentGC_thread(), - "only a conc GC thread can call this" ); - _sts.join(); -} - -void ConcurrentGCThread::stsLeave() { - assert( Thread::current()->is_ConcurrentGC_thread(), - "only a conc GC thread can call this" ); - _sts.leave(); -} diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp --- a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp Fri Apr 11 12:29:24 2014 +0200 @@ -26,55 +26,8 @@ #define SHARE_VM_GC_IMPLEMENTATION_SHARED_CONCURRENTGCTHREAD_HPP #include "utilities/macros.hpp" -#if INCLUDE_ALL_GCS +#include "gc_implementation/shared/suspendibleThreadSet.hpp" #include "runtime/thread.hpp" -#endif // INCLUDE_ALL_GCS - -class VoidClosure; - -// A SuspendibleThreadSet is (obviously) a set of threads that can be -// suspended. A thread can join and later leave the set, and periodically -// yield. If some thread (not in the set) requests, via suspend_all, that -// the threads be suspended, then the requesting thread is blocked until -// all the threads in the set have yielded or left the set. (Threads may -// not enter the set when an attempted suspension is in progress.) The -// suspending thread later calls resume_all, allowing the suspended threads -// to continue. - -class SuspendibleThreadSet { - Monitor* _m; - int _async; - bool _async_stop; - int _async_stopped; - bool _initialized; - double _suspend_all_start; - - void initialize_work(); - - public: - SuspendibleThreadSet() : _initialized(false) {} - - // Add the current thread to the set. May block if a suspension - // is in progress. - void join(); - // Removes the current thread from the set. - void leave(); - // Returns "true" iff an suspension is in progress. - bool should_yield() { return _async_stop; } - // Suspends the current thread if a suspension is in progress (for - // the duration of the suspension.) - void yield(const char* id); - // Return when all threads in the set are suspended. - void suspend_all(); - // Allow suspended threads to resume. - void resume_all(); - // Redundant initializations okay. - void initialize() { - // Double-check dirty read idiom. - if (!_initialized) initialize_work(); - } -}; - class ConcurrentGCThread: public NamedThread { friend class VMStructs; @@ -96,9 +49,6 @@ static int set_CGC_flag(int b) { return _CGC_flag |= b; } static int reset_CGC_flag(int b) { return _CGC_flag &= ~b; } - // All instances share this one set. - static SuspendibleThreadSet _sts; - // Create and start the thread (setting it's priority high.) void create_and_start(); @@ -121,25 +71,6 @@ // Tester bool is_ConcurrentGC_thread() const { return true; } - - static void safepoint_synchronize(); - static void safepoint_desynchronize(); - - // All overridings should probably do _sts::yield, but we allow - // overriding for distinguished debugging messages. Default is to do - // nothing. - virtual void yield() {} - - bool should_yield() { return _sts.should_yield(); } - - // they are prefixed by sts since there are already yield() and - // should_yield() (non-static) methods in this class and it was an - // easy way to differentiate them. - static void stsYield(const char* id); - static bool stsShouldYield(); - static void stsJoin(); - static void stsLeave(); - }; // The SurrogateLockerThread is used by concurrent GC threads for diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/shared/suspendibleThreadSet.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/gc_implementation/shared/suspendibleThreadSet.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014, 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_implementation/shared/suspendibleThreadSet.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/thread.inline.hpp" + +uint SuspendibleThreadSet::_nthreads = 0; +uint SuspendibleThreadSet::_nthreads_stopped = 0; +bool SuspendibleThreadSet::_suspend_all = false; +double SuspendibleThreadSet::_suspend_all_start = 0.0; + +void SuspendibleThreadSet::join() { + MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag); + while (_suspend_all) { + ml.wait(Mutex::_no_safepoint_check_flag); + } + _nthreads++; +} + +void SuspendibleThreadSet::leave() { + MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag); + assert(_nthreads > 0, "Invalid"); + _nthreads--; + if (_suspend_all) { + ml.notify_all(); + } +} + +void SuspendibleThreadSet::yield() { + if (_suspend_all) { + MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag); + if (_suspend_all) { + _nthreads_stopped++; + if (_nthreads_stopped == _nthreads) { + if (ConcGCYieldTimeout > 0) { + double now = os::elapsedTime(); + guarantee((now - _suspend_all_start) * 1000.0 < (double)ConcGCYieldTimeout, "Long delay"); + } + } + ml.notify_all(); + while (_suspend_all) { + ml.wait(Mutex::_no_safepoint_check_flag); + } + assert(_nthreads_stopped > 0, "Invalid"); + _nthreads_stopped--; + ml.notify_all(); + } + } +} + +void SuspendibleThreadSet::synchronize() { + assert(Thread::current()->is_VM_thread(), "Must be the VM thread"); + if (ConcGCYieldTimeout > 0) { + _suspend_all_start = os::elapsedTime(); + } + MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag); + assert(!_suspend_all, "Only one at a time"); + _suspend_all = true; + while (_nthreads_stopped < _nthreads) { + ml.wait(Mutex::_no_safepoint_check_flag); + } +} + +void SuspendibleThreadSet::desynchronize() { + assert(Thread::current()->is_VM_thread(), "Must be the VM thread"); + MonitorLockerEx ml(STS_lock, Mutex::_no_safepoint_check_flag); + assert(_nthreads_stopped == _nthreads, "Invalid"); + _suspend_all = false; + ml.notify_all(); +} diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/gc_implementation/shared/suspendibleThreadSet.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/gc_implementation/shared/suspendibleThreadSet.hpp Fri Apr 11 12:29:24 2014 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, 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_IMPLEMENTATION_SHARED_SUSPENDIBLETHREADSET_HPP +#define SHARE_VM_GC_IMPLEMENTATION_SHARED_SUSPENDIBLETHREADSET_HPP + +#include "memory/allocation.hpp" + +// A SuspendibleThreadSet is a set of threads that can be suspended. +// A thread can join and later leave the set, and periodically yield. +// If some thread (not in the set) requests, via synchronize(), that +// the threads be suspended, then the requesting thread is blocked +// until all the threads in the set have yielded or left the set. Threads +// may not enter the set when an attempted suspension is in progress. The +// suspending thread later calls desynchronize(), allowing the suspended +// threads to continue. +class SuspendibleThreadSet : public AllStatic { +private: + static uint _nthreads; + static uint _nthreads_stopped; + static bool _suspend_all; + static double _suspend_all_start; + +public: + // Add the current thread to the set. May block if a suspension is in progress. + static void join(); + + // Removes the current thread from the set. + static void leave(); + + // Returns true if an suspension is in progress. + static bool should_yield() { return _suspend_all; } + + // Suspends the current thread if a suspension is in progress. + static void yield(); + + // Returns when all threads in the set are suspended. + static void synchronize(); + + // Resumes all suspended threads in the set. + static void desynchronize(); +}; + +class SuspendibleThreadSetJoiner : public StackObj { +public: + SuspendibleThreadSetJoiner() { + SuspendibleThreadSet::join(); + } + + ~SuspendibleThreadSetJoiner() { + SuspendibleThreadSet::leave(); + } + + bool should_yield() { + return SuspendibleThreadSet::should_yield(); + } + + void yield() { + SuspendibleThreadSet::yield(); + } +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_SUSPENDIBLETHREADSET_HPP diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/runtime/mutexLocker.cpp --- a/hotspot/src/share/vm/runtime/mutexLocker.cpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -69,7 +69,7 @@ Monitor* SerializePage_lock = NULL; Monitor* Threads_lock = NULL; Monitor* CGC_lock = NULL; -Mutex* STS_init_lock = NULL; +Monitor* STS_lock = NULL; Monitor* SLT_lock = NULL; Monitor* iCMS_lock = NULL; Monitor* FullGCCount_lock = NULL; @@ -173,7 +173,7 @@ def(tty_lock , Mutex , event, true ); // allow to lock in VM def(CGC_lock , Monitor, special, true ); // coordinate between fore- and background GC - def(STS_init_lock , Mutex, leaf, true ); + def(STS_lock , Monitor, leaf, true ); if (UseConcMarkSweepGC) { def(iCMS_lock , Monitor, special, true ); // CMS incremental mode start/stop notification } diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/runtime/mutexLocker.hpp --- a/hotspot/src/share/vm/runtime/mutexLocker.hpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp Fri Apr 11 12:29:24 2014 +0200 @@ -79,7 +79,7 @@ // (also used by Safepoints too to block threads creation/destruction) extern Monitor* CGC_lock; // used for coordination between // fore- & background GC threads. -extern Mutex* STS_init_lock; // coordinate initialization of SuspendibleThreadSets. +extern Monitor* STS_lock; // used for joining/leaving SuspendibleThreadSet. extern Monitor* SLT_lock; // used in CMS GC for acquiring PLL extern Monitor* iCMS_lock; // CMS incremental mode start/stop notification extern Monitor* FullGCCount_lock; // in support of "concurrent" full gc diff -r 095cc0a63ed9 -r 5dbf1f44de18 hotspot/src/share/vm/runtime/safepoint.cpp --- a/hotspot/src/share/vm/runtime/safepoint.cpp Fri Apr 11 11:00:12 2014 +0200 +++ b/hotspot/src/share/vm/runtime/safepoint.cpp Fri Apr 11 12:29:24 2014 +0200 @@ -75,7 +75,7 @@ #endif #if INCLUDE_ALL_GCS #include "gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp" -#include "gc_implementation/shared/concurrentGCThread.hpp" +#include "gc_implementation/shared/suspendibleThreadSet.hpp" #endif // INCLUDE_ALL_GCS #ifdef COMPILER1 #include "c1/c1_globals.hpp" @@ -110,7 +110,7 @@ // more-general mechanism below. DLD (01/05). ConcurrentMarkSweepThread::synchronize(false); } else if (UseG1GC) { - ConcurrentGCThread::safepoint_synchronize(); + SuspendibleThreadSet::synchronize(); } #endif // INCLUDE_ALL_GCS @@ -486,7 +486,7 @@ if (UseConcMarkSweepGC) { ConcurrentMarkSweepThread::desynchronize(false); } else if (UseG1GC) { - ConcurrentGCThread::safepoint_desynchronize(); + SuspendibleThreadSet::desynchronize(); } #endif // INCLUDE_ALL_GCS // record this time so VMThread can keep track how much time has elapsed