Skip to content
Permalink
Browse files

8237143: Eliminate DirtyCardQ_cbl_mon

Replace locked data structures with lock-free data structures.

Reviewed-by: tschatzl, sangheki
  • Loading branch information
Kim Barrett
Kim Barrett committed Feb 7, 2020
1 parent e37a6ae commit ccbd819a0161e6434a7b22b4f58285e8c11cc92c
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2020, 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
@@ -36,7 +36,6 @@
#include "oops/compressedOops.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/macros.hpp"
@@ -59,7 +58,7 @@ G1BarrierSet::G1BarrierSet(G1CardTable* card_table) :
_satb_mark_queue_buffer_allocator("SATB Buffer Allocator", G1SATBBufferSize),
_dirty_card_queue_buffer_allocator("DC Buffer Allocator", G1UpdateBufferSize),
_satb_mark_queue_set(&_satb_mark_queue_buffer_allocator),
_dirty_card_queue_set(DirtyCardQ_CBL_mon, &_dirty_card_queue_buffer_allocator),
_dirty_card_queue_set(&_dirty_card_queue_buffer_allocator),
_shared_dirty_card_queue(&_dirty_card_queue_set)
{}

@@ -2776,8 +2776,6 @@ size_t G1CollectedHeap::pending_card_num() {
Threads::threads_do(&count_from_threads);

G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
dcqs.verify_num_cards();

return dcqs.num_cards() + count_from_threads._cards;
}

@@ -89,6 +89,11 @@ jint G1ConcurrentRefineThreadControl::initialize(G1ConcurrentRefine* cr, uint nu
}
}
}

if (num_max_threads > 0) {
G1BarrierSet::dirty_card_queue_set().set_primary_refinement_thread(_threads[0]);
}

return JNI_OK;
}

