1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "gc/g1/g1RedirtyCardsQueue.hpp"
  27 #include "runtime/atomic.hpp"
  28 #include "utilities/debug.hpp"
  29 #include "utilities/macros.hpp"
  30 
  31 // G1RedirtyCardsQueueBase::LocalQSet
  32 
  33 G1RedirtyCardsQueueBase::LocalQSet::LocalQSet(G1RedirtyCardsQueueSet* shared_qset) :
  34   PtrQueueSet(),
  35   _shared_qset(shared_qset),
  36   _buffers()
  37 {
  38   PtrQueueSet::initialize(_shared_qset->allocator());
  39 }
  40 
  41 G1RedirtyCardsQueueBase::LocalQSet::~LocalQSet() {
  42   assert(_buffers._head == NULL, "unflushed qset");
  43   assert(_buffers._tail == NULL, "invariant");
  44   assert(_buffers._entry_count == 0, "invariant");
  45 }
  46 
  47 void G1RedirtyCardsQueueBase::LocalQSet::enqueue_completed_buffer(BufferNode* node) {
  48   _buffers._entry_count += buffer_size() - node->index();
  49   node->set_next(_buffers._head);
  50   _buffers._head = node;
  51   if (_buffers._tail == NULL) {
  52     _buffers._tail = node;
  53   }
  54 }
  55 
  56 G1BufferNodeList G1RedirtyCardsQueueBase::LocalQSet::take_all_completed_buffers() {
  57   G1BufferNodeList result = _buffers;
  58   _buffers = G1BufferNodeList();
  59   return result;
  60 }
  61 
  62 void G1RedirtyCardsQueueBase::LocalQSet::flush() {
  63   _shared_qset->merge_bufferlist(this);
  64 }
  65 
  66 // G1RedirtyCardsQueue
  67 
  68 G1RedirtyCardsQueue::G1RedirtyCardsQueue(G1RedirtyCardsQueueSet* qset) :
  69   G1RedirtyCardsQueueBase(qset), // Init _local_qset before passing to PtrQueue.
  70   PtrQueue(&_local_qset, true /* active (always) */)
  71 {}
  72 
  73 G1RedirtyCardsQueue::~G1RedirtyCardsQueue() {
  74   flush();
  75 }
  76 
  77 void G1RedirtyCardsQueue::handle_completed_buffer() {
  78   enqueue_completed_buffer();
  79 }
  80 
  81 void G1RedirtyCardsQueue::flush() {
  82   flush_impl();
  83   _local_qset.flush();
  84 }
  85 
  86 // G1RedirtyCardsQueueSet
  87 
  88 G1RedirtyCardsQueueSet::G1RedirtyCardsQueueSet(BufferNode::Allocator* allocator) :
  89   PtrQueueSet(),
  90   _list(),
  91   _entry_count(0),
  92   _tail(NULL)
  93   DEBUG_ONLY(COMMA _collecting(true))
  94 {
  95   initialize(allocator);
  96 }
  97 
  98 G1RedirtyCardsQueueSet::~G1RedirtyCardsQueueSet() {
  99   verify_empty();
 100 }
 101 
 102 #ifdef ASSERT
 103 void G1RedirtyCardsQueueSet::verify_empty() const {
 104   assert(_list.empty(), "precondition");
 105   assert(_tail == NULL, "invariant");
 106   assert(_entry_count == 0, "invariant");
 107 }
 108 #endif // ASSERT
 109 
 110 BufferNode* G1RedirtyCardsQueueSet::all_completed_buffers() const {
 111   DEBUG_ONLY(_collecting = false;)
 112   return _list.top();
 113 }
 114 
 115 G1BufferNodeList G1RedirtyCardsQueueSet::take_all_completed_buffers() {
 116   DEBUG_ONLY(_collecting = false;)
 117   G1BufferNodeList result(_list.pop_all(), _tail, _entry_count);
 118   _tail = NULL;
 119   _entry_count = 0;
 120   DEBUG_ONLY(_collecting = true;)
 121   return result;
 122 }
 123 
 124 void G1RedirtyCardsQueueSet::update_tail(BufferNode* node) {
 125   // Node is the tail of a (possibly single element) list just prepended to
 126   // _list.  If, after that prepend, node's follower is NULL, then node is
 127   // also the tail of _list, so record it as such.
 128   if (node->next() == NULL) {
 129     assert(_tail == NULL, "invariant");
 130     _tail = node;
 131   }
 132 }
 133 
 134 void G1RedirtyCardsQueueSet::enqueue_completed_buffer(BufferNode* node) {
 135   assert(_collecting, "precondition");
 136   Atomic::add(buffer_size() - node->index(), &_entry_count);
 137   _list.push(*node);
 138   update_tail(node);
 139 }
 140 
 141 void G1RedirtyCardsQueueSet::merge_bufferlist(LocalQSet* src) {
 142   assert(_collecting, "precondition");
 143   const G1BufferNodeList from = src->take_all_completed_buffers();
 144   if (from._head != NULL) {
 145     assert(from._tail != NULL, "invariant");
 146     Atomic::add(from._entry_count, &_entry_count);
 147     _list.prepend(*from._head, *from._tail);
 148     update_tail(from._tail);
 149   }
 150 }