blob: 56ad446c2e73334d11e11920ae9979daaed55183 [file] [log] [blame]
/*
* Copyright (c) 2018-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_THINGY
#include "HeapLocker.h"
#include "SuspendScavenger.h"
#include "thingy_heap_config.h"
#include <functional>
#include <map>
#include "pas_all_heaps.h"
#include "pas_baseline_allocator_table.h"
#include "pas_bootstrap_free_heap.h"
#include "pas_get_object_kind.h"
#include "pas_heap.h"
#include "pas_heap_config.h"
#include "pas_heap_lock.h"
#include "pas_heap_ref.h"
#include "pas_intrinsic_heap_support.h"
#include "pas_large_free_heap_helpers.h"
#include "pas_large_sharing_pool.h"
#include "pas_large_utility_free_heap.h"
#include "pas_megapage_cache.h"
#include "pas_page_malloc.h"
#include "pas_scavenger.h"
#include "pas_segregated_size_directory.h"
#include "pas_segregated_size_directory_inlines.h"
#include "pas_thread_local_cache.h"
#include "pas_utility_heap.h"
#include "thingy_heap.h"
#include <set>
#include <thread>
#include <vector>
using namespace std;
namespace {
#if SEGHEAP
void flushDeallocationLog()
{
#if TLC
pas_thread_local_cache_flush_deallocation_log(pas_thread_local_cache_try_get(),
pas_lock_is_not_held);
#endif
}
#endif
void flushDeallocationLogAndStopAllocators()
{
#if TLC
pas_thread_local_cache_shrink(pas_thread_local_cache_try_get(), pas_lock_is_not_held);
pas_baseline_allocator_table_for_all(pas_allocator_scavenge_force_stop_action);
#endif
}
// FIXME: Once we add real scavenging, we will want to add a shrinkHeaps() function that does
// flushDeallocationLogAndStopAllocators() plus the actual scavenging.
pas_segregated_size_directory* sizeClassFor(void* object)
{
#if SEGHEAP
return pas_segregated_size_directory_for_object(
reinterpret_cast<uintptr_t>(object), &thingy_heap_config);
#else
return nullptr;
#endif
}
bool isLarge(void* object)
{
return pas_get_object_kind(object, THINGY_HEAP_CONFIG) == pas_large_object_kind;
}
bool forEachLiveUtilityObjectAdapter(uintptr_t begin,
size_t size,
void* arg)
{
function<bool(void*, size_t)>* callback = reinterpret_cast<function<bool(void*, size_t)>*>(arg);
return (*callback)(reinterpret_cast<void*>(begin), size);
}
void forEachLiveUtilityObject(function<bool(void*, size_t)> callback)
{
SuspendScavenger suspendScavenger;
HeapLocker heapLocker;
pas_utility_heap_for_each_live_object(forEachLiveUtilityObjectAdapter, &callback);
}
#if SEGHEAP
bool forEachCommittedViewAdapter(pas_segregated_heap* heap,
pas_segregated_size_directory* sizeClass,
pas_segregated_view view,
void* arg)
{
function<bool(pas_segregated_view)>* callback =
reinterpret_cast<function<bool(pas_segregated_view)>*>(arg);
return (*callback)(view);
}
void forEachCommittedView(pas_heap* heap, function<bool(pas_segregated_view)> callback)
{
pas_segregated_heap_for_each_committed_view(&heap->segregated_heap,
forEachCommittedViewAdapter,
&callback);
}
size_t numCommittedViews(pas_heap* heap)
{
return pas_segregated_heap_num_committed_views(&heap->segregated_heap);
}
#endif // SEGHEAP
#if TLC
size_t numViews(pas_heap* heap)
{
return pas_segregated_heap_num_views(&heap->segregated_heap);
}
#endif // TLC
void verifyMinimumObjectDistance(const set<void*>& objects,
size_t minimumDistance)
{
uintptr_t lastBegin = 0;
for (void* object : objects) {
uintptr_t begin = reinterpret_cast<uintptr_t>(object);
CHECK_GREATER_EQUAL(begin, lastBegin);
if (begin < lastBegin + minimumDistance) {
cout << "Object " << reinterpret_cast<void*>(lastBegin) << " is too close to " << object << endl;
dumpObjectSet(objects);
}
CHECK_GREATER_EQUAL(begin - lastBegin, minimumDistance);
lastBegin = begin;
}
}
template<typename ObjectVerificationFunc>
void verifyObjectSet(const set<void*>& expectedObjects,
const vector<pas_heap*>& heaps,
const ObjectVerificationFunc& objectVerificationFunc)
{
static constexpr bool verbose = false;
flushDeallocationLogAndStopAllocators();
if (verbose)
cout << "Iterating live objects...\n";
set<void*> foundObjects;
for (pas_heap* heap : heaps) {
forEachLiveObject(
heap,
[&] (void* object, size_t size) -> bool {
if (verbose)
cout << "Found " << object << " with size " << size << "\n";
CHECK(expectedObjects.count(object));
CHECK(!foundObjects.count(object));
objectVerificationFunc(object, size);
foundObjects.insert(object);
return true;
});
}
CHECK_EQUAL(foundObjects.size(), expectedObjects.size());
}
template<typename ObjectVerificationFunc>
void verifyUtilityObjectSet(const set<void*>& expectedObjects,
const ObjectVerificationFunc& objectVerificationFunc)
{
flushDeallocationLogAndStopAllocators();
set<void*> foundObjects;
set<void*> objectsToFind = expectedObjects;
forEachLiveUtilityObject(
[&] (void* object, size_t size) -> bool {
if (!objectsToFind.count(object))
return true;
CHECK(!foundObjects.count(object));
objectVerificationFunc(object, size);
foundObjects.insert(object);
objectsToFind.erase(object);
return true;
});
CHECK(!objectsToFind.size());
CHECK_GREATER_EQUAL(foundObjects.size(), expectedObjects.size());
}
template<typename ObjectVerificationFunc>
void verifyObjectSet(const set<void*>& expectedObjects,
pas_heap* heap,
const ObjectVerificationFunc& objectVerificationFunc)
{
vector<pas_heap*> heaps = { heap };
verifyObjectSet(expectedObjects, heaps, objectVerificationFunc);
}
void verifyObjectSet(const set<void*>& expectedObjects,
pas_heap* heap,
size_t resultingObjectSize)
{
verifyObjectSet(
expectedObjects,
heap,
[&] (void* object, size_t size) {
CHECK_EQUAL(size, resultingObjectSize);
});
}
void verifyObjectSet(const map<void*, size_t>& objectByPtr,
pas_heap* heap)
{
set<void*> expectedObjects;
for (const auto& pair : objectByPtr)
expectedObjects.insert(pair.first);
verifyObjectSet(
expectedObjects,
heap,
[] (void*, size_t) { });
}
void verifyHeapEmpty(pas_heap* heap)
{
pas_scavenger_suspend();
flushDeallocationLogAndStopAllocators();
forEachLiveObject(
heap,
[&] (void* object, size_t size) -> bool {
CHECK(!"should not have found any objects");
return true;
});
#if SEGHEAP
forEachCommittedView(
heap,
[&] (pas_segregated_view view) -> bool {
CHECK(pas_segregated_view_is_eligible(view));
#if TLC
CHECK(pas_segregated_view_is_empty(view));
#endif
return true;
});
#endif // SEGHEAP
pas_scavenger_resume();
}
class Allocator {
public:
virtual ~Allocator() = default;
virtual void* allocate() const = 0;
virtual pas_heap* heap() const = 0;
#if PAS_ENABLE_TESTING
virtual uint64_t allocateSlowPathCount() const = 0;
#endif // PAS_ENABLE_TESTING
};
class PrimitiveAllocator : public Allocator {
public:
PrimitiveAllocator(size_t objectSize)
: m_objectSize(objectSize)
{
}
void* allocate() const override
{
return thingy_try_allocate_primitive(m_objectSize);
}
pas_heap* heap() const override
{
return &thingy_primitive_heap;
}
#if PAS_ENABLE_TESTING
uint64_t allocateSlowPathCount() const override
{
return thingy_allocator_counts.slow_paths +
thingy_allocator_counts.slow_refills;
}
#endif // PAS_ENABLE_TESTING
private:
size_t m_objectSize;
};
class PrimitiveReallocAllocator : public Allocator {
public:
PrimitiveReallocAllocator(size_t objectSize)
: m_objectSize(objectSize)
{
}
void* allocate() const override
{
return thingy_try_reallocate_primitive(nullptr, m_objectSize);
}
pas_heap* heap() const override
{
return &thingy_primitive_heap;
}
#if PAS_ENABLE_TESTING
uint64_t allocateSlowPathCount() const override
{
return thingy_allocator_counts.slow_paths +
thingy_allocator_counts.slow_refills;
}
#endif // PAS_ENABLE_TESTING
private:
size_t m_objectSize;
};
class AlignedPrimitiveAllocator : public Allocator {
public:
AlignedPrimitiveAllocator(size_t objectSize, size_t alignment)
: m_objectSize(objectSize)
, m_alignment(alignment)
{
}
void* allocate() const override
{
void* result = thingy_try_allocate_primitive_with_alignment(m_objectSize, m_alignment);
CHECK(pas_is_aligned(reinterpret_cast<uintptr_t>(result), m_alignment));
return result;
}
pas_heap* heap() const override
{
return &thingy_primitive_heap;
}
#if PAS_ENABLE_TESTING
uint64_t allocateSlowPathCount() const override
{
return thingy_allocator_counts.slow_paths +
thingy_allocator_counts.slow_refills;
}
#endif // PAS_ENABLE_TESTING
private:
size_t m_objectSize;
size_t m_alignment;
};
pas_heap_ref* createIsolatedHeapRef(size_t size, size_t alignment)
{
pas_heap_ref* heapRef = new pas_heap_ref;
heapRef->type = reinterpret_cast<const pas_heap_type*>(PAS_SIMPLE_TYPE_CREATE(size, alignment));
heapRef->heap = nullptr;
#if TLC
heapRef->allocator_index = 0;
#endif
return heapRef;
}
class IsolatedHeapAllocator : public Allocator {
public:
IsolatedHeapAllocator(pas_heap_ref* heapRef)
: m_heapRef(heapRef)
{
}
IsolatedHeapAllocator(size_t size, size_t alignment)
: IsolatedHeapAllocator(createIsolatedHeapRef(size, alignment))
{
}
void* allocate() const override
{
void* result = thingy_try_allocate(m_heapRef);
CHECK(pas_is_aligned(reinterpret_cast<uintptr_t>(result),
pas_simple_type_alignment(reinterpret_cast<pas_simple_type>(m_heapRef->type))));
return result;
}
pas_heap* heap() const override
{
return thingy_heap_ref_get_heap(m_heapRef);
}
#if PAS_ENABLE_TESTING
uint64_t allocateSlowPathCount() const override
{
return thingy_allocator_counts.slow_paths +
thingy_allocator_counts.slow_refills;
}
#endif // PAS_ENABLE_TESTING
private:
pas_heap_ref* m_heapRef;
};
class IsolatedHeapArrayAllocator : public Allocator {
public:
IsolatedHeapArrayAllocator(pas_heap_ref* heapRef, size_t count, size_t alignment)
: m_heapRef(heapRef)
, m_count(count)
, m_alignment(alignment)
{
}
IsolatedHeapArrayAllocator(size_t size, size_t typeAlignment, size_t count, size_t alignment)
: IsolatedHeapArrayAllocator(createIsolatedHeapRef(size, typeAlignment), count, alignment)
{
}
void* allocate() const override
{
void* result = thingy_try_allocate_array(m_heapRef, m_count, m_alignment);
CHECK(pas_is_aligned(reinterpret_cast<uintptr_t>(result),
pas_simple_type_alignment(reinterpret_cast<pas_simple_type>(m_heapRef->type))));
CHECK(pas_is_aligned(reinterpret_cast<uintptr_t>(result), m_alignment));
return result;
}
pas_heap* heap() const override
{
return thingy_heap_ref_get_heap(m_heapRef);
}
#if PAS_ENABLE_TESTING
uint64_t allocateSlowPathCount() const override
{
return thingy_allocator_counts.slow_paths +
thingy_allocator_counts.slow_refills;
}
#endif // PAS_ENABLE_TESTING
private:
pas_heap_ref* m_heapRef;
size_t m_count;
size_t m_alignment;
};
class CounterScope {
public:
CounterScope(function<uint64_t()> getCount, uint64_t expectedCountAtEnd)
: m_getCount(getCount)
, m_countAtBegin(getCount())
, m_expectedCountAtEnd(expectedCountAtEnd)
{
}
~CounterScope()
{
CHECK_EQUAL(m_getCount() - m_countAtBegin, m_expectedCountAtEnd);
}
private:
function<uint64_t()> m_getCount;
uint64_t m_countAtBegin;
uint64_t m_expectedCountAtEnd;
};
void testDeallocateNull()
{
thingy_deallocate(nullptr);
}
void deallocationFailureCallback(const char* reason, void* begin)
{
cout << " Failed deallocation: " << reason << ": " << begin << endl;
testSucceeded();
}
class DeallocationShouldFail {
public:
DeallocationShouldFail()
{
pas_set_deallocation_did_fail_callback(deallocationFailureCallback);
}
~DeallocationShouldFail()
{
CHECK(!"Should not have succeeded.");
}
};
void testDeallocateMalloc()
{
DeallocationShouldFail deallocationShouldFail;
thingy_deallocate(malloc(1));
}
void testDeallocateStack()
{
DeallocationShouldFail deallocationShouldfail;
int stack;
thingy_deallocate(&stack);
}
template<typename ObjectVerifyFunc, typename BeforeFreeVerifyFunc>
void testSimpleAllocation(const Allocator& allocator,
size_t resultingObjectSize,
size_t count,
bool verifyBeforeFreeing,
const ObjectVerifyFunc& objectVerifyFunc,
const BeforeFreeVerifyFunc& beforeFreeVerifyFunc)
{
set<void*> objects;
#if TLC
pas_scavenger_suspend();
pas_large_sharing_pool_validate_each_splat = true;
#endif
for (size_t index = count; index--;) {
void* object = allocator.allocate();
CHECK(object);
objectVerifyFunc(object);
CHECK(pas_is_aligned(
reinterpret_cast<uintptr_t>(object),
pas_simple_type_alignment(reinterpret_cast<pas_simple_type>(allocator.heap()->type))));
CHECK(!objects.count(object));
CHECK_EQUAL(thingy_get_allocation_size(object), resultingObjectSize);
objects.insert(object);
}
verifyMinimumObjectDistance(objects, resultingObjectSize);
beforeFreeVerifyFunc();
CHECK_EQUAL(objects.size(), count);
if (verifyBeforeFreeing)
verifyObjectSet(objects, allocator.heap(), resultingObjectSize);
for (void* object : objects)
thingy_deallocate(object);
verifyHeapEmpty(allocator.heap());
CHECK_EQUAL(pas_scavenger_current_state, pas_scavenger_state_no_thread);
}
#if SEGHEAP
void testSmallOrMediumAllocation(const Allocator& allocator,
size_t resultingObjectSize,
pas_object_kind expectedObjectKind,
size_t count,
bool verifyBeforeFreeing,
size_t expectedNumberOfCommittedPages,
uint64_t expectedAllocateSlowPathCount)
{
testSimpleAllocation(
allocator,
resultingObjectSize,
count,
verifyBeforeFreeing,
[&] (void* object) {
CHECK_EQUAL(pas_get_object_kind(object, THINGY_HEAP_CONFIG),
expectedObjectKind);
},
[&] () {
CHECK_EQUAL(numCommittedViews(allocator.heap()), expectedNumberOfCommittedPages);
});
CHECK_EQUAL(numCommittedViews(allocator.heap()), expectedNumberOfCommittedPages);
#if PAS_ENABLE_TESTING
CHECK_EQUAL(allocator.allocateSlowPathCount(), expectedAllocateSlowPathCount);
#endif // PAS_ENABLE_TESTING
}
void testSmallAllocation(const Allocator& allocator,
size_t resultingObjectSize,
size_t count,
bool verifyBeforeFreeing,
size_t expectedNumberOfCommittedPages,
uint64_t expectedAllocateSlowPathCount)
{
testSmallOrMediumAllocation(allocator, resultingObjectSize, pas_small_segregated_object_kind,
count, verifyBeforeFreeing, expectedNumberOfCommittedPages,
expectedAllocateSlowPathCount);
}
#endif // SEGHEAP
void testLargeAllocation(const Allocator& allocator,
size_t resultingObjectSize,
size_t count,
size_t expectedNumberOfAllocatedPageBytes,
size_t expectedNumberOfBootstrapBytes)
{
static constexpr bool verbose = false;
uintptr_t lastObject = 0;
testSimpleAllocation(
allocator,
resultingObjectSize,
count,
true,
[&] (void* object) {
if (verbose) {
cout << "Allocated object at " << object << " at delta "
<< static_cast<intptr_t>(reinterpret_cast<uintptr_t>(object) - lastObject)
<< "\n";
}
lastObject = reinterpret_cast<uintptr_t>(object);
CHECK(isLarge(object));
},
[&] () {
CHECK_EQUAL(
allocator.heap()->large_heap.free_heap.num_mapped_bytes,
expectedNumberOfAllocatedPageBytes);
CHECK_LESS_EQUAL(pas_bootstrap_free_heap.num_mapped_bytes,
expectedNumberOfBootstrapBytes);
});
CHECK_EQUAL(allocator.heap()->large_heap.free_heap.num_mapped_bytes,
expectedNumberOfAllocatedPageBytes);
CHECK_LESS_EQUAL(pas_bootstrap_free_heap.num_mapped_bytes,
expectedNumberOfBootstrapBytes);
}
#if SEGHEAP
void testAllocationWithInterleavedFragmentation(const Allocator& allocator,
size_t resultingObjectSize,
size_t count,
bool verifyBeforeFreeing,
size_t expectedNumberOfCommittedPages,
size_t expectedNumberOfCommittedPagesAtEnd,
uint64_t expectedAllocateSlowPathCount)
{
static constexpr bool verbose = false;
set<void*> objects;
vector<void*> objectList;
#if TLC
pas_scavenger_suspend();
pas_large_sharing_pool_validate_each_splat = true;
#endif
for (size_t index = count; index--;) {
void* object = allocator.allocate();
CHECK(object);
CHECK(!objects.count(object));
objects.insert(object);
objectList.push_back(object);
}
verifyMinimumObjectDistance(objects, resultingObjectSize);
CHECK_EQUAL(numCommittedViews(allocator.heap()), expectedNumberOfCommittedPages);
CHECK_EQUAL(objects.size(), count);
if (verifyBeforeFreeing)
verifyObjectSet(objects, allocator.heap(), resultingObjectSize);
for (size_t index = 0; index < objectList.size(); index += 2) {
thingy_deallocate(objectList[index]);
objects.erase(objectList[index]);
}
CHECK_EQUAL(numCommittedViews(allocator.heap()), expectedNumberOfCommittedPages);
if (verifyBeforeFreeing)
verifyObjectSet(objects, allocator.heap(), resultingObjectSize);
if (verbose)
printStatusReport();
for (size_t index = count / 2; index--;) {
void* object = allocator.allocate();
CHECK(object);
CHECK(!objects.count(object));
objects.insert(object);
}
verifyMinimumObjectDistance(objects, resultingObjectSize);
if (verbose)
printStatusReport();
CHECK_EQUAL(numCommittedViews(allocator.heap()), expectedNumberOfCommittedPagesAtEnd);
if (verifyBeforeFreeing)
verifyObjectSet(objects, allocator.heap(), resultingObjectSize);
for (void* object : objects)
thingy_deallocate(object);
CHECK_EQUAL(numCommittedViews(allocator.heap()), expectedNumberOfCommittedPagesAtEnd);
verifyHeapEmpty(allocator.heap());
#if PAS_ENABLE_TESTING
CHECK_EQUAL(allocator.allocateSlowPathCount(), expectedAllocateSlowPathCount);
#endif // PAS_ENABLE_TESTING
CHECK_EQUAL(pas_scavenger_current_state, pas_scavenger_state_no_thread);
}
void testFreeListRefillSpans(unsigned prewarmObjectSize,
unsigned objectSize,
unsigned numberOfSpans,
unsigned firstSpanToFree,
pas_segregated_page_config_variant variant)
{
size_t numExtraCommittedPages = 0;
#if TLC
pas_scavenger_suspend();
pas_large_sharing_pool_validate_each_splat = true;
#endif
if (prewarmObjectSize) {
// Need this to skip the weird page at the start of the megapage.
thingy_try_allocate_primitive(prewarmObjectSize);
#if PAS_ENABLE_TESTING
thingy_allocator_counts.slow_paths = 0;
thingy_allocator_counts.slow_refills = 0;
#endif // PAS_ENABLE_TESTING
CHECK_EQUAL(numCommittedViews(&thingy_primitive_heap), 1);
numExtraCommittedPages = 1;
}
unsigned numberOfObjects = pas_segregated_page_number_of_objects(
objectSize,
*pas_heap_config_segregated_page_config_ptr_for_variant(
&thingy_heap_config,
variant),
pas_segregated_page_exclusive_role);
unsigned numberOfObjectsPerSpan = numberOfObjects / numberOfSpans;
CHECK(numberOfObjectsPerSpan >= 1);
unsigned numberOfObjectsInLastSpan = numberOfObjects - numberOfObjectsPerSpan * (numberOfSpans - 1);
vector<vector<void*>> objects;
unsigned actualNumberOfObjects = 0;
for (unsigned span = numberOfSpans; span--;) {
unsigned numberOfObjectsInThisSpan = span ? numberOfObjectsPerSpan : numberOfObjectsInLastSpan;
vector<void*> objectsInThisSpan;
for (unsigned objectIndex = numberOfObjectsInThisSpan; objectIndex--;) {
void* ptr = thingy_try_allocate_primitive(objectSize);
CHECK(ptr);
CHECK_EQUAL(pas_get_object_kind(ptr, THINGY_HEAP_CONFIG),
pas_object_kind_for_segregated_variant(variant));
objectsInThisSpan.push_back(ptr);
actualNumberOfObjects++;
}
objects.push_back(move(objectsInThisSpan));
}
#if PAS_ENABLE_TESTING
CHECK_EQUAL(thingy_allocator_counts.slow_paths, 1);
CHECK_EQUAL(thingy_allocator_counts.slow_refills, 1);
#endif // PAS_ENABLE_TESTING
CHECK_EQUAL(objects.size(), numberOfSpans);
CHECK_EQUAL(actualNumberOfObjects, numberOfObjects);
unsigned numberOfSpansFreed = 0;
unsigned numberOfObjectsFreed = 0;
set<void*> freedObject;
for (unsigned span = firstSpanToFree; span < objects.size(); span += 2) {
numberOfSpansFreed++;
for (void* object : objects[span]) {
thingy_deallocate(object);
freedObject.insert(object);
numberOfObjectsFreed++;
}
}
flushDeallocationLogAndStopAllocators();
for (unsigned objectIndex = numberOfObjectsFreed; objectIndex--;) {
void* ptr = thingy_try_allocate_primitive(objectSize);
CHECK(ptr);
CHECK(freedObject.count(ptr));
freedObject.erase(ptr);
}
#if PAS_ENABLE_TESTING
CHECK_EQUAL(thingy_allocator_counts.slow_paths, 1);
CHECK_EQUAL(thingy_allocator_counts.slow_refills, 2);
#endif // PAS_ENABLE_TESTING
CHECK_EQUAL(numCommittedViews(&thingy_primitive_heap), 1 + numExtraCommittedPages);
CHECK_EQUAL(pas_scavenger_current_state, pas_scavenger_state_no_thread);
}
#endif // SEGHEAP
#if TLC
void testInternalScavenge(unsigned firstObjectSize,
size_t resultingFirstObjectSize,
size_t firstCount,
bool verifyBeforeFreeing,
bool verifyBeforeReusing,
unsigned secondObjectSize,
size_t resultingSecondObjectSize,
size_t secondCount,
bool verifyAfterReusing,
size_t thirdCount,
bool verifyAfterEverything,
size_t expectedNumberOfCommittedPages,
size_t expectedNumberOfPageIndices,
size_t expectedNumberOfCommittedPagesAfterReusing,
size_t expectedNumberOfPageIndicesAfterReusing,
size_t expectedNumberOfCommittedPagesAtEnd,
size_t expectedNumberOfPageIndicesAtEnd,
bool checkHeapLock)
{
pas_scavenger_suspend();
pas_large_sharing_pool_validate_each_splat = true;
pas_physical_page_sharing_pool_balancing_enabled = true;
pas_physical_page_sharing_pool_balancing_enabled_for_utility = false;
pas_large_utility_free_heap_talks_to_large_sharing_pool = false;
set<void*> objects;
vector<void*> objectList;
pas_page_sharing_pool_verify(
&pas_physical_page_sharing_pool,
pas_lock_is_not_held);
for (size_t index = firstCount; index--;) {
pas_page_sharing_pool_verify(
&pas_physical_page_sharing_pool,
pas_lock_is_not_held);
void* object = thingy_try_allocate_primitive(firstObjectSize);
CHECK(object);
CHECK(!objects.count(object));
objects.insert(object);
objectList.push_back(object);
CHECK_EQUAL(sizeClassFor(object)->object_size, resultingFirstObjectSize);
}
if (verifyBeforeFreeing)
verifyObjectSet(objects, &thingy_primitive_heap, resultingFirstObjectSize);
pas_page_sharing_pool_verify(
&pas_physical_page_sharing_pool,
pas_lock_is_not_held);
for (void* object : objectList) {
thingy_deallocate(object);
objects.erase(object);
}
objectList.clear();
CHECK_EQUAL(numCommittedViews(&thingy_primitive_heap),
expectedNumberOfCommittedPages);
CHECK_EQUAL(numViews(&thingy_primitive_heap),
expectedNumberOfPageIndices);
if (verifyBeforeReusing)
verifyHeapEmpty(&thingy_primitive_heap);
pas_page_sharing_pool_verify(
&pas_physical_page_sharing_pool,
pas_lock_is_not_held);
// This test assumes that we will allocate and free enough objects of the first size that even
// if we don't verifyBeforeReusing (which shrinks the heap), we will end up having free pages
// in the first size class. That will have to trigger a delta if the use_epoch didn't do that
// already.
CHECK(pas_page_sharing_pool_has_delta(&pas_physical_page_sharing_pool));
for (size_t index = 0; index < secondCount; ++index) {
pas_page_sharing_pool_verify(
&pas_physical_page_sharing_pool,
pas_lock_is_not_held);
if (index == 1) {
// This test assumes that we will allocate and free enough objects of the first size
// that even if we don't verifyBeforeReusing (which shrinks the heap), we will end up
// reusing pages from the first size class.
CHECK(pas_page_sharing_pool_has_current_participant(&pas_physical_page_sharing_pool));
}
void* object = thingy_try_allocate_primitive(secondObjectSize);
CHECK(object);
CHECK(!objects.count(object));
objects.insert(object);
objectList.push_back(object);
CHECK_EQUAL(sizeClassFor(object)->object_size, resultingSecondObjectSize);
}
if (verifyAfterReusing)
verifyObjectSet(objects, &thingy_primitive_heap, resultingSecondObjectSize);
pas_page_sharing_pool_verify(
&pas_physical_page_sharing_pool,
pas_lock_is_not_held);
CHECK_EQUAL(numCommittedViews(&thingy_primitive_heap),
expectedNumberOfCommittedPagesAfterReusing);
CHECK_EQUAL(numViews(&thingy_primitive_heap),
expectedNumberOfPageIndicesAfterReusing);
if (checkHeapLock) {
for (void* object : objectList) {
thingy_deallocate(object);
objects.erase(object);
}
objectList.clear();
flushDeallocationLogAndStopAllocators();
}
for (size_t index = 0; index < thirdCount; ++index) {
pas_page_sharing_pool_verify(
&pas_physical_page_sharing_pool,
pas_lock_is_not_held);
void* object = thingy_try_allocate_primitive(firstObjectSize);
CHECK(object);
CHECK(!objects.count(object));
objects.insert(object);
CHECK_EQUAL(sizeClassFor(object)->object_size, resultingFirstObjectSize);
}
if (verifyAfterEverything) {
verifyObjectSet(
objects, &thingy_primitive_heap,
[&] (void* object, size_t size) {
CHECK(size == resultingFirstObjectSize || size == resultingSecondObjectSize);
});
}
pas_page_sharing_pool_verify(
&pas_physical_page_sharing_pool,
pas_lock_is_not_held);
CHECK_EQUAL(numCommittedViews(&thingy_primitive_heap),
expectedNumberOfCommittedPagesAtEnd);
CHECK_EQUAL(numViews(&thingy_primitive_heap),
expectedNumberOfPageIndicesAtEnd);
CHECK_EQUAL(pas_scavenger_current_state, pas_scavenger_state_no_thread);
}
void testInternalScavengeFromCorrectDirectory(size_t firstSize, size_t secondSize, size_t thirdSize)
{
pas_scavenger_suspend();
pas_large_sharing_pool_validate_each_splat = true;
pas_physical_page_sharing_pool_balancing_enabled = true;
pas_physical_page_sharing_pool_balancing_enabled_for_utility = false;
pas_large_utility_free_heap_talks_to_large_sharing_pool = false;
void* object1 = thingy_try_allocate_primitive(firstSize);
CHECK(object1);
pas_segregated_view view1 = pas_segregated_view_for_object(reinterpret_cast<uintptr_t>(object1),
&thingy_heap_config);
pas_segregated_size_directory* directory1 = pas_segregated_size_directory_for_object(
reinterpret_cast<uintptr_t>(object1), &thingy_heap_config);
CHECK_EQUAL(directory1->object_size, firstSize);
CHECK_EQUAL(pas_segregated_directory_size(&directory1->base), 1);
CHECK(!pas_segregated_directory_is_eligible(&directory1->base, 0));
CHECK(!pas_segregated_directory_is_empty(&directory1->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory1->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory1->base, 0),
view1);
flushDeallocationLogAndStopAllocators();
CHECK_EQUAL(pas_segregated_directory_size(&directory1->base), 1);
CHECK(pas_segregated_directory_is_eligible(&directory1->base, 0));
CHECK(!pas_segregated_directory_is_empty(&directory1->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory1->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory1->base, 0),
view1);
void* object2 = thingy_try_allocate_primitive(secondSize);
CHECK(object2);
pas_segregated_view view2 = pas_segregated_view_for_object(reinterpret_cast<uintptr_t>(object2),
&thingy_heap_config);
pas_segregated_size_directory* directory2 = pas_segregated_size_directory_for_object(
reinterpret_cast<uintptr_t>(object2), &thingy_heap_config);
CHECK_EQUAL(directory2->object_size, secondSize);
CHECK_EQUAL(pas_segregated_directory_size(&directory1->base), 1);
CHECK(pas_segregated_directory_is_eligible(&directory1->base, 0));
CHECK(!pas_segregated_directory_is_empty(&directory1->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory1->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory1->base, 0),
view1);
CHECK_EQUAL(pas_segregated_directory_size(&directory2->base), 1);
CHECK(!pas_segregated_directory_is_eligible(&directory2->base, 0));
CHECK(!pas_segregated_directory_is_empty(&directory2->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory2->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory2->base, 0),
view2);
flushDeallocationLogAndStopAllocators();
CHECK_EQUAL(pas_segregated_directory_size(&directory1->base), 1);
CHECK(pas_segregated_directory_is_eligible(&directory1->base, 0));
CHECK(!pas_segregated_directory_is_empty(&directory1->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory1->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory1->base, 0),
view1);
CHECK_EQUAL(pas_segregated_directory_size(&directory2->base), 1);
CHECK(pas_segregated_directory_is_eligible(&directory2->base, 0));
CHECK(!pas_segregated_directory_is_empty(&directory2->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory2->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory2->base, 0),
view2);
thingy_deallocate(object1);
thingy_deallocate(object2);
flushDeallocationLogAndStopAllocators();
CHECK_EQUAL(pas_segregated_directory_size(&directory1->base), 1);
CHECK(pas_segregated_directory_is_eligible(&directory1->base, 0));
CHECK(pas_segregated_directory_is_empty(&directory1->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory1->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory1->base, 0),
view1);
CHECK_EQUAL(pas_segregated_directory_size(&directory2->base), 1);
CHECK(pas_segregated_directory_is_eligible(&directory2->base, 0));
CHECK(pas_segregated_directory_is_empty(&directory2->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory2->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory2->base, 0),
view2);
void* object3 = thingy_try_allocate_primitive(thirdSize);
CHECK(object3);
pas_segregated_view view3 = pas_segregated_view_for_object(reinterpret_cast<uintptr_t>(object3),
&thingy_heap_config);
pas_segregated_size_directory* directory3 = pas_segregated_size_directory_for_object(
reinterpret_cast<uintptr_t>(object3), &thingy_heap_config);
CHECK_EQUAL(directory3->object_size, thirdSize);
CHECK_EQUAL(pas_segregated_directory_size(&directory1->base), 1);
CHECK(pas_segregated_directory_is_eligible(&directory1->base, 0));
CHECK(!pas_segregated_directory_is_empty(&directory1->base, 0));
CHECK(!pas_segregated_directory_is_committed(&directory1->base, 0));
CHECK_EQUAL(pas_segregated_directory_size(&directory2->base), 1);
CHECK(pas_segregated_directory_is_eligible(&directory2->base, 0));
CHECK(pas_segregated_directory_is_empty(&directory2->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory2->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory2->base, 0),
view2);
CHECK_EQUAL(pas_segregated_directory_size(&directory3->base), 1);
CHECK(!pas_segregated_directory_is_eligible(&directory3->base, 0));
CHECK(!pas_segregated_directory_is_empty(&directory3->base, 0));
CHECK(pas_segregated_directory_is_committed(&directory3->base, 0));
CHECK_EQUAL(
pas_segregated_directory_get(&directory3->base, 0),
view3);
CHECK_EQUAL(pas_scavenger_current_state, pas_scavenger_state_no_thread);
}
#endif // TLC
#if SEGHEAP
struct SizeClassProgram {
SizeClassProgram(const string& name,
const Allocator& allocator,
size_t resultingSize,
pas_object_kind objectKind,
size_t numAllocations,
size_t slowPathCount)
: name(name)
, allocator(&allocator)
, resultingSize(resultingSize)
, objectKind(objectKind)
, numAllocations(numAllocations)
, slowPathCount(slowPathCount)
{
}
string name;
const Allocator* allocator;
size_t resultingSize;
pas_object_kind objectKind;
size_t numAllocations;
size_t slowPathCount;
};
#define SIZE_CLASS_PROGRAM(allocator, resultingSize, objectKind, numAllocations, slowPathCount) \
SizeClassProgram(#allocator ", " #resultingSize ", " #objectKind ", " #numAllocations ", " #slowPathCount, \
allocator, resultingSize, objectKind, numAllocations, slowPathCount)
void testSizeClassCreationImpl(const vector<SizeClassProgram>& programs)
{
pas_scavenger_suspend(); // Because otherwise the CounterScope might see slow paths due to the scavenger collecting TLAs.
#if TLC
pas_large_sharing_pool_validate_each_splat = true;
#endif
map<size_t, pas_segregated_size_directory*> sizeMap;
for (const SizeClassProgram& program : programs) {
cout << " Program: " << program.name << endl;
pas_segregated_size_directory* resultingSizeClass = nullptr;
{
#if PAS_ENABLE_TESTING
CounterScope counterScope([&] { return program.allocator->allocateSlowPathCount(); },
program.slowPathCount);
#endif // PAS_ENABLE_TESTING
for (size_t i = 0; i < program.numAllocations; ++i) {
void* allocation = program.allocator->allocate();
CHECK(allocation);
CHECK_EQUAL(thingy_get_allocation_size(allocation), program.resultingSize);
CHECK_EQUAL(pas_get_object_kind(allocation, THINGY_HEAP_CONFIG),
program.objectKind);
if (!resultingSizeClass)
resultingSizeClass = sizeClassFor(allocation);
else
CHECK_EQUAL(sizeClassFor(allocation), resultingSizeClass);
}
}
if (!sizeMap.count(program.resultingSize))
sizeMap[program.resultingSize] = resultingSizeClass;
else
CHECK_EQUAL(resultingSizeClass, sizeMap[program.resultingSize]);
}
}
template<typename... Arguments>
void testSizeClassCreation(Arguments... programs)
{
testSizeClassCreationImpl({ programs... });
}
void testSpuriousEligibility()
{
static constexpr bool verbose = false;
#if TLC
pas_scavenger_suspend();
pas_large_sharing_pool_validate_each_splat = true;
#endif
unsigned objectSize = 16;
unsigned numberOfObjects =
pas_segregated_page_number_of_objects(objectSize,
THINGY_HEAP_CONFIG.small_segregated_config,
pas_segregated_page_exclusive_role);
unsigned numberOfObjectsInFirstSpan = numberOfObjects / 2;
unsigned numberOfObjectsInSecondSpan = numberOfObjects - numberOfObjectsInFirstSpan;
if (verbose)
cout << "Allocating first span of objects.\n";
vector<void*> firstObjects;
for (unsigned i = numberOfObjectsInFirstSpan; i--;)
firstObjects.push_back(thingy_try_allocate_primitive(objectSize));
if (verbose)
cout << "Allocating second span of objects.\n";
vector<void*> secondObjects;
for (unsigned i = numberOfObjectsInSecondSpan; i--;)
secondObjects.push_back(thingy_try_allocate_primitive(objectSize));
if (verbose)
cout << "Shrinking.\n";
flushDeallocationLogAndStopAllocators();
if (verbose)
cout << "Deallocating first span of objects.\n";
for (void* object : firstObjects)
thingy_deallocate(object);
if (verbose)
cout << "Shrinking.\n";
flushDeallocationLogAndStopAllocators();
if (verbose)
cout << "Reallocating first span of objects.\n";
for (void* object : firstObjects) {
void* reallocatedObject = thingy_try_allocate_primitive(objectSize);
CHECK_EQUAL(reallocatedObject, object);
}
if (verbose)
cout << "Deallocating second span of objects.\n";
for (void* object : secondObjects)
thingy_deallocate(object);
if (verbose)
cout << "Flushing deallocation log.\n";
flushDeallocationLog();
if (verbose)
cout << "Reallocating second span of objects.\n";
for (void* object : secondObjects) {
void* reallocatedObject = thingy_try_allocate_primitive(objectSize);
CHECK_EQUAL(reallocatedObject, object);
}
if (verbose)
cout << "Shrinking.\n";
flushDeallocationLogAndStopAllocators();
thingy_try_allocate_primitive(objectSize);
flushDeallocationLogAndStopAllocators();
for (void* object : firstObjects)
thingy_deallocate(object);
for (void* object : secondObjects)
thingy_deallocate(object);
flushDeallocationLog();
flushDeallocationLogAndStopAllocators();
for (void* object : firstObjects) {
void* reallocatedObject = thingy_try_allocate_primitive(objectSize);
CHECK_EQUAL(reallocatedObject, object);
}
for (void* object : secondObjects) {
void* reallocatedObject = thingy_try_allocate_primitive(objectSize);
CHECK_EQUAL(reallocatedObject, object);
}
CHECK_EQUAL(pas_scavenger_current_state, pas_scavenger_state_no_thread);
}
void testBasicSizeClassNotSet()
{
(thread([&] () { thingy_try_allocate_primitive(2); })).join();
(thread([&] () { thingy_try_allocate_primitive(1); })).join();
}
void testSmallDoubleFree()
{
void* ptr = thingy_try_allocate_primitive(1);
CHECK(ptr);
thingy_deallocate(ptr);
DeallocationShouldFail deallocationShouldFail;
thingy_deallocate(ptr);
flushDeallocationLogAndStopAllocators();
}
void testSmallFreeInner()
{
void* ptr = thingy_try_allocate_primitive(32);
CHECK(ptr);
DeallocationShouldFail deallocationShouldFail;
thingy_deallocate(reinterpret_cast<char*>(ptr) + 16);
}
void testSmallFreeNextWithShrink()
{
void* ptr = thingy_try_allocate_primitive(16);
CHECK(ptr);
flushDeallocationLogAndStopAllocators();
DeallocationShouldFail deallocationShouldFail;
thingy_deallocate(reinterpret_cast<char*>(ptr) + 16);
}
void testSmallFreeNextWithoutShrink()
{
void* ptr = thingy_try_allocate_primitive(16);
CHECK(ptr);
thingy_deallocate(ptr);
#if !TLC
DeallocationShouldFail deallocationShouldFail;
#endif
thingy_deallocate(reinterpret_cast<char*>(ptr) + 16);
void* ptr2 = thingy_try_allocate_primitive(16);
CHECK(ptr2 == reinterpret_cast<char*>(ptr) + 16);
verifyHeapEmpty(&thingy_primitive_heap);
}
void testSmallFreeNextBeforeShrink()
{
void* ptr = thingy_try_allocate_primitive(16);
CHECK(ptr);
#if !TLC
DeallocationShouldFail deallocationShouldFail;
#endif
thingy_deallocate(reinterpret_cast<char*>(ptr) + 16);
#if TLC
DeallocationShouldFail deallocationShouldFail;
#endif
flushDeallocationLogAndStopAllocators();
}
#endif // SEGHEAP
class AllocationProgram {
public:
enum Kind { Allocate, Free };
static AllocationProgram allocate(const string& key, size_t count, size_t alignment)
{
AllocationProgram result;
result.m_kind = Allocate;
result.m_key = key;
result.m_count = count;
result.m_alignment = alignment;
return result;
}
static AllocationProgram free(const string& key)
{
AllocationProgram result;
result.m_kind = Free;
result.m_key = key;
return result;
}
Kind kind() const { return m_kind; }
const string& key() const { return m_key; }
bool isAllocate() const { return m_kind == Kind::Allocate; }
size_t count() const
{
PAS_ASSERT(isAllocate());
return m_count;
}
size_t alignment() const
{
PAS_ASSERT(isAllocate());
return m_alignment;
}
bool isFree() const { return m_kind == Kind::Free; }
private:
Kind m_kind { Allocate };
string m_key;
size_t m_count { 0 };
size_t m_alignment { 0 };
};
class ComplexAllocator {
public:
virtual ~ComplexAllocator() = default;
virtual void* allocate(size_t count, size_t alignment) const = 0;
virtual pas_heap* heap() const = 0;
};
class PrimitiveComplexAllocator : public ComplexAllocator {
public:
void* allocate(size_t count, size_t alignment) const override
{
return thingy_try_allocate_primitive_with_alignment(count, alignment);
}
pas_heap* heap() const override
{
return &thingy_primitive_heap;
}
};
class IsolatedComplexAllocator : public ComplexAllocator {
public:
IsolatedComplexAllocator(pas_heap_ref* heapRef)
: m_heapRef(heapRef)
{
}
IsolatedComplexAllocator(size_t size, size_t alignment)
: IsolatedComplexAllocator(createIsolatedHeapRef(size, alignment))
{
}
void* allocate(size_t count, size_t alignment) const override
{
return thingy_try_allocate_array(m_heapRef, count, alignment);
}
pas_heap* heap() const override
{
return thingy_heap_ref_get_heap(m_heapRef);
}
private:
pas_heap_ref* m_heapRef;
};
class IsolatedUnitComplexAllocator : public ComplexAllocator {
public:
IsolatedUnitComplexAllocator(pas_heap_ref* heapRef)
: m_heapRef(heapRef)
{
}
IsolatedUnitComplexAllocator(size_t size, size_t alignment)
: IsolatedUnitComplexAllocator(createIsolatedHeapRef(size, alignment))
{
}
void* allocate(size_t count, size_t alignment) const override
{
if (count == 1 && alignment == 1)
return thingy_try_allocate(m_heapRef);
return thingy_try_allocate_array(m_heapRef, count, alignment);
}
pas_heap* heap() const override
{
return thingy_heap_ref_get_heap(m_heapRef);
}
private:
pas_heap_ref* m_heapRef;
};
void checkObjectDistances(const map<void*, size_t>& objectByPtr)
{
uintptr_t lastEnd = 0;
for (auto& pair : objectByPtr) {
uintptr_t begin = reinterpret_cast<uintptr_t>(pair.first);
uintptr_t end = begin + pair.second;
CHECK(begin >= lastEnd);
lastEnd = end;
}
}
void checkObjectBeginnings(pas_simple_type type, const set<uintptr_t>& objectBeginnings)
{
// Test that the objects don't overlap, since that would be a type system violation.
uintptr_t lastBegin = 0;
for (uintptr_t begin : objectBeginnings) {
CHECK(begin >= lastBegin);
CHECK(begin - lastBegin >= pas_simple_type_size(type));
lastBegin = begin;
}
}
void addObjectAllocation(map<void*, size_t>& objectByPtr,
set<uintptr_t>* objectBeginnings,
pas_simple_type type,
void* ptr,
size_t count)
{
CHECK(ptr);
CHECK(pas_is_aligned(reinterpret_cast<uintptr_t>(ptr), pas_simple_type_alignment(type)));
CHECK(!objectByPtr.count(ptr));
objectByPtr[ptr] = count * pas_simple_type_size(type);
if (objectBeginnings) {
for (uintptr_t begin = reinterpret_cast<uintptr_t>(ptr);
begin < reinterpret_cast<uintptr_t>(ptr) + count * pas_simple_type_size(type);
begin += pas_simple_type_size(type))
objectBeginnings->insert(begin);
}
}
struct ExpectedBytes {
enum Kind { Exact, UpperBound };
static ExpectedBytes exact(size_t bytes)
{
ExpectedBytes result;
result.bytes = bytes;
result.kind = Exact;
return result;
}
static ExpectedBytes upperBound(size_t bytes)
{
ExpectedBytes result;
result.bytes = bytes;
result.kind = UpperBound;
return result;
}
void check(size_t actualBytes)
{
switch (kind) {
case Exact:
CHECK_EQUAL(actualBytes, bytes);
return;
case UpperBound:
CHECK_LESS_EQUAL(actualBytes, bytes);
return;
}
PAS_ASSERT(!"unexpected kind");
}
size_t bytes { 0 };
Kind kind { Exact };
};
void testComplexLargeAllocationImpl(const ComplexAllocator& allocator,
ExpectedBytes expectedNumMappedBytes,
const vector<AllocationProgram>& programs)
{
static constexpr bool verbose = false;
#if TLC
pas_large_sharing_pool_validate_each_splat = true;
#endif
map<void*, size_t> objectByPtr;
map<string, void*> objectByKey;
set<uintptr_t> objectBeginnings;
for (const AllocationProgram& program : programs) {
switch (program.kind()) {
case AllocationProgram::Kind::Allocate: {
cout << " Program: allocate(" << program.key() << ", " << program.count() << ", "
<< program.alignment() << ")" << endl;
void* ptr = allocator.allocate(program.count(), program.alignment());
if (verbose) {
cout << "Allocated ptr = " << ptr << " with kind "
<< pas_get_object_kind(ptr, THINGY_HEAP_CONFIG) << endl;
}
CHECK(pas_is_aligned(reinterpret_cast<uintptr_t>(ptr), program.alignment()));
CHECK(!objectByKey.count(program.key()));
objectByKey[program.key()] = ptr;
addObjectAllocation(objectByPtr,
&objectBeginnings,
reinterpret_cast<pas_simple_type>(allocator.heap()->type),
ptr,
program.count());
break;
}
case AllocationProgram::Kind::Free: {
cout << " Program: free(" << program.key() << ")" << endl;
CHECK(objectByKey.count(program.key()));
void* ptr = objectByKey[program.key()];
CHECK(objectByPtr.count(ptr));
thingy_deallocate(ptr);
objectByPtr.erase(ptr);
break;
} }
checkObjectDistances(objectByPtr);
verifyObjectSet(objectByPtr, allocator.heap());
}
checkObjectBeginnings(reinterpret_cast<pas_simple_type>(allocator.heap()->type),
objectBeginnings);
#if SEGHEAP
CHECK_EQUAL(numCommittedViews(allocator.heap()), 0);
#endif
expectedNumMappedBytes.check(allocator.heap()->large_heap.free_heap.num_mapped_bytes);
}
template<typename... Arguments>
void testComplexLargeAllocation(const ComplexAllocator& allocator,
ExpectedBytes expectedNumMappedBytes,
Arguments... arguments)
{
testComplexLargeAllocationImpl(allocator, expectedNumMappedBytes, { arguments... });
}
void testAllocationCountProgression(const ComplexAllocator& allocator,
size_t beginCount,
size_t endCount,
size_t countStep,
size_t alignment,
size_t numPerSize,
bool doObjectBeginnings)
{
#if TLC
pas_scavenger_suspend();
pas_large_sharing_pool_validate_each_splat = true;
#endif
map<void*, size_t> objectByPtr;
set<uintptr_t> objectBeginnings;
for (size_t count = beginCount; count < endCount; count += countStep) {
for (size_t countPerSize = numPerSize; countPerSize--;) {
void* ptr = allocator.allocate(count, alignment);
addObjectAllocation(objectByPtr,
doObjectBeginnings ? &objectBeginnings : nullptr,
reinterpret_cast<pas_simple_type>(allocator.heap()->type),
ptr,
count);
}
}
checkObjectDistances(objectByPtr);
if (doObjectBeginnings) {
checkObjectBeginnings(
reinterpret_cast<pas_simple_type>(allocator.heap()->type), objectBeginnings);
}
flushDeallocationLogAndStopAllocators();
verifyObjectSet(objectByPtr, allocator.heap());
for (auto& pair : objectByPtr)
thingy_deallocate(pair.first);
verifyHeapEmpty(allocator.heap());
}
void testAllocationChaos(unsigned numThreads, unsigned numIsolatedHeaps,
unsigned numInitialAllocations, unsigned numActions, unsigned maxTotalSize,
bool validateEachSplat)
{
#if TLC
if (validateEachSplat)
pas_large_sharing_pool_validate_each_splat = true;
#endif
static constexpr bool verbose = false;
mutex lock;
struct Object {
Object() = default;
Object(void* ptr, size_t size)
: ptr(ptr)
, size(size)
{
}
void* ptr { nullptr };
size_t size { 0 };
};
vector<Object> objects;
vector<thread> threads;
unsigned totalSize;
vector<pas_heap_ref*> isolatedHeaps;
for (unsigned isolatedHeapIndex = numIsolatedHeaps; isolatedHeapIndex--;)
isolatedHeaps.push_back(createIsolatedHeapRef(deterministicRandomNumber(100), 1));
auto addObject =
[&] (void* ptr, unsigned size) {
if (verbose)
cout << "Allocated " << ptr << " with size = " << size << "\n";
CHECK(ptr);
for (unsigned i = size; i--;) {
char value = reinterpret_cast<char*>(ptr)[i];
CHECK(value == 0 || value == 0x42);
}
memset(ptr, 0x42, size);
{
lock_guard<mutex> locker(lock);
objects.push_back(Object(ptr, size));
totalSize += size;
}
};
auto allocateSomething = [&] () {
unsigned heapIndex = deterministicRandomNumber(static_cast<unsigned>(isolatedHeaps.size() + 1));
unsigned size = deterministicRandomNumber(5000);
if (heapIndex == isolatedHeaps.size()) {
void* ptr = thingy_try_allocate_primitive(size);
addObject(ptr, size);
return;
}
pas_heap_ref* heap = isolatedHeaps[heapIndex];
unsigned count = size / pas_simple_type_size(reinterpret_cast<pas_simple_type>(heap->type));
size = static_cast<unsigned>(
count * pas_simple_type_size(reinterpret_cast<pas_simple_type>(heap->type)));
void* ptr;
if (count <= 1)
ptr = thingy_try_allocate(heap);
else
ptr = thingy_try_allocate_array(heap, count, 1);
addObject(ptr, size);
};
auto freeSomething = [&] () {
Object object;
{
lock_guard<mutex> locker(lock);
if (objects.empty())
return;
unsigned index = deterministicRandomNumber(static_cast<unsigned>(objects.size()));
object = objects[index];
objects[index] = objects.back();
objects.pop_back();
totalSize -= object.size;
}
if (verbose)
cout << "Deallocating " << object.ptr << "\n";
thingy_deallocate(object.ptr);
};
for (unsigned allocationIndex = numInitialAllocations; allocationIndex--;)
allocateSomething();
unsigned numThreadsStopped = 0;
auto threadFunc = [&] () {
if (verbose)
cout << " Thread " << (void*)pthread_self() << " starting.\n";
for (unsigned actionIndex = numActions; actionIndex--;) {
if (totalSize >= maxTotalSize) {
freeSomething();
continue;
}
switch (deterministicRandomNumber(2)) {
case 0:
allocateSomething();
break;
case 1:
freeSomething();
break;
default:
PAS_ASSERT(!"bad random number");
}
}
if (verbose)
cout << " Thread " << (void*)pthread_self() << " stopping.\n";
{
lock_guard<mutex> locker(lock);
numThreadsStopped++;
}
};
{
lock_guard<mutex> locker(lock);
for (unsigned threadIndex = numThreads; threadIndex--;)
threads.push_back(thread(threadFunc));
}
for (thread& thread : threads)
thread.join();
CHECK_EQUAL(numThreadsStopped, threads.size());
vector<pas_heap*> heaps;
heaps.push_back(&thingy_primitive_heap);
for (pas_heap_ref* heapRef : isolatedHeaps)
heaps.push_back(thingy_heap_ref_get_heap(heapRef));
set<void*> expectedObjects;
for (Object object : objects)
expectedObjects.insert(object.ptr);
verifyObjectSet(expectedObjects, heaps, [] (void*, size_t) { });
pas_heap_lock_lock();
pas_large_sharing_pool_validate();
pas_heap_lock_unlock();
}
void testUtilityAllocationChaos(unsigned numThreads,
unsigned numInitialAllocations, unsigned numActions,
unsigned maxTotalSize)
{
mutex lock;
struct Object {
Object() = default;
Object(void* ptr, size_t size)
: ptr(ptr)
, size(size)
{
}
void* ptr { nullptr };
size_t size { 0 };
};
vector<Object> objects;
vector<thread> threads;
unsigned totalSize;
auto addObject = [&] (void* ptr, unsigned size) {
CHECK(ptr);
memset(ptr, 0x42, size);
{
lock_guard<mutex> locker(lock);
objects.push_back(Object(ptr, size));
totalSize += size;
}
};
auto allocateSomething = [&] () {
unsigned size = deterministicRandomNumber(500);
pas_heap_lock_lock();
void* ptr = pas_utility_heap_allocate(size, "test");
pas_heap_lock_unlock();
addObject(ptr, size);
};
auto freeSomething = [&] () {
Object object;
{
lock_guard<mutex> locker(lock);
if (objects.empty())
return;
unsigned index = deterministicRandomNumber(static_cast<unsigned>(objects.size()));
object = objects[index];
objects[index] = objects.back();
objects.pop_back();
totalSize -= object.size;
}
pas_heap_lock_lock();
pas_utility_heap_deallocate(object.ptr);
pas_heap_lock_unlock();
};
for (unsigned allocationIndex = numInitialAllocations; allocationIndex--;)
allocateSomething();
unsigned numThreadsStopped = 0;
auto threadFunc = [&] () {
for (unsigned actionIndex = numActions; actionIndex--;) {
if (totalSize >= maxTotalSize) {
freeSomething();
continue;
}
switch (deterministicRandomNumber(2)) {
case 0:
allocateSomething();
break;
case 1:
freeSomething();
break;
default:
PAS_ASSERT(!"bad random number");
}
}
{
lock_guard<mutex> locker(lock);
numThreadsStopped++;
}
};
{
lock_guard<mutex> locker(lock);
for (unsigned threadIndex = numThreads; threadIndex--;)
threads.push_back(thread(threadFunc));
}
for (thread& thread : threads)
thread.join();
CHECK_EQUAL(numThreadsStopped, threads.size());
pas_utility_heap_for_all_allocators(pas_allocator_scavenge_force_stop_action,
pas_lock_is_not_held);
set<void*> expectedObjects;
for (Object object : objects)
expectedObjects.insert(object.ptr);
verifyUtilityObjectSet(expectedObjects, [] (void*, size_t) { });
#if TLC
pas_heap_lock_lock();
pas_large_sharing_pool_validate();
pas_heap_lock_unlock();
#endif
}
void testCombinedAllocationChaos(unsigned numThreads, unsigned numIsolatedHeaps,
unsigned numInitialAllocations, unsigned numActions,
unsigned maxTotalSize,
bool calloc)
{
mutex lock;
struct Object {
Object() = default;
Object(void* ptr, size_t size, bool isUtility)
: ptr(ptr)
, size(size)
, isUtility(isUtility)
{
}
void* ptr { nullptr };
size_t size { 0 };
bool isUtility { false };
};
vector<Object> objects;
vector<thread> threads;
unsigned totalSize;
vector<pas_heap_ref*> isolatedHeaps;
for (unsigned isolatedHeapIndex = numIsolatedHeaps; isolatedHeapIndex--;)
isolatedHeaps.push_back(createIsolatedHeapRef(deterministicRandomNumber(100), 1));
auto addObject = [&] (void* ptr, unsigned size, bool isUtility) {
CHECK(ptr);
if (!isUtility) {
for (unsigned i = size; i--;) {
char value = reinterpret_cast<char*>(ptr)[i];
if (calloc)
CHECK(value == 0);
else
CHECK(value == 0 || value == 0x42);
}
}
memset(ptr, 0x42, size);
{
lock_guard<mutex> locker(lock);
objects.push_back(Object(ptr, size, isUtility));
totalSize += size;
}
};
auto allocateSomething = [&] () {
unsigned heapIndex = deterministicRandomNumber(static_cast<unsigned>(isolatedHeaps.size() + 2));
unsigned size = deterministicRandomNumber(5000);
if (heapIndex == isolatedHeaps.size() + 1) {
void* ptr;
size %= 500;
pas_heap_lock_lock();
ptr = pas_utility_heap_try_allocate(size, "test");
pas_heap_lock_unlock();
addObject(ptr, size, true);
return;
}
if (heapIndex == isolatedHeaps.size()) {
void* ptr;
if (calloc)
ptr = thingy_try_allocate_primitive_zeroed(size);
else
ptr = thingy_try_allocate_primitive(size);
addObject(ptr, size, false);
return;
}
pas_heap_ref* heap = isolatedHeaps[heapIndex];
unsigned count = size / pas_simple_type_size(reinterpret_cast<pas_simple_type>(heap->type));
size = static_cast<unsigned>(
count * pas_simple_type_size(reinterpret_cast<pas_simple_type>(heap->type)));
void* ptr;
if (count <= 1) {
if (calloc)
ptr = thingy_try_allocate_zeroed(heap);
else
ptr = thingy_try_allocate(heap);
} else {
if (calloc)
ptr = thingy_try_allocate_zeroed_array(heap, count, 1);
else
ptr = thingy_try_allocate_array(heap, count, 1);
}
addObject(ptr, size, false);
};
auto freeSomething = [&] () {
Object object;
{
lock_guard<mutex> locker(lock);
if (objects.empty())
return;
unsigned index = deterministicRandomNumber(static_cast<unsigned>(objects.size()));
object = objects[index];
objects[index] = objects.back();
objects.pop_back();
totalSize -= object.size;
}
if (object.isUtility) {
pas_heap_lock_lock();
pas_utility_heap_deallocate(object.ptr);
pas_heap_lock_unlock();
} else
thingy_deallocate(object.ptr);
};
for (unsigned allocationIndex = numInitialAllocations; allocationIndex--;)
allocateSomething();
unsigned numThreadsStopped = 0;
auto threadFunc = [&] () {
for (unsigned actionIndex = numActions; actionIndex--;) {
if (totalSize >= maxTotalSize) {
freeSomething();
continue;
}
switch (deterministicRandomNumber(2)) {
case 0:
allocateSomething();
break;
case 1:
freeSomething();
break;
default:
PAS_ASSERT(!"bad random number");
}
}
{
lock_guard<mutex> locker(lock);
numThreadsStopped++;
}
};
{
lock_guard<mutex> locker(lock);
for (unsigned threadIndex = numThreads; threadIndex--;)
threads.push_back(thread(threadFunc));
}
for (thread& thread : threads)
thread.join();
CHECK_EQUAL(numThreadsStopped, threads.size());
vector<pas_heap*> heaps;
heaps.push_back(&thingy_primitive_heap);
for (pas_heap_ref* heapRef : isolatedHeaps)
heaps.push_back(thingy_heap_ref_get_heap(heapRef));
set<void*> expectedObjects;
set<void*> expectedUtilityObjects;
for (Object object : objects) {
if (object.isUtility)
expectedUtilityObjects.insert(object.ptr);
else
expectedObjects.insert(object.ptr);
}
verifyObjectSet(expectedObjects, heaps, [] (void*, size_t) { });
verifyUtilityObjectSet(expectedUtilityObjects, [] (void*, size_t) { });
#if TLC
pas_heap_lock_lock();
pas_large_sharing_pool_validate();
pas_heap_lock_unlock();
#endif
}
void testLargeDoubleFree()
{
void* ptr = thingy_try_allocate_primitive(10000);
CHECK(ptr);
thingy_deallocate(ptr);
DeallocationShouldFail deallocationShouldFail;
thingy_deallocate(ptr);
}
void testLargeOffsetFree()
{
void* ptr = thingy_try_allocate_primitive(10000);
CHECK(ptr);
DeallocationShouldFail deallocationShouldFail;
thingy_deallocate(reinterpret_cast<char*>(ptr) + 1);
}
void addDeallocationTests()
{
ADD_TEST(testDeallocateNull());
ADD_TEST(testDeallocateMalloc());
ADD_TEST(testDeallocateStack());
}
void testReallocatePrimitive(size_t originalSize, size_t expectedOriginalSize,
size_t newSize, size_t expectedNewSize,
size_t count)
{
pas_scavenger_suspend();
for (size_t i = count; i--;) {
void* originalObject = thingy_try_allocate_primitive(originalSize);
CHECK(originalObject);
CHECK_EQUAL(thingy_get_allocation_size(originalObject), expectedOriginalSize);
void* newObject = thingy_try_reallocate_primitive(originalObject, newSize);
CHECK(newObject);
CHECK_EQUAL(thingy_get_allocation_size(newObject), expectedNewSize);
thingy_deallocate(newObject);
}
verifyHeapEmpty(&thingy_primitive_heap);
}
void testReallocateArray(size_t typeSize, size_t typeAlignment,
size_t originalCount, size_t expectedOriginalSize,
size_t newCount, size_t expectedNewSize,
size_t count)
{
pas_scavenger_suspend();
pas_heap_ref* heapRef = createIsolatedHeapRef(typeSize, typeAlignment);
for (size_t i = count; i--;) {
void* originalObject = thingy_try_allocate_array(heapRef, originalCount, 1);
CHECK(originalObject);
CHECK_EQUAL(thingy_get_allocation_size(originalObject), expectedOriginalSize);
void* newObject = thingy_try_reallocate_array(originalObject, heapRef, newCount);
CHECK(newObject);
CHECK_EQUAL(thingy_get_allocation_size(newObject), expectedNewSize);
thingy_deallocate(newObject);
}
verifyHeapEmpty(thingy_heap_ref_get_heap(heapRef));
}
#if SEGHEAP
void addSmallHeapTests()
{
DisableBitfit disableBitfit;
ADD_TEST(testSmallAllocation(PrimitiveAllocator(0), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(0), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(8), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(8), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(16), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(16), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(24), 24, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(24), 24, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(32), 32, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(32), 32, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(40), 40, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(40), 40, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(16), 16, 1000, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(16), 16, 1000, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(16), 16, 2000, false, 2, 3));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(16), 16, 2000, true, 2, 3));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(0), 8, 20000, false, 10, 11));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(0), 8, 20000, true, 10, 11));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(16), 16, 20000, false, 20, 21));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(16), 16, 20000, true, 20, 21));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(100), 104, 20000, false, 129 + TLC, 130 + TLC));
ADD_TEST(testSmallAllocation(PrimitiveAllocator(100), 104, 20000, true, 129 + TLC, 130 + TLC));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(0), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(0), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(8), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(8), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(16), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(16), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(24), 24, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(24), 24, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(16), 16, 1000, false, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(16), 16, 1000, true, 1, 2));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(16), 16, 2000, false, 2, 3));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(16), 16, 2000, true, 2, 3));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(0), 8, 20000, false, 10, 11));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(0), 8, 20000, true, 10, 11));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(16), 16, 20000, false, 20, 21));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(16), 16, 20000, true, 20, 21));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(100), 104, 20000, false, 129 + TLC, 130 + TLC));
ADD_TEST(testSmallAllocation(PrimitiveReallocAllocator(100), 104, 20000, true, 129 + TLC, 130 + TLC));
ADD_TEST(testAllocationWithInterleavedFragmentation(PrimitiveAllocator(1), 8, 20000, false, 10, 10, 21));
ADD_TEST(testAllocationWithInterleavedFragmentation(PrimitiveAllocator(1), 8, 20000, true, 10, 10, 21));
ADD_TEST(testAllocationWithInterleavedFragmentation(PrimitiveAllocator(16), 16, 20000, false, 20, 20, 41));
ADD_TEST(testAllocationWithInterleavedFragmentation(PrimitiveAllocator(16), 16, 20000, true, 20, 20, 41));
ADD_TEST(testAllocationWithInterleavedFragmentation(PrimitiveAllocator(100), 104, 20000, false, 129 + TLC, 130, 261));
ADD_TEST(testAllocationWithInterleavedFragmentation(PrimitiveAllocator(100), 104, 20000, true, 129 + TLC, 129 + TLC, TLC ? 261 : 257));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(0, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(0, 16), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(0, 32), 32, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(1, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(1, 16), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(1, 32), 32, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(16, 1), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(7, 16), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(16, 16), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(16, 32), 32, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(24, 32), 32, 1, true, 1, 2));
if (hasScope("only-small")) {
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(24, 1024), 1024, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(AlignedPrimitiveAllocator(666, 1024), 1024, 1, true, 1, 2));
}
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 8), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 8), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 8), 8, 10000, false, 5, 6));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 8), 8, 20000, false, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 8), 8, 20000, true, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 1), 8, 20000, false, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(8, 1), 8, 20000, true, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(1, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(1, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(1, 1), 8, 20000, false, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(1, 1), 8, 20000, true, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(5, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(5, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(5, 1), 8, 20000, false, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(5, 1), 8, 20000, true, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(2, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(2, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(3, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(3, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(4, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(4, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(6, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(6, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(7, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(7, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(9, 1), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(9, 1), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(10, 1), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(10, 1), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(11, 1), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(11, 1), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(12, 1), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(12, 1), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(13, 1), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(13, 1), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(14, 1), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(14, 1), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(15, 1), 16, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(15, 1), 16, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(24, 8), 24, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(24, 8), 24, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(24, 8), 24, 20000, false, 30, 31));
ADD_TEST(testSmallAllocation(IsolatedHeapAllocator(24, 8), 24, 20000, true, 30, 31));
ADD_TEST(testAllocationWithInterleavedFragmentation(IsolatedHeapAllocator(24, 8), 24, 20000, false, 30, 30, 61));
ADD_TEST(testAllocationWithInterleavedFragmentation(IsolatedHeapAllocator(24, 8), 24, 20000, true, 30, 30, 61));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(8, 8, 1, 1), 8, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(8, 8, 1, 1), 8, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(8, 8, 1, 1), 8, 20000, false, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(8, 8, 1, 1), 8, 20000, true, 10, 11));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(8, 8, 5, 1), 40, 1, false, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(8, 8, 5, 1), 40, 1, true, 1, 2));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(8, 8, 5, 1), 40, 20000, false, 50, 51));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(8, 8, 5, 1), 40, 20000, true, 50, 51));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(24, 8, 1, 32), 32, 20000, false, 40, 41));
ADD_TEST(testSmallAllocation(IsolatedHeapArrayAllocator(24, 8, 1, 32), 32, 20000, true, 40, 41));
ADD_TEST(testFreeListRefillSpans(0, 16, 6, 0, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 16, 6, 1, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 16, 5, 0, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 16, 5, 1, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 16, 1, 0, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 24, 6, 0, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 24, 6, 1, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 24, 5, 0, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 24, 5, 1, pas_small_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(0, 24, 1, 0, pas_small_segregated_page_config_variant));
#if TLC
SKIP_TEST(testInternalScavenge(32, 32, 10000, false, false, 64, 64, 10000, false, 10000, false, 20, 20, 41, 60, 60, 60, false));
SKIP_TEST(testInternalScavenge(32, 32, 10000, true, true, 64, 64, 10000, true, 10000, true, 20, 20, 40, 60, 60, 60, false));
SKIP_TEST(testInternalScavenge(32, 32, 10000, true, true, 64, 64, 5000, true, 10000, true, 20, 20, 20, 40, 20, 40, true));
SKIP_TEST(testInternalScavengeFromCorrectDirectory(32, 64, 128));
SKIP_TEST(testInternalScavengeFromCorrectDirectory(128, 64, 32));
SKIP_TEST(testInternalScavengeFromCorrectDirectory(64, 32, 128));
SKIP_TEST(testInternalScavengeFromCorrectDirectory(128, 32, 64));
#endif
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(24), 24, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(24), 24, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(24), 24, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(24), 24, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(20), 24, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(24), 24, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(0), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(0), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(1), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(1), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(2), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(2), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(3), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(3), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(4), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(4), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(5), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(5), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(6), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(6), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(7), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(7), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(9), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(9), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(10), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(10), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(11), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(11), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(12), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(12), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(13), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(13), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(14), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(14), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(15), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(15), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(0), 8, pas_small_segregated_object_kind, 100, 1),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(1), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(2), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(3), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(4), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(5), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(6), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(7), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(8), 8, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(9), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(10), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(11), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(12), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(13), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(14), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(15), 16, pas_small_segregated_object_kind, 100, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(16), 16, pas_small_segregated_object_kind, 100, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(56), 56, pas_small_segregated_object_kind, 50, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(48), 56, pas_small_segregated_object_kind, 50, 1),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(56), 56, pas_small_segregated_object_kind, 50, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(56), 56, pas_small_segregated_object_kind, 50, 2),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(48, 16), 48, pas_small_segregated_object_kind, 50, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(56), 56, pas_small_segregated_object_kind, 50, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(56), 56, pas_small_segregated_object_kind, 20, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(48), 56, pas_small_segregated_object_kind, 20, 1),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(48, 16), 48, pas_small_segregated_object_kind, 20, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(56), 56, pas_small_segregated_object_kind, 20, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(48), 48, pas_small_segregated_object_kind, 20, 0),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(48, 16), 48, pas_small_segregated_object_kind, 20, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(248), 248, pas_small_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(240), 248, pas_small_segregated_object_kind, 3, 1),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(232), 248, pas_small_segregated_object_kind, 3, 1),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(224), 248, pas_small_segregated_object_kind, 3, 1),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(240, 16), 240, pas_small_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(248), 248, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(240), 240, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(232), 240, pas_small_segregated_object_kind, 3, 1),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(224), 240, pas_small_segregated_object_kind, 3, 1),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(240, 16), 240, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(224, 32), 224, pas_small_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(248), 248, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(240), 240, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(232), 240, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(224), 224, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(240, 16), 240, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(224, 32), 224, pas_small_segregated_object_kind, 3, 0)));
if (hasScope("only-small")) {
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(1000, 16), 1008, pas_small_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(1008, 16), 1008, pas_small_segregated_object_kind, 3, 0)));
} else {
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(1000, 16), 1024, pas_medium_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(1008, 16), 1024, pas_medium_segregated_object_kind, 3, 0)));
}
if (hasScope("only-small")) {
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(PrimitiveAllocator(1024), 1072, pas_small_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(1072), 1072, pas_small_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(AlignedPrimitiveAllocator(1024, 1024), 1024, pas_small_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(PrimitiveAllocator(1024), 1024, pas_small_segregated_object_kind, 3, 0)));
}
pas_heap_ref* heap4Bytes = createIsolatedHeapRef(4, 4);
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(IsolatedHeapAllocator(heap4Bytes), 8, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap4Bytes, 2, 1), 8, pas_small_segregated_object_kind, 200, 0),
SIZE_CLASS_PROGRAM(IsolatedHeapAllocator(heap4Bytes), 8, pas_small_segregated_object_kind, 200, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap4Bytes, 2, 1), 8, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapAllocator(heap4Bytes), 8, pas_small_segregated_object_kind, 200, 0),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap4Bytes, 2, 1), 8, pas_small_segregated_object_kind, 200, 0)));
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap4Bytes, 14, 1), 56, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap4Bytes, 12, 1), 56, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap4Bytes, 12, 16), 48, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap4Bytes, 14, 1), 56, pas_small_segregated_object_kind, 200, 1),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap4Bytes, 12, 1), 48, pas_small_segregated_object_kind, 200, 1)));
pas_heap_ref* heap24Bytes = createIsolatedHeapRef(24, 8);
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(IsolatedHeapAllocator(heap24Bytes), 24, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap24Bytes, 1, 32), 32, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapAllocator(heap24Bytes), 24, pas_small_segregated_object_kind, 200, 0)));
pas_heap_ref* heap3Bytes = createIsolatedHeapRef(3, 1);
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(IsolatedHeapAllocator(heap3Bytes), 8, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap3Bytes, 2, 1), 8, pas_small_segregated_object_kind, 200, 0),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap3Bytes, 3, 1), 16, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap3Bytes, 4, 1), 16, pas_small_segregated_object_kind, 200, 0)));
pas_heap_ref* heap7Bytes = createIsolatedHeapRef(7, 1);
ADD_TEST(testSizeClassCreation(SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap7Bytes, 8, 1), 56, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap7Bytes, 7, 1), 56, pas_small_segregated_object_kind, 10, 0),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap7Bytes, 6, 1), 56, pas_small_segregated_object_kind, 10, 1),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap7Bytes, 6, 16), 48, pas_small_segregated_object_kind, 200, 2),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap7Bytes, 8, 1), 56, pas_small_segregated_object_kind, 10, 0),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap7Bytes, 7, 1), 56, pas_small_segregated_object_kind, 10, 0),
SIZE_CLASS_PROGRAM(IsolatedHeapArrayAllocator(heap7Bytes, 6, 1), 48, pas_small_segregated_object_kind, 10, 0)));
if (TLC)
ADD_TEST(testSpuriousEligibility());
ADD_TEST(testBasicSizeClassNotSet());
ADD_TEST(testSmallDoubleFree());
ADD_TEST(testSmallFreeInner());
ADD_TEST(testSmallFreeNextWithShrink());
ADD_TEST(testSmallFreeNextWithoutShrink());
ADD_TEST(testSmallFreeNextBeforeShrink());
}
#endif // SEGHEAP
void addLargeHeapTests()
{
DisableBitfit disableBitfit;
ADD_TEST(testLargeAllocation(PrimitiveAllocator(10000), 10000, 1, 10000, 200000000));
ADD_TEST(testLargeAllocation(PrimitiveAllocator(10000), 10000, 10, 100000, 200000000));
ADD_TEST(testLargeAllocation(PrimitiveAllocator(10000), 10000, 40, 400000, 200000000));
ADD_TEST(testLargeAllocation(PrimitiveAllocator(10000), 10000, 5000, 50000000, 200000000));
ADD_TEST(testLargeAllocation(AlignedPrimitiveAllocator(10000, 16384), 16384, 1, 16384, 200000000));
ADD_TEST(testLargeAllocation(AlignedPrimitiveAllocator(10000, 16384), 16384, 10, 163840, 200000000));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 40960, 1)));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot")));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("small", 4096, 1),
AllocationProgram::free("small")));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 4096, 1),
AllocationProgram::allocate("two", 4096, 1),
AllocationProgram::allocate("three", 4096, 1),
AllocationProgram::allocate("four", 4096, 1),
AllocationProgram::allocate("five", 4096, 1),
AllocationProgram::allocate("six", 4096, 1),
AllocationProgram::allocate("seven", 4096, 1),
AllocationProgram::allocate("eight", 4096, 1),
AllocationProgram::allocate("nine", 4096, 1),
AllocationProgram::allocate("ten", 4096, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::free("nine"),
AllocationProgram::free("ten")));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 4096, 1),
AllocationProgram::allocate("two", 4096, 1),
AllocationProgram::allocate("three", 4096, 1),
AllocationProgram::allocate("four", 4096, 1),
AllocationProgram::allocate("five", 4096, 1),
AllocationProgram::allocate("six", 4096, 1),
AllocationProgram::allocate("seven", 4096, 1),
AllocationProgram::allocate("eight", 4096, 1),
AllocationProgram::allocate("nine", 4096, 1),
AllocationProgram::allocate("ten", 4096, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::free("nine"),
AllocationProgram::free("ten"),
AllocationProgram::allocate("big", 40960, 1)));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 4096, 1),
AllocationProgram::allocate("two", 36864, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::allocate("big", 40960, 1)));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 4096, 1),
AllocationProgram::allocate("two", 36864, 1),
AllocationProgram::free("two"),
AllocationProgram::free("one"),
AllocationProgram::allocate("big", 40960, 1)));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 8192, 1),
AllocationProgram::allocate("two", 4096, 1),
AllocationProgram::allocate("three", 28672, 1),
AllocationProgram::free("one"),
AllocationProgram::free("three"),
AllocationProgram::free("two"),
AllocationProgram::allocate("big", 40960, 1)));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::exact(65536),
AllocationProgram::allocate("boot", 65536, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("aligned", 32768, 32768),
AllocationProgram::free("aligned"),
AllocationProgram::allocate("big", 65536, 1)));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::upperBound(65536 + 4096),
AllocationProgram::allocate("boot", 65536, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("aligned", 32768, 32768),
AllocationProgram::allocate("one", 4096, 1),
AllocationProgram::allocate("two", 4096, 1),
AllocationProgram::allocate("three", 4096, 1),
AllocationProgram::allocate("four", 4096, 1),
AllocationProgram::allocate("five", 4096, 1),
AllocationProgram::allocate("six", 4096, 1),
AllocationProgram::allocate("seven", 4096, 1),
AllocationProgram::allocate("eight", 4096, 1),
AllocationProgram::free("aligned"),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::allocate("big", 65536, 1)));
ADD_TEST(testComplexLargeAllocation(PrimitiveComplexAllocator(),
ExpectedBytes::upperBound(131072),
AllocationProgram::allocate("aligned", 32768, 32768),
AllocationProgram::allocate("one", 4096, 1),
AllocationProgram::allocate("two", 4096, 1),
AllocationProgram::allocate("three", 4096, 1),
AllocationProgram::allocate("four", 4096, 1),
AllocationProgram::allocate("five", 4096, 1),
AllocationProgram::allocate("six", 4096, 1),
AllocationProgram::allocate("seven", 4096, 1),
AllocationProgram::allocate("eight", 4096, 1),
AllocationProgram::free("aligned"),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::allocate("big", 65536, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 5851, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 5851, 1),
AllocationProgram::free("boot")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 5851, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("small", 585, 1),
AllocationProgram::free("small")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 5851, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 585, 1),
AllocationProgram::allocate("two", 585, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 5851, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 585, 1),
AllocationProgram::allocate("two", 585, 1),
AllocationProgram::allocate("three", 585, 1),
AllocationProgram::allocate("four", 585, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::exact(286720),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 585, 1),
AllocationProgram::allocate("two", 585, 1),
AllocationProgram::allocate("three", 585, 1),
AllocationProgram::allocate("four", 585, 1),
AllocationProgram::allocate("five", 585, 1),
AllocationProgram::allocate("six", 585, 1),
AllocationProgram::allocate("seven", 585, 1),
AllocationProgram::allocate("eight", 585, 1),
AllocationProgram::allocate("nine", 585, 1),
AllocationProgram::allocate("ten", 585, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::free("nine"),
AllocationProgram::free("ten"),
AllocationProgram::allocate("firstHalf", 20480, 1),
AllocationProgram::allocate("secondHalf", 20480, 1),
AllocationProgram::free("firstHalf"),
AllocationProgram::free("secondHalf")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::upperBound(638976),
AllocationProgram::allocate("boot", 40960, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("big", 9362, 65536),
AllocationProgram::allocate("two", 585, 1),
AllocationProgram::allocate("three", 585, 1),
AllocationProgram::allocate("four", 585, 1),
AllocationProgram::allocate("five", 585, 1),
AllocationProgram::allocate("six", 585, 1),
AllocationProgram::allocate("seven", 585, 1),
AllocationProgram::allocate("eight", 585, 1),
AllocationProgram::allocate("nine", 585, 1),
AllocationProgram::allocate("ten", 585, 1),
AllocationProgram::free("big"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::free("nine"),
AllocationProgram::free("ten"),
AllocationProgram::allocate("firstHalf", 20480, 1),
AllocationProgram::allocate("secondHalf", 20480, 1),
AllocationProgram::free("firstHalf"),
AllocationProgram::free("secondHalf")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::upperBound(589824),
AllocationProgram::allocate("boot", 65536, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("big", 9362, 65536),
AllocationProgram::allocate("two", 585, 1),
AllocationProgram::allocate("three", 585, 1),
AllocationProgram::allocate("four", 585, 1),
AllocationProgram::allocate("five", 585, 1),
AllocationProgram::allocate("six", 585, 1),
AllocationProgram::allocate("seven", 585, 1),
AllocationProgram::allocate("eight", 585, 1),
AllocationProgram::allocate("nine", 585, 1),
AllocationProgram::allocate("ten", 585, 1),
AllocationProgram::free("big"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::free("nine"),
AllocationProgram::free("ten"),
AllocationProgram::allocate("firstHalf", 32768, 1),
AllocationProgram::allocate("secondHalf", 32768, 1),
AllocationProgram::free("firstHalf"),
AllocationProgram::free("secondHalf")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(7, 1),
ExpectedBytes::exact(458752),
AllocationProgram::allocate("boot", 65536, 65536),
AllocationProgram::free("boot"),
AllocationProgram::allocate("big", 9362, 65536),
AllocationProgram::allocate("two", 585, 1),
AllocationProgram::allocate("three", 585, 1),
AllocationProgram::allocate("four", 585, 1),
AllocationProgram::allocate("five", 585, 1),
AllocationProgram::allocate("six", 585, 1),
AllocationProgram::allocate("seven", 585, 1),
AllocationProgram::allocate("eight", 585, 1),
AllocationProgram::allocate("nine", 585, 1),
AllocationProgram::allocate("ten", 585, 1),
AllocationProgram::free("big"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::free("nine"),
AllocationProgram::free("ten"),
AllocationProgram::allocate("firstHalf", 32768, 1),
AllocationProgram::allocate("secondHalf", 32768, 1),
AllocationProgram::free("firstHalf"),
AllocationProgram::free("secondHalf")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 10, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 10, 1),
AllocationProgram::free("boot")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 10, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("small", 1, 1),
AllocationProgram::free("small")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 10, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 1, 1),
AllocationProgram::allocate("two", 1, 1),
AllocationProgram::allocate("three", 1, 1),
AllocationProgram::allocate("four", 1, 1),
AllocationProgram::allocate("five", 1, 1),
AllocationProgram::allocate("six", 1, 1),
AllocationProgram::allocate("seven", 1, 1),
AllocationProgram::allocate("eight", 1, 1),
AllocationProgram::allocate("nine", 1, 1),
AllocationProgram::allocate("ten", 1, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::free("nine"),
AllocationProgram::free("ten")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 10, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 1, 1),
AllocationProgram::allocate("two", 1, 1),
AllocationProgram::allocate("three", 1, 1),
AllocationProgram::allocate("four", 1, 1),
AllocationProgram::allocate("five", 1, 1),
AllocationProgram::allocate("six", 1, 1),
AllocationProgram::allocate("seven", 1, 1),
AllocationProgram::allocate("eight", 1, 1),
AllocationProgram::allocate("nine", 1, 1),
AllocationProgram::allocate("ten", 1, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::free("nine"),
AllocationProgram::free("ten"),
AllocationProgram::allocate("big", 10, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 10, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 1, 1),
AllocationProgram::allocate("two", 9, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::allocate("big", 10, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 10, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 1, 1),
AllocationProgram::allocate("two", 9, 1),
AllocationProgram::free("two"),
AllocationProgram::free("one"),
AllocationProgram::allocate("big", 10, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::exact(40960),
AllocationProgram::allocate("boot", 10, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 2, 1),
AllocationProgram::allocate("two", 1, 1),
AllocationProgram::allocate("three", 7, 1),
AllocationProgram::free("one"),
AllocationProgram::free("three"),
AllocationProgram::free("two"),
AllocationProgram::allocate("big", 10, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::upperBound(98304),
AllocationProgram::allocate("boot", 16, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("aligned", 8, 32768),
AllocationProgram::free("aligned"),
AllocationProgram::allocate("big", 16, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::upperBound(98304),
AllocationProgram::allocate("boot", 16, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("aligned", 8, 32768),
AllocationProgram::allocate("one", 1, 1),
AllocationProgram::allocate("two", 1, 1),
AllocationProgram::allocate("three", 1, 1),
AllocationProgram::allocate("four", 1, 1),
AllocationProgram::allocate("five", 1, 1),
AllocationProgram::allocate("six", 1, 1),
AllocationProgram::allocate("seven", 1, 1),
AllocationProgram::allocate("eight", 1, 1),
AllocationProgram::free("aligned"),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::allocate("big", 16, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(4096, 8),
ExpectedBytes::upperBound(131072),
AllocationProgram::allocate("aligned", 8, 32768),
AllocationProgram::allocate("one", 1, 1),
AllocationProgram::allocate("two", 1, 1),
AllocationProgram::allocate("three", 1, 1),
AllocationProgram::allocate("four", 1, 1),
AllocationProgram::allocate("five", 1, 1),
AllocationProgram::allocate("six", 1, 1),
AllocationProgram::allocate("seven", 1, 1),
AllocationProgram::allocate("eight", 1, 1),
AllocationProgram::free("aligned"),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::free("five"),
AllocationProgram::free("six"),
AllocationProgram::free("seven"),
AllocationProgram::free("eight"),
AllocationProgram::allocate("big", 16, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(32768, 32768),
ExpectedBytes::exact(32768),
AllocationProgram::allocate("boot", 1, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedUnitComplexAllocator(32768, 32768),
ExpectedBytes::exact(32768),
AllocationProgram::allocate("boot", 1, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(5000, 8),
ExpectedBytes::exact(5000),
AllocationProgram::allocate("boot", 1, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(5000, 8),
ExpectedBytes::exact(5000),
AllocationProgram::allocate("boot", 1, 1),
AllocationProgram::free("boot")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(5000, 8),
ExpectedBytes::upperBound(15000),
AllocationProgram::allocate("boot", 1, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("reboot", 2, 1),
AllocationProgram::free("reboot")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(5000, 8),
ExpectedBytes::exact(5120000),
AllocationProgram::allocate("boot", 1024, 1),
AllocationProgram::free("boot")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(5000, 8),
ExpectedBytes::exact(5120000),
AllocationProgram::allocate("boot", 1024, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("firstHalf", 512, 1),
AllocationProgram::allocate("secondHalf", 512, 1),
AllocationProgram::free("firstHalf"),
AllocationProgram::free("secondHalf"),
AllocationProgram::allocate("reboot", 1024, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(5000, 8),
ExpectedBytes::exact(5120000),
AllocationProgram::allocate("boot", 1024, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("firstHalf", 512, 1),
AllocationProgram::allocate("secondHalf", 512, 1),
AllocationProgram::free("firstHalf"),
AllocationProgram::free("secondHalf"),
AllocationProgram::allocate("reboot", 1023, 1)));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(5000, 8),
ExpectedBytes::exact(5120000),
AllocationProgram::allocate("boot", 1024, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 1, 1),
AllocationProgram::allocate("two", 1, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two")));
ADD_TEST(testComplexLargeAllocation(IsolatedComplexAllocator(5000, 8),
ExpectedBytes::exact(5120000),
AllocationProgram::allocate("boot", 1024, 1),
AllocationProgram::free("boot"),
AllocationProgram::allocate("one", 1, 1),
AllocationProgram::allocate("two", 1, 1),
AllocationProgram::allocate("three", 1, 1),
AllocationProgram::allocate("four", 1, 1),
AllocationProgram::free("one"),
AllocationProgram::free("two"),
AllocationProgram::free("three"),
AllocationProgram::free("four"),
AllocationProgram::allocate("oneAgain", 1, 1),
AllocationProgram::allocate("twoAgain", 1, 1),
AllocationProgram::allocate("threeAgain", 1, 1),
AllocationProgram::allocate("fourAgain", 1, 1),
AllocationProgram::free("oneAgain"),
AllocationProgram::free("twoAgain"),
AllocationProgram::free("threeAgain"),
AllocationProgram::free("fourAgain")));
ADD_TEST(testLargeDoubleFree());
ADD_TEST(testLargeOffsetFree());
}
#if SEGHEAP
void addMediumHeapTests()
{
DisableBitfit disableBitfit;
ADD_TEST(testFreeListRefillSpans(4096, 2048, 6, 0, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(4096, 2048, 6, 1, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(4096, 2048, 5, 0, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(4096, 2048, 5, 1, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(4096, 2048, 1, 0, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(2048, 4096, 6, 0, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(2048, 4096, 6, 1, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(2048, 4096, 5, 0, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(2048, 4096, 5, 1, pas_medium_segregated_page_config_variant));
ADD_TEST(testFreeListRefillSpans(2048, 4096, 1, 0, pas_medium_segregated_page_config_variant));
#if TLC
ADD_TEST(testInternalScavenge(4096, 4096, 100, false, false, 8192, 8192, 100, false, 100, false, 4, 4, 8, 11, 11, 11, false));
ADD_TEST(testInternalScavenge(4096, 4096, 100, true, true, 8192, 8192, 100, true, 100, true, 4, 4, 7, 11, 11, 11, false));
#endif
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(2048), 2048, pas_medium_segregated_object_kind, 10, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(2072), 2296, pas_small_segregated_object_kind, 10, 3),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(1600), 1608, pas_small_segregated_object_kind, 10, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(1617), 2048, pas_medium_segregated_object_kind, 10, 1),
SIZE_CLASS_PROGRAM(
AlignedPrimitiveAllocator(2048, 2048), 2048, pas_medium_segregated_object_kind, 10, 0),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(2072), 2296, pas_small_segregated_object_kind, 10, 1),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(1617), 2048, pas_medium_segregated_object_kind, 10, 0)));
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(4608), 4608, pas_medium_segregated_object_kind, 5, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(4096), 4608, pas_medium_segregated_object_kind, 5, 1),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(3584), 4608, pas_medium_segregated_object_kind, 5, 1),
SIZE_CLASS_PROGRAM(
AlignedPrimitiveAllocator(4096, 4096), 4096, pas_medium_segregated_object_kind, 5, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(4608), 4608, pas_medium_segregated_object_kind, 5, 0),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(3584), 4096, pas_medium_segregated_object_kind, 5, 1),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(4096), 4096, pas_medium_segregated_object_kind, 5, 0)));
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16384), 16384, pas_medium_segregated_object_kind, 4, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16000), 16384, pas_medium_segregated_object_kind, 3, 1)));
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16384), 16384, pas_medium_segregated_object_kind, 4, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(18712), 21504, pas_medium_segregated_object_kind, 3, 2)));
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16384), 16384, pas_medium_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(
AlignedPrimitiveAllocator(16384, 16384), 16384, pas_medium_segregated_object_kind, 3, 0),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(18712), 21504, pas_medium_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16384), 16384, pas_medium_segregated_object_kind, 3, 1)));
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16896), 18432, pas_medium_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16384), 18432, pas_medium_segregated_object_kind, 3, 1),
SIZE_CLASS_PROGRAM(
AlignedPrimitiveAllocator(16384, 16384), 16384, pas_medium_segregated_object_kind, 4, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16896), 18432, pas_medium_segregated_object_kind, 3, 1),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16384), 16384, pas_medium_segregated_object_kind, 3, 0)));
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16384), 16384, pas_medium_segregated_object_kind, 3, 2),
SIZE_CLASS_PROGRAM(
AlignedPrimitiveAllocator(16384, 16384), 16384, pas_medium_segregated_object_kind, 1, 0),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(16000), 16384, pas_medium_segregated_object_kind, 3, 1)));
}
void addLargerThanMediumHeapTests()
{
DisableBitfit disableBitfit;
ADD_TEST(testLargeAllocation(PrimitiveAllocator(1000000), 1000000, 1, 1000000, 200000000));
ADD_TEST(testLargeAllocation(PrimitiveAllocator(1000000), 1000000, 10, 10000000, 200000000));
ADD_TEST(testLargeAllocation(PrimitiveAllocator(1000000), 1000000, 40, 40000000, 200000000));
ADD_TEST(testLargeAllocation(AlignedPrimitiveAllocator(1000000, 16384), 1015808, 1, 1015808, 200000000));
ADD_TEST(testLargeAllocation(AlignedPrimitiveAllocator(1000000, 16384), 1015808, 10, 10158080, 200000000));
}
void addMargeBitfitTests()
{
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(20000), 21504, pas_medium_segregated_object_kind, 5, 2),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(30000), 32768, pas_marge_bitfit_object_kind, 5, 1),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(29000), 32768, pas_marge_bitfit_object_kind, 5, 1)));
ADD_TEST(testSizeClassCreation(
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(300000), 303104, pas_marge_bitfit_object_kind, 5, 1),
SIZE_CLASS_PROGRAM(
PrimitiveAllocator(290000), 290816, pas_marge_bitfit_object_kind, 5, 1)));
}
void addLargerThanMargeBitfitTests()
{
ADD_TEST(testLargeAllocation(PrimitiveAllocator(10000000), 10000000, 1, 10000000, 11000000));
ADD_TEST(testLargeAllocation(PrimitiveAllocator(10000000), 10000000, 10, 100000000, 101000000));
ADD_TEST(testLargeAllocation(AlignedPrimitiveAllocator(10000000, 16384), 10010624, 1, 10010624, 11000000));
ADD_TEST(testLargeAllocation(AlignedPrimitiveAllocator(10000000, 16384), 10010624, 10, 100106240, 101000000));
}
#endif // SEGHEAP
void addCombinedHeapTests()
{
ADD_TEST(testAllocationCountProgression(PrimitiveComplexAllocator(), 0, 6000, 8, 1, 1, true));
ADD_TEST(testAllocationCountProgression(PrimitiveComplexAllocator(), 0, 6000, 80, 1, 10, true));
ADD_TEST(testAllocationCountProgression(PrimitiveComplexAllocator(), 0, 60000, 80, 1, 1, true));
ADD_TEST(testAllocationCountProgression(PrimitiveComplexAllocator(), 0, 600000, 4000, 1, 1, false));
ADD_TEST(testAllocationCountProgression(PrimitiveComplexAllocator(), 0, 6000000, 40000, 1, 1, false));
ADD_TEST(testAllocationCountProgression(IsolatedComplexAllocator(7, 1), 0, 600, 1, 1, 1, true));
ADD_TEST(testAllocationCountProgression(IsolatedComplexAllocator(7, 1), 0, 6000, 1, 1, 1, true));
ADD_TEST(testAllocationChaos(1, 10, 1000, 1000000, 10000000, true));
ADD_TEST(testAllocationChaos(10, 10, 1000, 100000, 10000000, true));
ADD_TEST(testAllocationChaos(10, 10, 1000, 100000, 10000000, false));
ADD_TEST(testUtilityAllocationChaos(1, 1000, 1000000, 10000000));
ADD_TEST(testUtilityAllocationChaos(10, 1000, 100000, 10000000));
ADD_TEST(testCombinedAllocationChaos(1, 10, 1000, 1000000, 10000000, false));
ADD_TEST(testCombinedAllocationChaos(10, 10, 1000, 100000, 10000000, false));
ADD_TEST(testCombinedAllocationChaos(1, 10, 1000, 1000000, 10000000, true));
ADD_TEST(testCombinedAllocationChaos(10, 10, 1000, 100000, 10000000, true));
ADD_TEST(testReallocatePrimitive(100, 104, 1000, 1000, 1000));
ADD_TEST(testReallocatePrimitive(100, 104, 100000, 102400, 1000));
ADD_TEST(testReallocatePrimitive(1000, 1000, 100, 104, 1000));
ADD_TEST(testReallocatePrimitive(100000, 102400, 100, 104, 1000));
if (hasScope("only-small"))
ADD_TEST(testReallocateArray(7, 1, 100, 728, 1000, 7000, 1000));
else
ADD_TEST(testReallocateArray(7, 1, 100, 728, 1000, 7168, 1000));
ADD_TEST(testReallocateArray(7, 1, 100, 728, 5000, 35000, 1000));
ADD_TEST(testReallocateArray(7, 1, 5000, 35000, 100, 728, 1000));
}
void addAllTestsImpl()
{
addDeallocationTests();
#if SEGHEAP
addSmallHeapTests();
if (hasScope("only-small"))
addLargeHeapTests();
else {
addMediumHeapTests();
addMargeBitfitTests();
addLargerThanMediumHeapTests();
addLargerThanMargeBitfitTests();
}
#else
addLargeHeapTests();
#endif
addCombinedHeapTests();
}
void addAllTests()
{
{
TestScope testScope(
"only-small",
[] () {
pas_medium_segregated_page_config_variant_is_enabled_override = false;
});
addAllTestsImpl();
}
{
TestScope testScope(
"small-and-medium",
[] () {
});
addAllTestsImpl();
addSmallHeapTests();
}
}
} // anonymous namespace
#endif // PAS_ENABLE_THINGY
void addThingyAndUtilityHeapAllocationTests()
{
#if PAS_ENABLE_THINGY
// It's important that we run this test with a totally clean heap. This assert reminds us
// of this. If this assert fires, it's probably because one of the other test suites used
// pas API outside of ADD_TEST().
CHECK(!pas_bootstrap_free_heap.num_mapped_bytes);
ForceExclusives forceExclusives;
ForceTLAs forceTLAs;
EpochIsCounter epochIsCounter;
{
InstallVerifier installVerifier;
addAllTests();
}
addAllTests();
#endif // PAS_ENABLE_THINGY
}