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(shared_qset->allocator()),
  35   _shared_qset(shared_qset),
  36   _buffers()
  37 {}
  38 
  39 G1RedirtyCardsQueueBase::LocalQSet::~LocalQSet() {
  40   assert(_buffers._head == NULL, "unflushed qset");
  41   assert(_buffers._tail == NULL, "invariant");
  42   assert(_buffers._entry_count == 0, "invariant");
  43 }
  44 
  45 void G1RedirtyCardsQueueBase::LocalQSet::enqueue_completed_buffer(BufferNode* node) {
  46   _buffers._entry_count += buffer_size() - node->index();
  47   node->set_next(_buffers._head);
  48   _buffers._head = node;
  49   if (_buffers._tail == NULL) {
  50     _buffers._tail = node;
  51   }
  52 }
  53 
  54 G1BufferNodeList G1RedirtyCardsQueueBase::LocalQSet::take_all_completed_buffers() {
  55   G1BufferNodeList result = _buffers;
  56   _buffers = G1BufferNodeList();
  57   return result;
  58 }
  59 
  60 void G1RedirtyCardsQueueBase::LocalQSet::flush() {
  61   _shared_qset->merge_bufferlist(this);
  62 }
  63 
  64 // G1RedirtyCardsQueue
  65 
  66 G1RedirtyCardsQueue::G1RedirtyCardsQueue(G1RedirtyCardsQueueSet* qset) :
  67   G1RedirtyCardsQueueBase(qset), // Init _local_qset before passing to PtrQueue.
  68   PtrQueue(&_local_qset, true /* active (always) */)
  69 {}
  70 
  71 G1RedirtyCardsQueue::~G1RedirtyCardsQueue() {
  72   flush();
  73 }
  74 
  75 void G1RedirtyCardsQueue::handle_completed_buffer() {
  76   enqueue_completed_buffer();
  77 }
  78 
  79 void G1RedirtyCardsQueue::flush() {
  80   flush_impl();
  81   _local_qset.flush();
  82 }
  83 
  84 // G1RedirtyCardsQueueSet
  85 
  86 G1RedirtyCardsQueueSet::G1RedirtyCardsQueueSet(BufferNode::Allocator* allocator) :
  87   PtrQueueSet(allocator),
  88   _list(),
  89   _entry_count(0),
  90   _tail(NULL)
  91   DEBUG_ONLY(COMMA _collecting(true))
  92 {}
  93 
  94 G1RedirtyCardsQueueSet::~G1RedirtyCardsQueueSet() {
  95   verify_empty();
  96 }
  97 
  98 #ifdef ASSERT
  99 void G1RedirtyCardsQueueSet::verify_empty() const {
 100   assert(_list.empty(), "precondition");
 101   assert(_tail == NULL, "invariant");
 102   assert(_entry_count == 0, "invariant");
 103 }
 104 #endif // ASSERT
 105 
 106 BufferNode* G1RedirtyCardsQueueSet::all_completed_buffers() const {
 107   DEBUG_ONLY(_collecting = false;)
 108   return _list.top();
 109 }
 110 
 111 G1BufferNodeList G1RedirtyCardsQueueSet::take_all_completed_buffers() {
 112   DEBUG_ONLY(_collecting = false;)
 113   G1BufferNodeList result(_list.pop_all(), _tail, _entry_count);
 114   _tail = NULL;
 115   _entry_count = 0;
 116   DEBUG_ONLY(_collecting = true;)
 117   return result;
 118 }
 119 
 120 void G1RedirtyCardsQueueSet::update_tail(BufferNode* node) {
 121   // Node is the tail of a (possibly single element) list just prepended to
 122   // _list.  If, after that prepend, node's follower is NULL, then node is
 123   // also the tail of _list, so record it as such.
 124   if (node->next() == NULL) {
 125     assert(_tail == NULL, "invariant");
 126     _tail = node;
 127   }
 128 }
 129 
 130 void G1RedirtyCardsQueueSet::enqueue_completed_buffer(BufferNode* node) {
 131   assert(_collecting, "precondition");
 132   Atomic::add(&_entry_count, buffer_size() - node->index());
 133   _list.push(*node);
 134   update_tail(node);
 135 }
 136 
 137 void G1RedirtyCardsQueueSet::merge_bufferlist(LocalQSet* src) {
 138   assert(_collecting, "precondition");
 139   const G1BufferNodeList from = src->take_all_completed_buffers();
 140   if (from._head != NULL) {
 141     assert(from._tail != NULL, "invariant");
 142     Atomic::add(&_entry_count, from._entry_count);
 143     _list.prepend(*from._head, *from._tail);
 144     update_tail(from._tail);
 145   }
 146 }