/* * Copyright (c) 2019, SAP. All rights reserved. * Copyright (c) 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. */ #include "precompiled.hpp" #include "memory/allocation.hpp" #include "memory/metaspace/abstractPool.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "unittest.hpp" #include // Test AbstractPool class template class AbstractPoolTest { public: typedef metaspace::AbstractPool PoolType; private: PoolType _pool; size_t _num_allocated; // Array of the same size as the pool max capacity; holds the allocated elements. E** _elems; // Helper function. Writes a canary into *E. static void mark_element(E* e, uint8_t v) { *(uint8_t*)e = v; } // Helper function. Check expected canary in *E. static void check_element_mark(const E* e, uint8_t v) { uint8_t v2 = *(const uint8_t*)e; ASSERT_TRUE(v == v2); } // Given an element, check its index resolution. void check_element_to_index_translation(const E* e) { I idx = _pool->index_for_elem(e); ASSERT_TRUE(_pool->is_valid_index(idx)); E* e2 = _pool->elem_at_index(idx); ASSERT_TRUE(e2 = e); } void attempt_free_at(size_t index) { if (_elems[index] == NULL) { return; } check_element_mark(_elems[index]); _pool.return_element(_elems[index]); _elems[index] = NULL; ASSERT_GT(_num_allocated, 0); _num_allocated --; ASSERT_EQ(_num_allocated, _pool.used()); } void attempt_allocate_at(size_t index) { assert(index <= _pool.max_capacity(), "Sanity"); if (_elems[index] != NULL) { return; } E* e = _pool.allocate_element(); if (e == NULL) { ASSERT_TRUE(_pool.is_full()); ASSERT_TRUE(_pool.used() == _pool.max_capacity()); } else { mark_element(e, index & 0xFF); check_element_to_index_translation(e); _num_allocated ++; _elems[index] = e; ASSERT_EQ(_num_allocated, _pool.used()); } } void attempt_allocate_or_free_at(size_t index) { if (_elems[index] == NULL) { attempt_allocate_at(index); } else { attempt_free_at(index); } } // Fill pool until exhaustion. void test_exhaustion() { for (size_t i = 0; i < _pool.max_capacity(); i ++) { attempt_allocate_at(i); } ASSERT_TRUE(_pool.used() == _pool.max_capacity()); ASSERT_TRUE(_pool.is_full()); DEBUG_ONLY(_pool.verify(true);) } // Fill pool until exhaustion. void test_draining() { for (size_t i = 0; i < _pool.max_capacity(); i ++) { attempt_free_at(i); } ASSERT_TRUE(_pool.used() == _pool.max_capacity()); ASSERT_TRUE(_pool.is_empty()); DEBUG_ONLY(_pool.verify(true);) } // Randomly allocate from the pool and free. Slight preference for allocation. void test_random_alloc_free(int num_iterations) { for (int iter = 0; iter < num_iterations; iter ++) { size_t index = (size_t)os::random() % _pool.max_capacity(); attempt_allocate_or_free_at(index); } DEBUG_ONLY(_pool.verify(true);) } static void test_once() { const int max_for_index_type = (int) std::numeric_limits::max(); const size_t max = MAX2(os::random() % max_for_index_type, 2); const size_t inc = MAX2(max / ((os::random() % 100) + 1), (size_t)1); AbstractPoolTest test(inc, max); test.test_random_alloc_free(100); test.test_exhaustion(); test.test_draining(); test.test_random_alloc_free(100); } public: AbstractPoolTest(size_t capacity_increase, size_t max_capacity) : _pool("just-a-test-pool", max_capacity, capacity_increase), _num_allocated(0), _elems(NULL) { _elems = NEW_C_HEAP_ARRAY(E*, max_capacity, mtInternal); memset(_elems, 0, max_capacity * sizeof(E)); } ~AbstractPoolTest() { FREE_C_HEAP_ARRAY(E*, _elems); } static void run_tests() { for (int i = 0; i < 1000; i ++) { test_once(); } } }; TEST(metaspace, pool1) { AbstractPoolTest::run_tests(); } TEST(metaspace, pool2) { AbstractPoolTest::run_tests(); } TEST(metaspace, pool3) { AbstractPoolTest::run_tests(); } struct S1 { uint16_t i1; uint16_t i2; uint16_t i3; }; TEST(metaspace, pool4) { AbstractPoolTest::run_tests(); } TEST(metaspace, pool5) { AbstractPoolTest::run_tests(); } // crooked and unaligned :) struct S2 { char i [0x100]; }; TEST(metaspace, pool6) { AbstractPoolTest::run_tests(); } TEST(metaspace, pool7) { AbstractPoolTest::run_tests(); } // crooked and unaligned :) /* * FOr now, excluded: see STATIC_ASSERT in AssertPool. We should not be able to define an AbstractPool where E has a smaller alignment requirement than I. struct S3 { char i [33]; }; TEST(metaspace, pool8) { AbstractPoolTest::run_tests(); } TEST(metaspace, pool9) { AbstractPoolTest::run_tests(); } */