changeset 60549:44a909932c7c

8139652: Mutator refinement processing should take the oldest dirty card buffer Summary: Changed mutator refinement to take from queue rather than in-place and reuse. Reviewed-by: tschatzl, sjohanss
author kbarrett
date Thu, 19 Mar 2020 18:11:52 -0400
parents 6385879efd46
children 0c03ed579379
files src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp
diffstat 2 files changed, 63 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp	Thu Mar 19 12:29:59 2020 -0700
+++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp	Thu Mar 19 18:11:52 2020 -0400
@@ -55,14 +55,10 @@
 }
 
 void G1DirtyCardQueue::handle_completed_buffer() {
-  assert(_buf != NULL, "precondition");
+  assert(!is_empty(), "precondition");
   BufferNode* node = BufferNode::make_node_from_buffer(_buf, index());
-  G1DirtyCardQueueSet* dcqs = dirty_card_qset();
-  if (dcqs->process_or_enqueue_completed_buffer(node)) {
-    reset();                    // Buffer fully processed, reset index.
-  } else {
-    allocate_buffer();          // Buffer enqueued, get a new one.
-  }
+  allocate_buffer();
+  dirty_card_qset()->handle_completed_buffer(node);
 }
 
 // Assumed to be zero by concurrent threads.
@@ -227,11 +223,7 @@
   }
 }
 
