blob: 62090a79396a564f4926b87e5981fb37ccbf2443 [file] [log] [blame]
/*
* Copyright (c) 2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "TestHarness.h"
#if PAS_ENABLE_BMALLOC
#include "bmalloc_heap.h"
#include "pas_compact_expendable_memory.h"
#include "pas_large_expendable_memory.h"
#include "pas_segregated_heap.h"
using namespace std;
namespace {
bmalloc_type theType = BMALLOC_TYPE_INITIALIZER(42, 2, "foo");
pas_heap_ref theHeap = BMALLOC_HEAP_REF_INITIALIZER(&theType);
void testPayloadImpl(pas_heap_ref& heap, bool firstRun)
{
void* object = bmalloc_iso_allocate(&heap);
CHECK(object);
CHECK_EQUAL(bmalloc_get_allocation_size(object), 48);
if (firstRun) {
// Check that so far, we don't need any expendable memories.
CHECK(!pas_compact_expendable_memory_header.header.size);
CHECK(!pas_compact_expendable_memory_header.header.bump);
CHECK(!pas_compact_expendable_memory_payload);
CHECK(!pas_large_expendable_memory_head);
}
void* smallArray = bmalloc_iso_allocate_array_by_size(&heap, 100);
CHECK(smallArray);
CHECK_EQUAL(bmalloc_get_allocation_size(smallArray), 112);
void* mediumArray = bmalloc_iso_allocate_array_by_size(&heap, 400);
CHECK(mediumArray);
CHECK_EQUAL(bmalloc_get_allocation_size(mediumArray), 400);
void* largeArray = bmalloc_iso_allocate_array_by_size(&heap, 2000);
CHECK(largeArray);
CHECK_EQUAL(bmalloc_get_allocation_size(largeArray), 2016);
void* largerArray = bmalloc_iso_allocate_array_by_size(&heap, 10000);
CHECK(largerArray);
CHECK_EQUAL(bmalloc_get_allocation_size(largerArray), 10752);
void* largestArray = bmalloc_iso_allocate_array_by_size(&heap, 100000);
CHECK(largestArray);
CHECK_EQUAL(bmalloc_get_allocation_size(largestArray), 100000);
// Check that the test indeed created expendable memories.
CHECK(pas_compact_expendable_memory_header.header.size);
CHECK(pas_compact_expendable_memory_header.header.bump);
CHECK(pas_compact_expendable_memory_payload);
CHECK(pas_large_expendable_memory_head);
CHECK(pas_large_expendable_memory_head->header.size);
CHECK(pas_large_expendable_memory_head->header.bump);
}
void testPayloadImpl(bool firstRun = true)
{
testPayloadImpl(theHeap, firstRun);
}
template<typename Func>
void forEachExpendableMemory(const Func& func)
{
pas_heap_lock_lock();
func(pas_compact_expendable_memory_header.header, pas_compact_expendable_memory_payload);
for (pas_large_expendable_memory* largeMemory = pas_large_expendable_memory_head;
largeMemory;
largeMemory = largeMemory->next)
func(largeMemory->header, pas_large_expendable_memory_payload(largeMemory));
pas_heap_lock_unlock();
}
void checkAllDecommitted()
{
forEachExpendableMemory([&] (pas_expendable_memory& header, void* payload) {
CHECK(pas_expendable_memory_num_pages_in_use(&header));
for (unsigned index = 0; index < pas_expendable_memory_num_pages(&header); ++index) {
CHECK_EQUAL(pas_expendable_memory_state_get_kind(header.states[index]),
PAS_EXPENDABLE_MEMORY_STATE_KIND_DECOMMITTED);
}
});
}
void checkAllInUseCommitted()
{
forEachExpendableMemory([&] (pas_expendable_memory& header, void* payload) {
CHECK(pas_expendable_memory_num_pages_in_use(&header));
CHECK_EQUAL(pas_expendable_memory_state_get_kind(header.states[0]),
PAS_EXPENDABLE_MEMORY_STATE_KIND_JUST_USED);
CHECK_EQUAL(pas_expendable_memory_state_get_kind(
header.states[pas_expendable_memory_num_pages_in_use(&header) - 1]),
PAS_EXPENDABLE_MEMORY_STATE_KIND_JUST_USED);
for (unsigned index = 0; index < pas_expendable_memory_num_pages_in_use(&header); ++index) {
CHECK_NOT_EQUAL(pas_expendable_memory_state_get_kind(header.states[index]),
PAS_EXPENDABLE_MEMORY_STATE_KIND_DECOMMITTED);
CHECK_LESS_EQUAL(pas_expendable_memory_state_get_kind(header.states[index]),
PAS_EXPENDABLE_MEMORY_STATE_KIND_JUST_USED);
CHECK((pas_expendable_memory_state_get_kind(header.states[index])
== PAS_EXPENDABLE_MEMORY_STATE_KIND_INTERIOR) ||
(pas_expendable_memory_state_get_kind(header.states[index])
== PAS_EXPENDABLE_MEMORY_STATE_KIND_JUST_USED));
}
for (unsigned index = pas_expendable_memory_num_pages_in_use(&header);
index < pas_expendable_memory_num_pages(&header);
++index) {
CHECK_EQUAL(pas_expendable_memory_state_get_kind(header.states[index]),
PAS_EXPENDABLE_MEMORY_STATE_KIND_DECOMMITTED);
}
});
}
void testSynchronousScavengingExpendsExpendableMemory()
{
pas_scavenger_suspend();
testPayloadImpl();
checkAllInUseCommitted();
CHECK(!pas_segregated_heap_num_size_lookup_rematerializations);
pas_scavenger_run_synchronously_now();
checkAllDecommitted();
testPayloadImpl(false);
checkAllInUseCommitted();
CHECK(pas_segregated_heap_num_size_lookup_rematerializations);
}
bool scavengerIsDone;
mutex scavengerMutex;
condition_variable scavengerCondition;
void scavengerWillShutDown()
{
lock_guard<mutex> locker(scavengerMutex);
scavengerIsDone = true;
scavengerCondition.notify_all();
}
void testScavengerExpendsExpendableMemory()
{
// Make the scavenger very fast so that this test does not run for too long.
pas_scavenger_deep_sleep_timeout_in_milliseconds = 1;
pas_scavenger_period_in_milliseconds = 1;
scavengerIsDone = false;
pas_scavenger_will_shut_down_callback = scavengerWillShutDown;
testPayloadImpl();
{
unique_lock<mutex> locker(scavengerMutex);
scavengerCondition.wait(locker, [&] () -> bool { return scavengerIsDone; });
}
checkAllDecommitted();
testPayloadImpl(false);
CHECK(pas_segregated_heap_num_size_lookup_rematerializations);
}
void testSoManyHeaps()
{
static constexpr unsigned numHeaps = 10000;
pas_heap_ref* heaps = new pas_heap_ref[numHeaps];
pas_scavenger_suspend();
for (unsigned i = numHeaps; i--;)
heaps[i] = BMALLOC_HEAP_REF_INITIALIZER(&theType);
for (unsigned i = 0; i < numHeaps; ++i)
testPayloadImpl(heaps[i], !i);
checkAllInUseCommitted();
CHECK(!pas_segregated_heap_num_size_lookup_rematerializations);
pas_scavenger_run_synchronously_now();
checkAllDecommitted();
for (unsigned i = 0; i < numHeaps; ++i)
testPayloadImpl(heaps[i], false);
checkAllInUseCommitted();
CHECK(pas_segregated_heap_num_size_lookup_rematerializations);
}
} // anonymous namespace
#endif // PAS_ENABLE_BMALLOC
void addExpendableMemoryTests()
{
#if PAS_ENABLE_BMALLOC
ADD_TEST(testSynchronousScavengingExpendsExpendableMemory());
ADD_TEST(testScavengerExpendsExpendableMemory());
ADD_TEST(testSoManyHeaps());
#endif // PAS_ENABLE_BMALLOC
}