1 /*
  2  * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
  3  * Copyright (c) 2020 SAP SE. All rights reserved.
  4  * Copyright (c) 2023 Red Hat, Inc. All rights reserved.
  5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  6  *
  7  * This code is free software; you can redistribute it and/or modify it
  8  * under the terms of the GNU General Public License version 2 only, as
  9  * published by the Free Software Foundation.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  *
 25  */
 26 
 27 #ifndef SHARE_MEMORY_METASPACE_BINLIST_HPP
 28 #define SHARE_MEMORY_METASPACE_BINLIST_HPP
 29 
 30 #include "memory/metaspace/counters.hpp"
 31 #include "memory/metaspace/metaspaceCommon.hpp"
 32 #include "utilities/align.hpp"
 33 #include "utilities/debug.hpp"
 34 #include "utilities/globalDefinitions.hpp"
 35 
 36 namespace metaspace {
 37 
 38 // BinList is a data structure to manage small to very small memory blocks
 39 // (only a few words). It is used to manage deallocated blocks - see
 40 // class FreeBlocks.
 41 
 42 // Memory blocks are kept in a vector of linked lists of equi-sized blocks:
 43 //
 44 // wordsize
 45 //
 46 //       +---+   +---+   +---+      +---+
 47 //  1    |   |-->|   |-->|   |-...->|   |
 48 //       +---+   +---+   +---+      +---+
 49 //
 50 //       +----+   +----+   +----+      +----+
 51 //  2    |    |-->|    |-->|    |-...->|    |
 52 //       +----+   +----+   +----+      +----+
 53 //
 54 //       +-----+   +-----+   +-----+      +-----+
 55 //  3    |     |-->|     |-->|     |-...->|     |
 56 //       +-----+   +-----+   +-----+      +-----+
 57 //  .
 58 //  .
 59 //  .
 60 //
 61 //       +----------+   +----------+   +----------+      +----------+
 62 //  n    |          |-->|          |-->|          |-...->|          |
 63 //       +----------+   +----------+   +----------+      +----------+
 64 
 65 // Insertion is of course fast, O(1).
 66 //
 67 // On retrieval, we attempt to find the closest fit to a given size, walking the
 68 // list head vector (a bitmask is used to speed that part up).
 69 //
 70 // This structure is a bit expensive in memory costs (we pay one pointer per managed
 71 // block size) so we only use it for a small number of sizes.
 72 
 73 template <int num_lists>
 74 class BinListImpl {
 75 
 76   struct Block {
 77     Block* const _next;
 78     Block(Block* next) : _next(next) {}
 79   };
 80 
 81 #define BLOCK_FORMAT              "Block @" PTR_FORMAT ": size: " SIZE_FORMAT ", next: " PTR_FORMAT
 82 #define BLOCK_FORMAT_ARGS(b, sz)  p2i(b), (sz), p2i((b)->_next)
 83 
 84   // Block size must be exactly one word size.
 85   STATIC_ASSERT(sizeof(Block) == BytesPerWord);
 86   STATIC_ASSERT(num_lists > 0);
 87 
 88 public:
 89 
 90   // Minimal word size a block must have to be manageable by this structure.
 91   const static size_t MinWordSize = 1;
 92 
 93   // Maximal (incl) word size a block can have to be manageable by this structure.
 94   const static size_t MaxWordSize = num_lists;
 95 
 96 private:
 97 
 98   Block* _blocks[num_lists];
 99 
100   MemRangeCounter _counter;
101 
102   // Given a word size, returns the index of the list holding blocks of that size
103   static int index_for_word_size(size_t word_size) {
104     int index = (int)(word_size - MinWordSize);
105     assert(index >= 0 && index < num_lists, "Invalid index %d", index);
106     return index;
107   }
108 
109   // Given an index of a list, return the word size that list serves
110   static size_t word_size_for_index(int index) {
111     assert(index >= 0 && index < num_lists, "Invalid index %d", index);
112     return index + MinWordSize;
113   }
114 
115   // Search the range [index, _num_lists) for the smallest non-empty list. Returns -1 on fail.
116   int index_for_next_non_empty_list(int index) {
117     assert(index >= 0 && index < num_lists, "Invalid index %d", index);
118     int i2 = index;
119     while (i2 < num_lists && _blocks[i2] == nullptr) {
120       i2 ++;
121     }
122     return i2 == num_lists ? -1 : i2;
123   }
124 
125 #ifdef ASSERT
126   static const uintptr_t canary = 0xFFEEFFEE;
127   static void write_canary(MetaWord* p, size_t word_size) {
128     if (word_size > 1) { // 1-word-sized blocks have no space for a canary
129       ((uintptr_t*)p)[word_size - 1] = canary;
130     }
131   }
132   static bool check_canary(const Block* b, size_t word_size) {
133     return word_size == 1 || // 1-word-sized blocks have no space for a canary
134            ((const uintptr_t*)b)[word_size - 1] == canary;
135   }
136 #endif
137 
138 public:
139 
140   BinListImpl() {
141     for (int i = 0; i < num_lists; i++) {
142       _blocks[i] = nullptr;
143     }
144   }
145 
146   void add_block(MetaWord* p, size_t word_size) {
147     assert(word_size >= MinWordSize &&
148            word_size <= MaxWordSize, "bad block size");
149     DEBUG_ONLY(write_canary(p, word_size);)
150     const int index = index_for_word_size(word_size);
151     Block* old_head = _blocks[index];
152     Block* new_head = new (p) Block(old_head);
153     _blocks[index] = new_head;
154     _counter.add(word_size);
155   }
156 
157   // Given a word_size, searches and returns a block of at least that size.
158   // Block may be larger. Real block size is returned in *p_real_word_size.
159   MetaWord* remove_block(size_t word_size, size_t* p_real_word_size) {
160     assert(word_size >= MinWordSize &&
161            word_size <= MaxWordSize, "bad block size " SIZE_FORMAT ".", word_size);
162     int index = index_for_word_size(word_size);
163     index = index_for_next_non_empty_list(index);
164     if (index != -1) {
165       Block* b = _blocks[index];
166       const size_t real_word_size = word_size_for_index(index);
167       assert(b != nullptr, "Sanity");
168       assert(check_canary(b, real_word_size),
169              "bad block in list[%d] (" BLOCK_FORMAT ")", index, BLOCK_FORMAT_ARGS(b, real_word_size));
170       _blocks[index] = b->_next;
171       _counter.sub(real_word_size);
172       *p_real_word_size = real_word_size;
173       return (MetaWord*)b;
174     } else {
175       *p_real_word_size = 0;
176       return nullptr;
177     }
178   }
179 
180   // Returns number of blocks in this structure
181   unsigned count() const { return _counter.count(); }
182 
183   // Returns total size, in words, of all elements.
184   size_t total_size() const { return _counter.total_size(); }
185 
186   bool is_empty() const { return count() == 0; }
187 
188 #ifdef ASSERT
189   void verify() const {
190     MemRangeCounter local_counter;
191     for (int i = 0; i < num_lists; i++) {
192       const size_t s = word_size_for_index(i);
193       int pos = 0;
194       for (Block* b = _blocks[i]; b != nullptr; b = b->_next, pos++) {
195         assert(check_canary(b, s), "");
196         local_counter.add(s);
197       }
198     }
199     local_counter.check(_counter);
200   }
201 #endif // ASSERT
202 
203 };
204 
205 typedef BinListImpl<32> BinList32;
206 
207 } // namespace metaspace
208 
209 #endif // SHARE_MEMORY_METASPACE_BINLIST_HPP