1 /*
   2  * Copyright (c) 2018, 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 #ifndef SHARE_MEMORY_METASPACE_COMMITMASK_HPP
  26 #define SHARE_MEMORY_METASPACE_COMMITMASK_HPP
  27 
  28 #include "memory/metaspace/constants.hpp"
  29 #include "utilities/debug.hpp"
  30 #include "utilities/bitMap.hpp"
  31 #include "utilities/globalDefinitions.hpp"
  32 
  33 namespace metaspace {
  34 
  35 // A bitmap covering a range of metaspace; each bit in this mask corresponds to
  36 //
  37 class CommitMask : private CHeapBitMap {
  38 
  39   const MetaWord* const _base;
  40   const size_t _word_size;
  41 
  42   // Given an offset, in words, into the area, return the number of the bit
  43   // covering it.
  44   static idx_t bitno_for_word_offset(size_t offset) {
  45     return offset / constants::commit_granule_words;
  46   }
  47 
  48   idx_t bitno_for_address(const MetaWord* p) const {
  49     assert(p >= _base && p < _base + _word_size, "Invalid address");
  50     const size_t off = p - _base;
  51     return bitno_for_word_offset(off);
  52   }
  53 
  54   static idx_t mask_size(size_t word_size) {
  55     assert(is_aligned(word_size, constants::commit_granule_words), "size not aligned correctly.");
  56     return bitno_for_word_offset(word_size) + 1;
  57   }
  58 
  59   struct BitCounterClosure : public BitMapClosure {
  60     idx_t cnt;
  61     bool do_bit(BitMap::idx_t offset) { cnt ++; return true; }
  62   };
  63 
  64   // Missing from BitMap.
  65   // Count 1 bits in range [start, end).
  66   idx_t count_one_bits_in_range(idx_t start, idx_t end) const {
  67     if (start == end) {
  68       return at(start) ? 1 : 0;
  69     }
  70     // TODO: This can be done more efficiently.
  71     BitCounterClosure bcc;
  72     bcc.cnt = 0;
  73     iterate(&bcc, start, end + 1);
  74     return bcc.cnt;
  75   }
  76 
  77 #ifdef ASSERT
  78   // Given a pointer, check if it points into the range this bitmap covers.
  79   bool is_pointer_valid(const MetaWord* p) const {
  80     return p >= _base && p < _base + _word_size;
  81   }
  82   // Given a pointer, check if it points into the range this bitmap covers,
  83   // and if it is aligned to commit granule border.
  84   bool is_pointer_valid_and_aligned(const MetaWord* p) const {
  85     return is_pointer_valid(p) && is_aligned(p, constants::commit_granule_bytes);
  86   }
  87   // Given a pointer, check if it points into the range this bitmap covers.
  88   void check_pointer(const MetaWord* p) const {
  89     assert(is_pointer_valid(p),
  90            "Pointer " PTR_FORMAT " not in range of this bitmap [" PTR_FORMAT ", " PTR_FORMAT ").",
  91            p2i(p), p2i(_base), p2i(_base + _word_size));
  92   }
  93   // Given a pointer, check if it points into the range this bitmap covers,
  94   // and if it is aligned to commit granule border.
  95   void check_pointer_aligned(const MetaWord* p) const {
  96     check_pointer(p);
  97     assert(is_aligned(p, constants::commit_granule_bytes),
  98            "Pointer " PTR_FORMAT " should be aligned to commit granule size " SIZE_FORMAT ".", p2i(p), constants::commit_granule_bytes);
  99   }
 100   // Given a range, check if it points into the range this bitmap covers,
 101   // and if its borders are aligned to commit granule border.
 102   void check_range(const MetaWord* start, size_t word_size) const {
 103     check_pointer_aligned(start);
 104     check_pointer_aligned(start + word_size);
 105   }
 106 #endif
 107 
 108   // Marks a single commit granule as committed or uncomitted and returns
 109   // its prior state.
 110   bool mark_granule(idx_t bitno, bool value) {
 111     bool b = at(bitno);
 112     at_put(bitno, value);
 113     return b;
 114   }
 115 
 116 public:
 117 
 118   CommitMask(const MetaWord* start, size_t word_size)
 119     : CHeapBitMap(mask_size(word_size))
 120     , _base(start)
 121     , _word_size(word_size)
 122   {}
 123 
 124   virtual ~CommitMask() {}
 125 
 126   // Given an address, returns true if the address is committed, false if not.
 127   bool is_committed_address(const MetaWord* p) const {
 128     DEBUG_ONLY(check_pointer(p));
 129     const idx_t bitno = bitno_for_address(p);
 130     return at(bitno);
 131   }
 132 
 133   // Given an address range [start, end), returns true if area is fully committed through.
 134   bool is_fully_committed_range(const MetaWord* start, size_t word_size) const {
 135     DEBUG_ONLY(check_range(start, word_size));
 136     const idx_t b1 = bitno_for_address(start);
 137     const idx_t b2 = bitno_for_address(start + word_size);
 138     return get_next_zero_offset(b1, b2) == b2;
 139   }
 140 
 141   // Given an address range, return size, in number of words, of committed area within that range.
 142   size_t get_committed_size_in_range(const MetaWord* start, size_t word_size) const {
 143     DEBUG_ONLY(check_range(start, word_size));
 144     const idx_t b1 = bitno_for_address(start);
 145     const idx_t b2 = bitno_for_address(start + word_size);
 146     const idx_t num_bits = count_one_bits_in_range(b1, b2);
 147     return num_bits * constants::commit_granule_words;
 148   }
 149 
 150   // Return total committed size, in number of words.
 151   size_t get_committed_size() const {
 152     return count_one_bits() * constants::commit_granule_words;
 153   }
 154 
 155   // Mark a whole address range [start, end) as committed.
 156   // Return the number of words which had already been committed before this operation.
 157   size_t mark_range_as_committed(const MetaWord* start, size_t word_size) {
 158     DEBUG_ONLY(check_range(start, word_size));
 159     const idx_t b1 = bitno_for_address(start);
 160     const idx_t b2 = bitno_for_address(start + word_size);
 161     if (b1 == b2) { // Simple case, 1 granule
 162       return mark_granule(b1, true) ? constants::commit_granule_words : 0;
 163     }
 164     const idx_t bits_set_before = count_one_bits_in_range(bitno_for_address(start), word_size);
 165     set_range(b1, b2);
 166     return bits_set_before * constants::commit_granule_words;
 167   }
 168 
 169   // Mark a whole address range [start, end) as uncommitted.
 170   // Return the number of words which had already been uncommitted before this operation.
 171   size_t mark_range_as_uncommitted(const MetaWord* start, size_t word_size) {
 172     DEBUG_ONLY(check_range(start, word_size));
 173     const idx_t b1 = bitno_for_address(start);
 174     const idx_t b2 = bitno_for_address(start + word_size);
 175     if (b1 == b2) { // Simple case, 1 granule
 176       return mark_granule(b1, true) ? constants::commit_granule_words : 0;
 177     }
 178     const idx_t bits_set_before = count_one_bits_in_range(bitno_for_address(start), word_size);
 179     clear_range(b1, b2);
 180     return ((b2 - b1) - bits_set_before) * constants::commit_granule_words;
 181   }
 182 
 183 
 184   //// Debug stuff ////
 185   DEBUG_ONLY(void verify(bool slow) const;)
 186 
 187 };
 188 
 189 } // namespace metaspace
 190 
 191 #endif // SHARE_MEMORY_METASPACE_COMMITMASK_HPP