test: cover slabTest

This commit is contained in:
dualfroz committed 2026-09-12 16:46:35 +02:00
1 parent a4a523ed11
commit bd21e377c8
1 file changed
+154
+154
View File
@@ -0,0 +1,154 @@
#include "ktest.h"
#include "slab.h"
#include "pmm.h"
#include "string.h"
#include "heap.h"
KTEST(slabInitRejectsOversizedObject)
{
KmemCache cache;
KT_FALSE(t, kmemCacheInit(&cache, "toobig", frameSize));
KT_EQ(t, kmemCacheCapacity(&cache), 0);
KT_NULL(t, kmemCacheAlloc(&cache));
}
KTEST(slabAllocDistinctNonOverlapping)
{
KmemCache cache;
KT_TRUE(t, kmemCacheInit(&cache, "small32", 32));
KT_GT(t, kmemCacheObjsPerSlab(&cache), 0);
KT_EQ(t, kmemCacheCapacity(&cache), 0); /* nothing grown yet */
void* a = kmemCacheAlloc(&cache);
void* b = kmemCacheAlloc(&cache);
void* c = kmemCacheAlloc(&cache);
KT_NOTNULL(t, a);
KT_NOTNULL(t, b);
KT_NOTNULL(t, c);
KT_NE(t, a, b);
KT_NE(t, b, c);
KT_NE(t, a, c);
KT_EQ(t, kmemCacheUsed(&cache), 3);
/* write a distinct pattern into each object and check none of them
stomped on a neighbor */
memset(a, 0xAA, 32);
memset(b, 0xBB, 32);
memset(c, 0xCC, 32);
uint8 probe[32];
memset(probe, 0xAA, 32);
KT_EQ(t, memcmp(a, probe, 32), 0);
memset(probe, 0xBB, 32);
KT_EQ(t, memcmp(b, probe, 32), 0);
memset(probe, 0xCC, 32);
KT_EQ(t, memcmp(c, probe, 32), 0);
kmemCacheFree(&cache, a);
kmemCacheFree(&cache, b);
kmemCacheFree(&cache, c);
KT_EQ(t, kmemCacheUsed(&cache), 0);
kmemCacheDestroy(&cache);
}
KTEST(slabFreeReusesLifo)
{
KmemCache cache;
kmemCacheInit(&cache, "reuse", 16);
void* a = kmemCacheAlloc(&cache);
kmemCacheFree(&cache, a);
void* b = kmemCacheAlloc(&cache);
KT_EQP(t, a, b); /* single-slot free list, must come back the same */
kmemCacheFree(&cache, b);
kmemCacheDestroy(&cache);
}
KTEST(slabGrowsAcrossMultipleSlabs)
{
KmemCache cache;
kmemCacheInit(&cache, "grow64", 64);
usize perSlab = kmemCacheObjsPerSlab(&cache);
KT_GT(t, perSlab, 0);
uint64 framesBefore = pmmFreeFrames();
/* fill exactly the first slab */
void* first = nullptr;
for (usize i = 0; i < perSlab; i++) {
void* p = kmemCacheAlloc(&cache);
KT_NOTNULL(t, p);
if (i == 0)
first = p;
}
KT_EQ(t, kmemCacheSlabCount(&cache), 1);
KT_EQ(t, kmemCacheUsed(&cache), perSlab);
/* one more forces a second slab (a second frame from pmm) */
void* overflow = kmemCacheAlloc(&cache);
KT_NOTNULL(t, overflow);
KT_EQ(t, kmemCacheSlabCount(&cache), 2);
KT_EQ(t, kmemCacheCapacity(&cache), perSlab * 2);
KT_EQ(t, pmmFreeFrames(), framesBefore - 2);
KT_NE(t, overflow, first);
kmemCacheDestroy(&cache);
/* destroying the cache must return every frame it ever grew into */
KT_EQ(t, pmmFreeFrames(), framesBefore);
}
#define slabStressCount 800
KTEST(slabStressAllocFreePattern)
{
KmemCache cache;
kmemCacheInit(&cache, "stress", 40);
void** slots = (void**)kmalloc(sizeof(void*) * slabStressCount);
KT_NOTNULL(t, slots);
if (!slots) {
kmemCacheDestroy(&cache);
return;
}
for (int i = 0; i < slabStressCount; i++) {
slots[i] = kmemCacheAlloc(&cache);
KT_NOTNULL(t, slots[i]);
if (slots[i])
*(int32*)slots[i] = i;
}
KT_EQ(t, kmemCacheUsed(&cache), slabStressCount);
/* every slot still holds the value we stamped, none aliased */
for (int i = 0; i < slabStressCount; i++) {
if (slots[i])
KT_EQ(t, *(int32*)slots[i], i);
}
/* free every third object, then re-allocate that many and confirm the
cache handed back exactly that many reclaimed slots (no capacity
growth needed to satisfy them) */
usize freedCount = 0;
for (int i = 0; i < slabStressCount; i += 3) {
kmemCacheFree(&cache, slots[i]);
slots[i] = nullptr;
freedCount++;
}
KT_EQ(t, kmemCacheUsed(&cache), slabStressCount - freedCount);
usize capacityBeforeRefill = kmemCacheCapacity(&cache);
for (int i = 0; i < slabStressCount; i += 3) {
slots[i] = kmemCacheAlloc(&cache);
KT_NOTNULL(t, slots[i]);
}
KT_EQ(t, kmemCacheCapacity(&cache), capacityBeforeRefill);
KT_EQ(t, kmemCacheUsed(&cache), slabStressCount);
for (int i = 0; i < slabStressCount; i++)
kmemCacheFree(&cache, slots[i]);
KT_EQ(t, kmemCacheUsed(&cache), 0);
kfree(slots);
kmemCacheDestroy(&cache);
}