1 /*
   2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2020 SAP SE. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  *
  24  */
  25 
  26 #ifndef SHARE_MEMORY_METASPACE_COMMITMASK_HPP
  27 #define SHARE_MEMORY_METASPACE_COMMITMASK_HPP
  28 
  29 #include "utilities/debug.hpp"
  30 #include "utilities/bitMap.hpp"
  31 #include "utilities/globalDefinitions.hpp"
  32 
  33 class outputStream;
  34 
  35 namespace metaspace {
  36 
  37 // The CommitMask is a bitmask used to store the commit state of commit granules.
  38 // It keeps one bit per granule; 1 means committed, 0 means uncommitted.
  39 
  40 class CommitMask : public CHeapBitMap {
  41 
  42   const MetaWord* const _base;
  43   const size_t _word_size;
  44   const size_t _words_per_bit;
  45 
  46   // Given an offset, in words, into the area, return the number of the bit
  47   // covering it.
  48   static idx_t bitno_for_word_offset(size_t offset, size_t words_per_bit) {
  49     return offset / words_per_bit;
  50   }
  51 
  52   idx_t bitno_for_address(const MetaWord* p) const {
  53     // Note: we allow one-beyond since this is a typical need.
  54     assert(p >= _base && p <= _base + _word_size, "Invalid address");
  55     const size_t off = p - _base;
  56     return bitno_for_word_offset(off, _words_per_bit);
  57   }
  58 
  59   static idx_t mask_size(size_t word_size, size_t words_per_bit) {
  60     return bitno_for_word_offset(word_size, words_per_bit);
  61   }
  62 
  63   struct BitCounterClosure : public BitMapClosure {
  64     idx_t cnt;
  65     bool do_bit(BitMap::idx_t offset) { cnt ++; return true; }
  66   };
  67 
  68 #ifdef ASSERT
  69   // Given a pointer, check if it points into the range this bitmap covers.
  70   bool is_pointer_valid(const MetaWord* p) const {
  71     return p >= _base && p < _base + _word_size;
  72   }
  73 
  74   // Given a pointer, check if it points into the range this bitmap covers.
  75   void check_pointer(const MetaWord* p) const {
  76     assert(is_pointer_valid(p),
  77            "Pointer " PTR_FORMAT " not in range of this bitmap [" PTR_FORMAT ", " PTR_FORMAT ").",
  78            p2i(p), p2i(_base), p2i(_base + _word_size));
  79   }
  80   // Given a pointer, check if it points into the range this bitmap covers,
  81   // and if it is aligned to commit granule border.
  82   void check_pointer_aligned(const MetaWord* p) const {
  83     check_pointer(p);
  84     assert(is_aligned(p, _words_per_bit * BytesPerWord),
  85            "Pointer " PTR_FORMAT " should be aligned to commit granule size " SIZE_FORMAT ".",
  86            p2i(p), _words_per_bit * BytesPerWord);
  87   }
  88   // Given a range, check if it points into the range this bitmap covers,
  89   // and if its borders are aligned to commit granule border.
  90   void check_range(const MetaWord* start, size_t word_size) const {
  91     check_pointer_aligned(start);
  92     assert(is_aligned(word_size, _words_per_bit),
  93            "Range " SIZE_FORMAT " should be aligned to commit granule size " SIZE_FORMAT ".",
  94            word_size, _words_per_bit);
  95     check_pointer(start + word_size - 1);
  96   }
  97 #endif
  98 
  99   // Marks a single commit granule as committed (value == true)
 100   // or uncomitted (value == false) and returns
 101   // its prior state.
 102   bool mark_granule(idx_t bitno, bool value) {
 103     bool b = at(bitno);
 104     at_put(bitno, value);
 105     return b;
 106   }
 107 
 108 public:
 109 
 110   // Create a commit mask covering a range [start, start + word_size).
 111   CommitMask(const MetaWord* start, size_t word_size);
 112 
 113   const MetaWord* base() const  { return _base; }
 114   size_t word_size() const      { return _word_size; }
 115   const MetaWord* end() const   { return _base + word_size(); }
 116 
 117   // Given an address, returns true if the address is committed, false if not.
 118   bool is_committed_address(const MetaWord* p) const {
 119     DEBUG_ONLY(check_pointer(p));
 120     const idx_t bitno = bitno_for_address(p);
 121     return at(bitno);
 122   }
 123 
 124   // Given an address range, return size, in number of words, of committed area within that range.
 125   size_t get_committed_size_in_range(const MetaWord* start, size_t word_size) const {
 126     DEBUG_ONLY(check_range(start, word_size));
 127     assert(word_size > 0, "zero range");
 128     const idx_t b1 = bitno_for_address(start);
 129     const idx_t b2 = bitno_for_address(start + word_size);
 130     const idx_t num_bits = count_one_bits(b1, b2);
 131     return num_bits * _words_per_bit;
 132   }
 133 
 134   // Return total committed size, in number of words.
 135   size_t get_committed_size() const {
 136     return count_one_bits() * _words_per_bit;
 137   }
 138 
 139   // Mark a whole address range [start, end) as committed.
 140   // Return the number of words which had already been committed before this operation.
 141   size_t mark_range_as_committed(const MetaWord* start, size_t word_size) {
 142     DEBUG_ONLY(check_range(start, word_size));
 143     assert(word_size > 0, "zero range");
 144     const idx_t b1 = bitno_for_address(start);
 145     const idx_t b2 = bitno_for_address(start + word_size);
 146     if (b1 == b2) { // Simple case, 1 granule
 147       bool was_committed = mark_granule(b1, true);
 148       return was_committed ? _words_per_bit : 0;
 149     }
 150     const idx_t one_bits_in_range_before = count_one_bits(b1, b2);
 151     set_range(b1, b2);
 152     return one_bits_in_range_before * _words_per_bit;
 153   }
 154 
 155   // Mark a whole address range [start, end) as uncommitted.
 156   // Return the number of words which had already been uncommitted before this operation.
 157   size_t mark_range_as_uncommitted(const MetaWord* start, size_t word_size) {
 158     DEBUG_ONLY(check_range(start, word_size));
 159     assert(word_size > 0, "zero range");
 160     const idx_t b1 = bitno_for_address(start);
 161     const idx_t b2 = bitno_for_address(start + word_size);
 162     if (b1 == b2) { // Simple case, 1 granule
 163       bool was_committed = mark_granule(b1, false);
 164       return was_committed ? 0 : _words_per_bit;
 165     }
 166     const idx_t zero_bits_in_range_before =
 167         (b2 - b1) - count_one_bits(b1, b2);
 168     clear_range(b1, b2);
 169     return zero_bits_in_range_before * _words_per_bit;
 170   }
 171 
 172 
 173   //// Debug stuff ////
 174   DEBUG_ONLY(void verify(bool slow) const;)
 175 
 176   void print_on(outputStream* st) const;
 177 
 178 };
 179 
 180 } // namespace metaspace
 181 
 182 #endif // SHARE_MEMORY_METASPACE_COMMITMASK_HPP