@@ -108,7 +113,7 @@ void G1ConcurrentRefineThreadControl::maybe_activate_next(uint cur_worker_id) {
_threads[worker_id] = create_refinement_thread(worker_id, false);
thread_to_activate = _threads[worker_id];
}
if (thread_to_activate != NULL && !thread_to_activate->is_active()) {
if (thread_to_activate != NULL) {
thread_to_activate->activate();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2020, 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
@@ -29,9 +29,8 @@
#include "gc/g1/g1DirtyCardQueue.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/atomic.hpp"
#include "runtime/thread.hpp"

G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) :
ConcurrentGCThread(),
@@ -40,56 +39,53 @@ G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint
_total_refinement_time(),
_total_refined_cards(0),
_worker_id(worker_id),
_active(false),
_monitor(NULL),
_notifier(new Semaphore(0)),
_should_notify(true),
_cr(cr)
{
// Each thread has its own monitor. The i-th thread is responsible for signaling
// to thread i+1 if the number of buffers in the queue exceeds a threshold for this
// thread. Monitors are also used to wake up the threads during termination.
// The 0th (primary) worker is notified by mutator threads and has a special monitor.
if (!is_primary()) {
_monitor = new Monitor(Mutex::nonleaf, "Refinement monitor", true,
Monitor::_safepoint_check_never);
} else {
_monitor = DirtyCardQ_CBL_mon;
}

// set name
set_name("G1 Refine#%d", worker_id);
create_and_start();
}

void G1ConcurrentRefineThread::wait_for_completed_buffers() {
MonitorLocker ml(_monitor, Mutex::_no_safepoint_check_flag);
while (!should_terminate() && !is_active()) {
ml.wait();
assert(this == Thread::current(), "precondition");
while (Atomic::load_acquire(&_should_notify)) {
_notifier->wait();
}
}

bool G1ConcurrentRefineThread::is_active() {
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
return is_primary() ? dcqs.process_completed_buffers() : _active;
}

void G1ConcurrentRefineThread::activate() {
MutexLocker x(_monitor, Mutex::_no_safepoint_check_flag);
if (!is_primary()) {
set_active(true);
} else {
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
dcqs.set_process_completed_buffers(true);
assert(this != Thread::current(), "precondition");
// Notify iff transitioning from needing activation to not. This helps
// keep the semaphore count bounded and minimizes the work done by
// activators when the thread is already active.
if (Atomic::load_acquire(&_should_notify) &&
Atomic::cmpxchg(&_should_notify, true, false)) {
_notifier->signal();
}
_monitor->notify();
}

void G1ConcurrentRefineThread::deactivate() {
MutexLocker x(_monitor, Mutex::_no_safepoint_check_flag);
if (!is_primary()) {
set_active(false);
bool G1ConcurrentRefineThread::maybe_deactivate(bool more_work) {
assert(this == Thread::current(), "precondition");

if (more_work) {
// Suppress unnecessary notifications.
Atomic::release_store(&_should_notify, false);
return false;
} else if (Atomic::load_acquire(&_should_notify)) {
// Deactivate if no notifications since enabled (see below).
return true;
} else {
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
dcqs.set_process_completed_buffers(false);
// Try for more refinement work with notifications enabled, to close
// race; there could be a plethora of suppressed activation attempts
// after we found no work but before we enable notifications here
// (so there could be lots of work for this thread to do), followed
// by a long time without activation after enabling notifications.
// But first, clear any pending signals to prevent accumulation.
while (_notifier->trywait()) {}
Atomic::release_store(&_should_notify, true);
return false;
}
}

@@ -119,14 +115,13 @@ void G1ConcurrentRefineThread::run_service() {
}

Ticks start_time = Ticks::now();
if (!_cr->do_refinement_step(_worker_id, &_total_refined_cards)) {
break; // No cards to process.
}
bool more_work = _cr->do_refinement_step(_worker_id, &_total_refined_cards);
_total_refinement_time += (Ticks::now() - start_time);

if (maybe_deactivate(more_work)) break;
}
}

deactivate();
log_debug(gc, refine)("Deactivated worker %d, off threshold: " SIZE_FORMAT
", current: " SIZE_FORMAT ", refined cards: "
SIZE_FORMAT ", total refined cards: " SIZE_FORMAT,
@@ -146,6 +141,5 @@ void G1ConcurrentRefineThread::run_service() {
}

void G1ConcurrentRefineThread::stop_service() {
MutexLocker x(_monitor, Mutex::_no_safepoint_check_flag);
_monitor->notify();
activate();
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -45,24 +45,33 @@ class G1ConcurrentRefineThread: public ConcurrentGCThread {

uint _worker_id;

bool _active;
Monitor* _monitor;
// _notifier and _should_notify form a single-reader / multi-writer
// notification mechanism. The owning concurrent refinement thread is the
// single reader. The writers are (other) threads that call activate() on
// the thread. The i-th concurrent refinement thread is responsible for
// activating thread i+1 if the number of buffers in the queue exceeds a
// threshold for that i+1th thread. The 0th (primary) thread is activated
// by threads that add cards to the dirty card queue set when the primary
// thread's threshold is exceeded. activate() is also used to wake up the
// threads during termination, so even the non-primary thread case is
// multi-writer.
Semaphore* _notifier;
volatile bool _should_notify;

// Called when no refinement work found for this thread.
// Returns true if should deactivate.
bool maybe_deactivate(bool more_work);

G1ConcurrentRefine* _cr;

void wait_for_completed_buffers();

void set_active(bool x) { _active = x; }
// Deactivate this thread.
void deactivate();

bool is_primary() { return (_worker_id == 0); }
virtual void run_service();
virtual void stop_service();

void run_service();
void stop_service();
public:
G1ConcurrentRefineThread(G1ConcurrentRefine* cg1r, uint worker_id);

bool is_active();
// Activate this thread.
void activate();

0 comments on commit ccbd819

Please sign in to comment.