< prev index next >
src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp
Print this page
rev 57544 : 8236485: Work-in-progress: Epoch synchronization protocol for G1 concurrent refinement
Reviewed-by:
*** 25,34 ****
--- 25,35 ----
#include "precompiled.hpp"
#include "gc/g1/g1BufferNodeList.hpp"
#include "gc/g1/g1CardTableEntryClosure.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1DirtyCardQueue.hpp"
+ #include "gc/g1/g1EpochSynchronizer.hpp"
#include "gc/g1/g1FreeIdSet.hpp"
#include "gc/g1/g1RedirtyCardsQueue.hpp"
#include "gc/g1/g1RemSet.hpp"
#include "gc/g1/g1ThreadLocalData.hpp"
#include "gc/g1/heapRegionRemSet.hpp"
*** 233,242 ****
--- 234,246 ----
CardTable::CardValue** const _node_buffer;
const size_t _node_buffer_size;
const uint _worker_id;
size_t* _total_refined_cards;
G1RemSet* const _g1rs;
+ // TODO: Remove _dcqs when G1TestEpochSyncInConcRefinement executes refine_cleaned_cards()
+ // after the try_synchronize() loop.
+ G1DirtyCardQueueSet* const _dcqs;
static inline int compare_card(const CardTable::CardValue* p1,
const CardTable::CardValue* p2) {
return p2 - p1;
}
*** 311,327 ****
public:
G1RefineBufferedCards(BufferNode* node,
size_t node_buffer_size,
uint worker_id,
! size_t* total_refined_cards) :
_node(node),
_node_buffer(reinterpret_cast<CardTable::CardValue**>(BufferNode::make_buffer_from_node(node))),
_node_buffer_size(node_buffer_size),
_worker_id(worker_id),
_total_refined_cards(total_refined_cards),
! _g1rs(G1CollectedHeap::heap()->rem_set()) {}
bool refine() {
size_t first_clean_index = clean_cards();
if (first_clean_index == _node_buffer_size) {
_node->set_index(first_clean_index);
--- 315,333 ----
public:
G1RefineBufferedCards(BufferNode* node,
size_t node_buffer_size,
uint worker_id,
! size_t* total_refined_cards,
! G1DirtyCardQueueSet* dcqs) :
_node(node),
_node_buffer(reinterpret_cast<CardTable::CardValue**>(BufferNode::make_buffer_from_node(node))),
_node_buffer_size(node_buffer_size),
_worker_id(worker_id),
_total_refined_cards(total_refined_cards),
! _g1rs(G1CollectedHeap::heap()->rem_set()),
! _dcqs(dcqs) {}
bool refine() {
size_t first_clean_index = clean_cards();
if (first_clean_index == _node_buffer_size) {
_node->set_index(first_clean_index);
*** 334,355 ****
// humongous object allocation (see comment at the StoreStore fence before
// setting the regions' tops in humongous allocation path).
// It's okay that reading region's top and reading region's type were racy
// wrto each other. We need both set, in any order, to proceed.
OrderAccess::fence();
sort_cards(first_clean_index);
return refine_cleaned_cards(first_clean_index);
}
};
bool G1DirtyCardQueueSet::refine_buffer(BufferNode* node,
uint worker_id,
size_t* total_refined_cards) {
G1RefineBufferedCards buffered_cards(node,
buffer_size(),
worker_id,
! total_refined_cards);
return buffered_cards.refine();
}
#ifndef ASSERT
#define assert_fully_consumed(node, buffer_size)
--- 340,406 ----
// humongous object allocation (see comment at the StoreStore fence before
// setting the regions' tops in humongous allocation path).
// It's okay that reading region's top and reading region's type were racy
// wrto each other. We need both set, in any order, to proceed.
OrderAccess::fence();
+
+ if (G1TestEpochSyncInConcRefinement && !Thread::current()->is_Java_thread()) {
+ // TODO: Asynchronously execute epoch synchronization for multiple
+ // node buffers. This could be done by calling start_synchronizing() for
+ // multiple node buffers, and associating each required frontier with the
+ // buffer. Then execute sort_cards() for these buffers. Finally call
+ // try_synchronize() for each of these node buffers with the recorded
+ // required frontier.
+ G1EpochSynchronizer syncer;
+ ResourceMark rm; // For retrieving thread names in log messages.
+ syncer.start_synchronizing();
+ jlong start_counter = os::elapsed_counter();
+
+ // Spend some time doing useful work instead of blindly waiting.
+ sort_cards(first_clean_index);
+
+ // TODO: refine_cleaned_cards() should be called AFTER the try_synchronize()
+ // loop below. However, we should redirty unrefined cards and skip refinement
+ // work if try_synchronize() spans across a safepoint.
+ // See the TODO comment in try_synchronize().
+ bool result = refine_cleaned_cards(first_clean_index);
+ if (!result) {
+ // We need to enqueue partially processed cards before the try_synchronize() loop,
+ // which could spans across a safepoint.
+ _dcqs->enqueue_completed_buffer(_node);
+ }
+
+ const jlong increase_urgency_thres_ns = 3 * NANOSECS_PER_MILLISEC; // 3 millis
+ const char* thread_name = Thread::current()->name();
+ bool synced = false;
+ bool high_urgency = false;
+ // The first call to try_synchronize() does not need high urgency.
+ while (!syncer.try_synchronize()) {
+ jlong elapsed_counter = os::elapsed_counter() - start_counter;
+ if (!high_urgency && elapsed_counter > increase_urgency_thres_ns) {
+ high_urgency = true;
+ syncer.increase_urgency();
+ }
+ };
+ log_debug(gc, refine, handshake)("%s: Epoch synced after %.3f ms",
+ thread_name, TimeHelper::counter_to_millis(os::elapsed_counter() - start_counter));
+ return result;
+ } else {
sort_cards(first_clean_index);
return refine_cleaned_cards(first_clean_index);
}
+ }
};
bool G1DirtyCardQueueSet::refine_buffer(BufferNode* node,
uint worker_id,
size_t* total_refined_cards) {
G1RefineBufferedCards buffered_cards(node,
buffer_size(),
worker_id,
! total_refined_cards,
! this);
return buffered_cards.refine();
}
#ifndef ASSERT
#define assert_fully_consumed(node, buffer_size)
*** 405,416 ****
--- 456,469 ----
assert_fully_consumed(node, buffer_size());
// Done with fully processed buffer.
deallocate_buffer(node);
return true;
} else {
+ if (!G1TestEpochSyncInConcRefinement) {
// Return partially processed buffer to the queue.
enqueue_completed_buffer(node);
+ }
return true;
}
}
void G1DirtyCardQueueSet::abandon_logs() {
< prev index next >