/* * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #ifndef SHARE_MEMORY_METASPACE_ABSTRACTPOOL_HPP #define SHARE_MEMORY_METASPACE_ABSTRACTPOOL_HPP #include "memory/allocation.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" namespace metaspace { // A simple helper class; // // Holds a linear array of elements of type E. Array lives in C heap and // expands automatically. Free elements can be returned, will be kept in // a freelist. Elements can be retrieved by their index (index type I). // Index 0 will never be allocated and can be relied upon as being invalid. // // E must be of at least sizeof(I), and I must be an integral size. // template class AbstractPool { public: typedef I size_type_t; private: E* _arr; size_type_t _capacity; size_type_t _top; I _freelist; size_type_t _num_in_freelist; const size_type_t _max_capacity; const size_type_t _capacity_increase; const char* const _name; // Enlarge internal array if needed. Zero out new portion of array. void enlarge_to(size_type_t new_len) { E* p = REALLOC_C_HEAP_ARRAY(E, _arr, new_len, mtInternal); _arr = p; _capacity = new_len; } //// freelist handling //// struct free_elem_t { I next_idx; }; // free_elem_t must fit into an E, and sizeof(E) must be aligned such that // one can write free_elem_t at &E. STATIC_ASSERT(sizeof(E) >= sizeof(free_elem_t)); STATIC_ASSERT(sizeof(E) % sizeof(free_elem_t) == 0); E* take_from_freelist() { I idx = _freelist; if (0 == idx) { assert(_num_in_freelist == 0, "counter mismatch"); return NULL; } E* p = elem_at_index(idx); _freelist = ((free_elem_t*)p)->next_idx; assert(_num_in_freelist > 0, "counter underflow"); _num_in_freelist --; return p; } void add_to_freelist(E* p) { ((free_elem_t*)p)->next_idx = _freelist; _freelist = index_for_elem(p); _num_in_freelist ++; } public: AbstractPool(const char* name, size_type_t max_capacity, size_type_t capacity_increase) : _arr(NULL), _capacity(0), _top(0), _freelist((I)0), _num_in_freelist(0), _max_capacity(max_capacity), _capacity_increase(capacity_increase), _name(name) {} ~AbstractPool() { FREE_C_HEAP_ARRAY(E, _arr); } I index_for_elem(const E* p) const { DEBUG_ONLY(check_elem(p)); return p - _arr; } E* elem_at_index(I i) const { DEBUG_ONLY(check_idx(i)); return _arr + i; } // Allocate a new element. Enlarge internal array if needed. // Will return NULL if max_size is reached. E* allocate_element() { DEBUG_ONLY(verify(false)); E* p = take_from_freelist(); if (p != NULL) { return p; } if (_capacity == _top) { // Need to enlarge. size_type_t new_cap = MIN2((size_type_t)(_capacity + _capacity_increase), _max_capacity); if (new_cap == _capacity) { return NULL; // capped out. } enlarge_to(new_cap); } // Avoid handing out anything at index 0. // This allows callers to use "I index == 0" as "invalid ref". if (_top == 0) { _top ++; } p = _arr + _top; _top ++; return p; } void return_element(E* p) { DEBUG_ONLY(check_elem(p)); add_to_freelist(p); } // Returns number of allocated elements. size_type_t used() const { return _top - _num_in_freelist; } // True if pool is full. bool is_full() const { return used() == max_capacity(); } // True if pool is empty. bool is_empty() const { return used() == 0; } // Returns number of elements in free list. size_type_t free_elements() const { return _num_in_freelist; } // Returns max. capacity of this pool. size_type_t max_capacity() const { return _max_capacity; } // Returns size of memory used. size_t memory_footprint_words() const { return _capacity * sizeof(E); } // Returns true if p was allocated from this pool. bool is_valid_elem(const E* p) const { return p >= _arr && p < _arr + _top; } // Returns true if the index points into the allocated portion of the pool. // Note: may point to an allocated element or one in the freelist. bool is_valid_index(I idx) const { return idx > 0 && idx < _top; } #ifdef ASSERT void check_elem(const E* p) const { assert(is_valid_elem(p), "invalid pointer"); } void check_idx(I i) const { assert(i > 0 && i <= _top, "invalid index"); } void verify(bool slow) const { assert(_top <= _capacity && _capacity <= _max_capacity, "Sanity"); assert(used() <= _capacity, "Sanity"); if (slow) { // check freelist. I idx = _freelist; size_type_t num = 0; while (idx != 0) { num ++; idx = ((free_elem_t*)elem_at_index(idx))->next_idx; } assert(num == _num_in_freelist, "counter mismatch"); } } #endif // ASSERT }; } // namespace metaspace #endif // SHARE_MEMORY_METASPACE_ABSTRACTPOOL_HPP