-BufferNode* G1DirtyCardQueueSet::get_completed_buffer(size_t stop_at) {
-  if (Atomic::load_acquire(&_num_cards) < stop_at) {
-    return NULL;
-  }
-
+BufferNode* G1DirtyCardQueueSet::get_completed_buffer() {
   BufferNode* result = _completed.pop();
   if (result == NULL) {         // Unlikely if no paused buffers.
     enqueue_previous_paused_buffers();
@@ -548,71 +540,61 @@
   return buffered_cards.refine();
 }
 
-#ifndef ASSERT
-#define assert_fully_consumed(node, buffer_size)
-#else
-#define assert_fully_consumed(node, buffer_size)                \
-  do {                                                          \
-    size_t _afc_index = (node)->index();                        \
-    size_t _afc_size = (buffer_size);                           \
-    assert(_afc_index == _afc_size,                             \
-           "Buffer was not fully consumed as claimed: index: "  \
-           SIZE_FORMAT ", size: " SIZE_FORMAT,                  \
-            _afc_index, _afc_size);                             \
-  } while (0)
-#endif // ASSERT
-
-bool G1DirtyCardQueueSet::process_or_enqueue_completed_buffer(BufferNode* node) {
-  if (Thread::current()->is_Java_thread()) {
-    // If the number of buffers exceeds the limit, make this Java
-    // thread do the processing itself.  Calculation is racy but we
-    // don't need precision here.
-    if (num_cards() > Atomic::load(&_padded_max_cards)) {
-      if (mut_process_buffer(node)) {
-        return true;
-      }
-      // Buffer was incompletely processed because of a pending safepoint
-      // request.  Unlike with refinement thread processing, for mutator
-      // processing the buffer did not come from the completed buffer queue,
-      // so it is okay to add it to the queue rather than to the paused set.
-      // Indeed, it can't be added to the paused set because we didn't pass
-      // through enqueue_previous_paused_buffers.
-    }
+void G1DirtyCardQueueSet::handle_refined_buffer(BufferNode* node,
+                                                bool fully_processed) {
+  if (fully_processed) {
+    assert(node->index() == buffer_size(),
+           "Buffer not fully consumed: index: " SIZE_FORMAT ", size: " SIZE_FORMAT,
+           node->index(), buffer_size());
+    deallocate_buffer(node);
+  } else {
+    assert(node->index() < buffer_size(), "Buffer fully consumed.");
+    // Buffer incompletely processed because there is a pending safepoint.
+    // Record partially processed buffer, to be finished later.
+    record_paused_buffer(node);
   }
-  enqueue_completed_buffer(node);
-  return false;
 }
 
-bool G1DirtyCardQueueSet::mut_process_buffer(BufferNode* node) {
+void G1DirtyCardQueueSet::handle_completed_buffer(BufferNode* new_node) {
+  enqueue_completed_buffer(new_node);
+
+  // No need for mutator refinement if number of cards is below limit.
+  if (Atomic::load(&_num_cards) <= Atomic::load(&_padded_max_cards)) {
+    return;
+  }
+
+  // Only Java threads perform mutator refinement.
+  if (!Thread::current()->is_Java_thread()) {
+    return;
+  }
+
+  BufferNode* node = get_completed_buffer();
+  if (node == NULL) return;     // Didn't get a buffer to process.
+
+  // Refine cards in buffer.
+
   uint worker_id = _free_ids.claim_par_id(); // temporarily claim an id
   uint counter_index = worker_id - par_ids_start();
   size_t* counter = &_mutator_refined_cards_counters[counter_index];
-  bool result = refine_buffer(node, worker_id, counter);
+  bool fully_processed = refine_buffer(node, worker_id, counter);
   _free_ids.release_par_id(worker_id); // release the id
 
-  if (result) {
-    assert_fully_consumed(node, buffer_size());
-  }
-  return result;
+  // Deal with buffer after releasing id, to let another thread use id.
+  handle_refined_buffer(node, fully_processed);
 }
 
 bool G1DirtyCardQueueSet::refine_completed_buffer_concurrently(uint worker_id,
                                                                size_t stop_at,
                                                                size_t* total_refined_cards) {
-  BufferNode* node = get_completed_buffer(stop_at);
-  if (node == NULL) {
-    return false;
-  } else if (refine_buffer(node, worker_id, total_refined_cards)) {
-    assert_fully_consumed(node, buffer_size());
-    // Done with fully processed buffer.
-    deallocate_buffer(node);
-    return true;
-  } else {
-    // Buffer incompletely processed because there is a pending safepoint.
-    // Record partially processed buffer, to be finished later.
-    record_paused_buffer(node);
-    return true;
-  }
+  // Not enough cards to trigger processing.
+  if (Atomic::load(&_num_cards) <= stop_at) return false;
+
+  BufferNode* node = get_completed_buffer();
+  if (node == NULL) return false; // Didn't get a buffer to process.
+
+  bool fully_processed = refine_buffer(node, worker_id, total_refined_cards);
+  handle_refined_buffer(node, fully_processed);
+  return true;
 }
 
 void G1DirtyCardQueueSet::abandon_logs() {
--- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp	Thu Mar 19 12:29:59 2020 -0700
+++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp	Thu Mar 19 18:11:52 2020 -0400
@@ -245,11 +245,13 @@
   // processed and removed from the buffer.
   bool refine_buffer(BufferNode* node, uint worker_id, size_t* total_refined_cards);
 
-  bool mut_process_buffer(BufferNode* node);
+  // Deal with buffer after a call to refine_buffer.  If fully processed,
+  // deallocate the buffer.  Otherwise, record it as paused.
+  void handle_refined_buffer(BufferNode* node, bool fully_processed);
 
-  // If the number of completed buffers is > stop_at, then remove and
-  // return a completed buffer from the list.  Otherwise, return NULL.
-  BufferNode* get_completed_buffer(size_t stop_at = 0);
+  // Remove and return a completed buffer from the list, or return NULL
+  // if none available.
+  BufferNode* get_completed_buffer();
 
 public:
   G1DirtyCardQueueSet(BufferNode::Allocator* allocator);
@@ -265,11 +267,6 @@
 
   static void handle_zero_index_for_thread(Thread* t);
 
-  // Either process the entire buffer and return true, or enqueue the
-  // buffer and return false.  If the buffer is completely processed,
-  // it can be reused in place.
-  bool process_or_enqueue_completed_buffer(BufferNode* node);
-
   virtual void enqueue_completed_buffer(BufferNode* node);
 
   // Upper bound on the number of cards currently in in this queue set.
@@ -295,6 +292,17 @@
 
   G1BufferNodeList take_all_completed_buffers();
 
+  // Helper for G1DirtyCardQueue::handle_completed_buffer().
+  // Enqueue the buffer, and optionally perform refinement by the mutator.
+  // Mutator refinement is only done by Java threads, and only if there
+  // are more than max_cards (possibly padded) cards in the completed
+  // buffers.
+  //
+  // Mutator refinement, if performed, stops processing a buffer if
+  // SuspendibleThreadSet::should_yield(), recording the incompletely
+  // processed buffer for later processing of the remainder.
+  void handle_completed_buffer(BufferNode* node);
+
   // If there are more than stop_at cards in the completed buffers, pop
   // a buffer, refine its contents, and return true.  Otherwise return
   // false.