blob: 5278fc2861a0032564fea862e600a23dd10950c4 [file] [log] [blame]
/*
* Copyright (c) 2001, 2022, 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/g1/g1BarrierSet.hpp"
#include "gc/g1/g1ConcurrentRefine.hpp"
#include "gc/g1/g1ConcurrentRefineStats.hpp"
#include "gc/g1/g1ConcurrentRefineThread.hpp"
#include "gc/g1/g1DirtyCardQueue.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "logging/log.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/ticks.hpp"
G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) :
ConcurrentGCThread(),
_vtime_start(0.0),
_vtime_accum(0.0),
_notifier(Mutex::nosafepoint, FormatBuffer<>("G1 Refine#%d", worker_id), true),
_requested_active(false),
_refinement_stats(),
_worker_id(worker_id),
_cr(cr)
{
// set name
set_name("G1 Refine#%d", worker_id);
}
void G1ConcurrentRefineThread::run_service() {
_vtime_start = os::elapsedVTime();
while (wait_for_completed_buffers()) {
SuspendibleThreadSetJoiner sts_join;
G1ConcurrentRefineStats active_stats_start = _refinement_stats;
report_active("Activated");
while (!should_terminate()) {
if (sts_join.should_yield()) {
report_inactive("Paused", _refinement_stats - active_stats_start);
sts_join.yield();
// Reset after yield rather than accumulating across yields, else a
// very long running thread could overflow.
active_stats_start = _refinement_stats;
report_active("Resumed");
} else if (maybe_deactivate()) {
break;
} else {
do_refinement_step();
}
}
report_inactive("Deactivated", _refinement_stats - active_stats_start);
if (os::supports_vtime()) {
_vtime_accum = (os::elapsedVTime() - _vtime_start);
} else {
_vtime_accum = 0.0;
}
}
log_debug(gc, refine)("Stopping %d", _worker_id);
}
void G1ConcurrentRefineThread::report_active(const char* reason) const {
log_trace(gc, refine)("%s worker %u, current: %zu",
reason,
_worker_id,
G1BarrierSet::dirty_card_queue_set().num_cards());
}
void G1ConcurrentRefineThread::report_inactive(const char* reason,
const G1ConcurrentRefineStats& stats) const {
log_trace(gc, refine)
("%s worker %u, cards: %zu, refined %zu, rate %1.2fc/ms",
reason,
_worker_id,
G1BarrierSet::dirty_card_queue_set().num_cards(),
stats.refined_cards(),
stats.refinement_rate_ms());
}
void G1ConcurrentRefineThread::activate() {
assert(this != Thread::current(), "precondition");
MonitorLocker ml(&_notifier, Mutex::_no_safepoint_check_flag);
if (!_requested_active || should_terminate()) {
_requested_active = true;
ml.notify();
}
}
bool G1ConcurrentRefineThread::maybe_deactivate() {
assert(this == Thread::current(), "precondition");
if (cr()->is_thread_wanted(_worker_id)) {
return false;
} else {
MutexLocker ml(&_notifier, Mutex::_no_safepoint_check_flag);
bool requested = _requested_active;
_requested_active = false;
return !requested; // Deactivate only if not recently requested active.
}
}
bool G1ConcurrentRefineThread::try_refinement_step(size_t stop_at) {
assert(this == Thread::current(), "precondition");
return _cr->try_refinement_step(_worker_id, stop_at, &_refinement_stats);
}
void G1ConcurrentRefineThread::stop_service() {
activate();
}
// The (single) primary thread drives the controller for the refinement threads.
class G1PrimaryConcurrentRefineThread final : public G1ConcurrentRefineThread {
bool wait_for_completed_buffers() override;
bool maybe_deactivate() override;
void do_refinement_step() override;
public:
G1PrimaryConcurrentRefineThread(G1ConcurrentRefine* cr) :
G1ConcurrentRefineThread(cr, 0)
{}
};
// When inactive, the primary thread periodically wakes up and requests
// adjustment of the number of active refinement threads.
bool G1PrimaryConcurrentRefineThread::wait_for_completed_buffers() {
assert(this == Thread::current(), "precondition");
MonitorLocker ml(notifier(), Mutex::_no_safepoint_check_flag);
if (!requested_active() && !should_terminate()) {
// Rather than trying to be smart about spurious wakeups, we just treat
// them as timeouts.
ml.wait(cr()->adjust_threads_wait_ms());
}
// Record adjustment needed whenever reactivating.
cr()->record_thread_adjustment_needed();
return !should_terminate();
}
bool G1PrimaryConcurrentRefineThread::maybe_deactivate() {
// Don't deactivate while needing to adjust the number of active threads.
return !cr()->is_thread_adjustment_needed() &&
G1ConcurrentRefineThread::maybe_deactivate();
}
void G1PrimaryConcurrentRefineThread::do_refinement_step() {
// Try adjustment first. If it succeeds then don't do any refinement this
// round. This thread may have just woken up but no threads are currently
// needed, which is common. In this case we want to just go back to
// waiting, with a minimum of fuss; in particular, don't do any "premature"
// refinement. However, adjustment may be pending but temporarily
// blocked. In that case we *do* try refinement, rather than possibly
// uselessly spinning while waiting for adjustment to succeed.
if (!cr()->adjust_threads_periodically()) {
// No adjustment, so try refinement, with the target as a cuttoff.
if (!try_refinement_step(cr()->pending_cards_target())) {
// Refinement was cut off, so proceed with fewer threads.
cr()->reduce_threads_wanted();
}
}
}
class G1SecondaryConcurrentRefineThread final : public G1ConcurrentRefineThread {
bool wait_for_completed_buffers() override;
void do_refinement_step() override;
public:
G1SecondaryConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) :
G1ConcurrentRefineThread(cr, worker_id)
{
assert(worker_id > 0, "precondition");
}
};
bool G1SecondaryConcurrentRefineThread::wait_for_completed_buffers() {
assert(this == Thread::current(), "precondition");
MonitorLocker ml(notifier(), Mutex::_no_safepoint_check_flag);
while (!requested_active() && !should_terminate()) {
ml.wait();
}
return !should_terminate();
}
void G1SecondaryConcurrentRefineThread::do_refinement_step() {
assert(this == Thread::current(), "precondition");
// Secondary threads ignore the target and just drive the number of pending
// dirty cards down. The primary thread is responsible for noticing the
// target has been reached and reducing the number of wanted threads. This
// makes the control of wanted threads all under the primary, while avoiding
// useless spinning by secondary threads until the primary thread notices.
// (Useless spinning is still possible if there are no pending cards, but
// that should rarely happen.)
try_refinement_step(0);
}
G1ConcurrentRefineThread*
G1ConcurrentRefineThread::create(G1ConcurrentRefine* cr, uint worker_id) {
G1ConcurrentRefineThread* crt;
if (worker_id == 0) {
crt = new (std::nothrow) G1PrimaryConcurrentRefineThread(cr);
} else {
crt = new (std::nothrow) G1SecondaryConcurrentRefineThread(cr, worker_id);
}
if (crt != nullptr) {
crt->create_and_start();
}
return crt;
}